#include <stdio.h>
#define decode(s,t,u,m,p,e,d) m##s##u##t
#define begin decode(a,n,i,m,a,t,e)
int begin()
{
printf("Ha HA see how it is?? ");
}
Cái này có gọi là gián tiếp main
không? làm sao?
#include <stdio.h>
#define decode(s,t,u,m,p,e,d) m##s##u##t
#define begin decode(a,n,i,m,a,t,e)
int begin()
{
printf("Ha HA see how it is?? ");
}
Cái này có gọi là gián tiếp main
không? làm sao?
Câu trả lời:
Ngôn ngữ C định nghĩa môi trường thực thi theo hai loại: tự do và được lưu trữ . Trong cả hai môi trường thực thi, một hàm được gọi bởi môi trường khởi động chương trình.
Trong môi trường tự do, chức năng khởi động chương trình có thể được xác định trong khi trong môi trường được lưu trữ, nó phải như vậy main
. Không chương trình nào trong C có thể chạy mà không có chức năng khởi động chương trình trên các môi trường đã xác định.
Trong trường hợp của bạn, main
bị ẩn bởi các định nghĩa tiền xử lý. begin()
sẽ mở rộng đến decode(a,n,i,m,a,t,e)
mà sẽ được mở rộng hơn nữa main
.
int begin() -> int decode(a,n,i,m,a,t,e)() -> int m##a##i##n() -> int main()
decode(s,t,u,m,p,e,d)
là một macro được tham số hóa với 7 tham số. Danh sách thay thế cho macro này là m##s##u##t
. m, s, u
và t
4 thứ , 1 st , 3 thứ và 2 nd tham số được sử dụng trong danh sách thay thế.
s, t, u, m, p, e, d
1 2 3 4 5 6 7
Phần còn lại không có ích gì ( chỉ để làm rối loạn ). Đối số được chuyển tới decode
là " a , n , i , m , a, t, e" vì vậy, các định danh m, s, u
và t
được thay thế bằng các đối số m, a, i
và n
tương ứng.
m --> m
s --> a
u --> i
t --> n
_start()
. Hoặc cấp thấp hơn nữa, tôi có thể cố gắng căn chỉnh phần bắt đầu chương trình của mình với địa chỉ mà IP được đặt sau khi khởi động. main()
là thư viện C Standard . Bản thân C không áp đặt hạn chế về điều này.
decode(a,n,i,m,a,t,e)
trở thành m##a##i##n
? Nó có thay thế các ký tự không? Bạn có thể cung cấp liên kết đến tài liệu của decode
hàm không? Cảm ơn.
begin
được định nghĩa để được thay thế bởi decode(a,n,i,m,a,t,e)
cái được định nghĩa trước đó. Hàm này nhận các đối số s,t,u,m,p,e,d
và nối chúng ở dạng này m##s##u##t
( ##
có nghĩa là nối). Tức là, nó bỏ qua các giá trị của p, e và d. Khi bạn "gọi" decode
với s = a, t = n, u = i, m = m, nó sẽ thay thế begin
bằng main
.
Hãy thử sử dụng gcc -E source.c
, đầu ra kết thúc bằng:
int main()
{
printf("Ha HA see how it is?? ");
}
Vì vậy, một main()
hàm thực sự được tạo ra bởi bộ tiền xử lý.
Chương trình được đề cập thực hiện cuộc gọi main()
do mở rộng macro, nhưng giả định của bạn là thiếu sót - nó hoàn toàn không phải gọi main()
!
Nói một cách chính xác, bạn có thể có một chương trình C và có thể biên dịch nó mà không cần main
ký hiệu. main
là thứ mà đối tượng c library
mong đợi sẽ nhảy vào, sau khi nó hoàn thành quá trình khởi tạo của chính nó. Thông thường bạn nhảy vào main
từ biểu tượng libc được gọi là _start
. Luôn luôn có thể có một chương trình rất hợp lệ, chỉ đơn giản thực hiện hợp ngữ mà không cần có một chương trình chính. Hãy xem này:
/* This must be compiled with the flag -nostdlib because otherwise the
* linker will complain about multiple definitions of the symbol _start
* (one here and one in glibc) and a missing reference to symbol main
* (that the libc expects to be linked against).
*/
void
_start ()
{
/* calling the write system call, with the arguments in this order:
* 1. the stdout file descriptor
* 2. the buffer we want to print (Here it's just a string literal).
* 3. the amount of bytes we want to write.
*/
asm ("int $0x80"::"a"(4), "b"(1), "c"("Hello world!\n"), "d"(13));
asm ("int $0x80"::"a"(1), "b"(0)); /* calling exit syscall, with the argument to be 0 */
}
Biên dịch ở trên với gcc -nostdlib without_main.c
và xem nó in Hello World!
trên màn hình chỉ bằng cách đưa ra lệnh gọi hệ thống (ngắt) trong lắp ráp nội tuyến.
Để biết thêm thông tin về vấn đề cụ thể này, hãy xem blog ksplice
Một vấn đề thú vị khác là bạn cũng có thể có một chương trình biên dịch mà không cần main
ký hiệu tương ứng với một hàm C. Ví dụ, bạn có thể có phần sau là một chương trình C rất hợp lệ, chương trình này chỉ làm cho trình biên dịch rên rỉ khi bạn tăng cấp Cảnh báo.
/* These values are extracted from the decimal representation of the instructions
* of a hello world program written in asm, that gdb provides.
*/
const int main[] = {
-443987883, 440, 113408, -1922629632,
4149, 899584, 84869120, 15544,
266023168, 1818576901, 1461743468, 1684828783,
-1017312735
};
Các giá trị trong mảng là các byte tương ứng với các hướng dẫn cần thiết để in Hello World trên màn hình. Để có tài khoản chi tiết hơn về cách chương trình cụ thể này hoạt động, hãy xem bài đăng trên blog này , đây là nơi tôi cũng đọc nó đầu tiên.
Tôi muốn thông báo cuối cùng về các chương trình này. Tôi không biết liệu họ có đăng ký là các chương trình C hợp lệ theo đặc tả ngôn ngữ C hay không, nhưng việc biên dịch các chương trình này và chạy chúng chắc chắn là rất khả thi, ngay cả khi chúng vi phạm chính đặc tả.
_start
một phần của tiêu chuẩn đã xác định, hay đó chỉ là việc thực hiện cụ thể? Chắc chắn "chính như một mảng" của bạn là kiến trúc cụ thể. Cũng quan trọng, sẽ không vô lý nếu thủ thuật "chính dưới dạng mảng" của bạn không thành công trong thời gian chạy do các hạn chế về bảo mật (mặc dù điều đó sẽ có nhiều khả năng xảy ra hơn nếu bạn không sử dụng bộ định lượng const
và vẫn có nhiều hệ thống cho phép nó).
_start
không có trong tiêu chuẩn ELF, mặc dù AMD64 psABI có chứa tham chiếu đến Khởi tạo quy trình_start
tại 3.4 . Về mặt chính thức, ELF chỉ biết về địa chỉ e_entry
trong tiêu đề ELF, _start
chỉ là một cái tên mà triển khai đã chọn.
const
sẽ không quan trọng một chút nào - tên biểu tượng trong tệp thực thi nhị phân đó là main
. Không nhiều không ít. const
là một cấu trúc C có nghĩa là không có gì tại thời điểm thực thi.
Ai đó đang cố gắng hành động như Magician. Anh ta nghĩ rằng anh ta có thể lừa chúng tôi. Nhưng chúng ta đều biết, việc thực thi chương trình c bắt đầu bằng main()
.
Các int begin()
sẽ được thay thế bằng decode(a,n,i,m,a,t,e)
bằng một đường chuyền của giai đoạn tiền xử lý. Sau đó, một lần nữa, decode(a,n,i,m,a,t,e)
sẽ được thay thế bằng m ## a ## i ## n. Như bằng liên kết vị trí của lệnh gọi macro, s
sẽ có giá trị là ký tự a
. Tương tự như vậy, u
sẽ được thay thế bằng 'i' và t
sẽ được thay thế bằng 'n'. Và, đó là cách, m##s##u##t
sẽ trở thànhmain
Về, ##
biểu tượng trong mở rộng macro, nó là toán tử tiền xử lý và nó thực hiện dán mã thông báo. Khi macro được mở rộng, hai mã thông báo ở hai bên của mỗi toán tử '##' được kết hợp thành một mã thông báo duy nhất, sau đó thay thế cho '##' và hai mã thông báo ban đầu trong mở rộng macro.
Nếu bạn không tin tôi, bạn có thể biên dịch mã của mình với -E
cờ. Nó sẽ dừng quá trình biên dịch sau khi xử lý trước và bạn có thể thấy kết quả của việc dán mã thông báo.
gcc -E FILENAME.c
decode(a,b,c,d,[...])
xáo trộn bốn đối số đầu tiên và kết hợp chúng để nhận một số nhận dạng mới, theo thứ tự dacb
. (Ba đối số còn lại bị bỏ qua.) Ví dụ: decode(a,n,i,m,[...])
đưa ra số nhận dạng main
. Lưu ý rằng đây là những gì begin
macro được định nghĩa.
Do đó, begin
macro được định nghĩa đơn giản là main
.
Trong ví dụ của bạn, main()
hàm thực sự có mặt, vì begin
là một macro mà trình biên dịch thay thế bằng decode
macro, lần lượt được thay thế bằng biểu thức m ## s ## u ## t. Sử dụng mở rộng macro ##
, bạn sẽ đạt được main
từ decode
. Đây là một dấu vết:
begin --> decode(a,n,i,m,a,t,e) --> m##parameter1##parameter3##parameter2 ---> main
Đó chỉ là một thủ thuật cần có main()
, nhưng việc sử dụng tên main()
cho hàm nhập của chương trình là không cần thiết trong ngôn ngữ lập trình C. Nó phụ thuộc vào hệ điều hành của bạn và trình liên kết là một trong những công cụ của nó.
Trong Windows, bạn không phải lúc nào cũng sử dụng main()
, nhưng đúng hơn là WinMain
hoặcwWinMain
, mặc dù bạn có thể sử dụng main()
, ngay cả với chuỗi công cụ của Microsoft . Trong Linux, người ta có thể sử dụng _start
.
Nó phụ thuộc vào trình liên kết như một công cụ của hệ điều hành để thiết lập điểm nhập, chứ không phải bản thân ngôn ngữ. Bạn thậm chí có thể đặt điểm vào của riêng chúng tôi, và bạn có thể tạo một thư viện cũng có thể thực thi được !
main()
hàm với ngôn ngữ lập trình C, câu này không đúng.