#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?? ");
}
นี่เรียกทางอ้อมmain
หรือเปล่า? ยังไง
#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?? ");
}
นี่เรียกทางอ้อมmain
หรือเปล่า? ยังไง
คำตอบ:
ภาษา C กำหนดสภาพแวดล้อมในการดำเนินการในสองประเภท: อิสระและเป็นเจ้าภาพ ในสภาพแวดล้อมการดำเนินการทั้งสองฟังก์ชันถูกเรียกใช้โดยสภาพแวดล้อมสำหรับการเริ่มต้นโปรแกรม
ในความอิสระฟังก์ชั่นสภาพแวดล้อมการเริ่มต้นโปรแกรมสามารถดำเนินการตามที่กำหนดไว้ในขณะที่เจ้าภาพmain
สภาพแวดล้อมที่มันควรจะเป็น ไม่มีโปรแกรมใดใน C สามารถทำงานได้หากไม่มีฟังก์ชันเริ่มต้นโปรแกรมบนสภาพแวดล้อมที่กำหนด
ในกรณีของคุณmain
ถูกซ่อนไว้โดยคำจำกัดความของตัวประมวลผลล่วงหน้า begin()
จะขยายไป ซึ่งต่อไปจะขยายไปยัง decode(a,n,i,m,a,t,e)
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)
เป็นมาโครที่กำหนดพารามิเตอร์ที่มีพารามิเตอร์ 7 ตัว m##s##u##t
รายการทดแทนสำหรับแมโครนี้คือ m, s, u
และt
เป็นพารามิเตอร์4 th , 1 st , 3 rdและ 2 nd ที่ใช้ในรายการแทนที่
s, t, u, m, p, e, d
1 2 3 4 5 6 7
ส่วนที่เหลือไม่มีประโยชน์ ( เพียงเพื่อทำให้สับสน ) อาร์กิวเมนต์ที่ส่งไปยังdecode
คือ " a , n , i , m , a, t, e" ดังนั้นตัวระบุm, s, u
และt
จะถูกแทนที่ด้วยอาร์กิวเมนต์m, a, i
และn
ตามลำดับ
m --> m
s --> a
u --> i
t --> n
_start()
ใช้ได้ หรือระดับต่ำกว่านั้นฉันสามารถลองปรับตำแหน่งเริ่มต้นของโปรแกรมของฉันให้ตรงกับที่อยู่ที่ตั้งค่า IP ไว้หลังจากบูต main()
คือ C มาตรฐานห้องสมุด C เองไม่ได้กำหนดข้อ จำกัด ในเรื่องนี้
decode(a,n,i,m,a,t,e)
กลายเป็นm##a##i##n
อย่างไร? มันแทนที่อักขระ? คุณสามารถให้ลิงค์ไปยังเอกสารของdecode
ฟังก์ชันได้หรือไม่? ขอบคุณ.
begin
ถูกกำหนดให้แทนที่โดยdecode(a,n,i,m,a,t,e)
ที่กำหนดไว้ก่อนหน้า ฟังก์ชันนี้รับอาร์กิวเมนต์s,t,u,m,p,e,d
และเชื่อมต่อเข้าด้วยกันในรูปแบบนี้m##s##u##t
( ##
หมายถึงการเรียงต่อกัน) กล่าวคือไม่สนใจค่าของ p, e และ d ในขณะที่คุณ "เรียกว่า" decode
กับ s = A, t = n, U = i, m = ม. ได้อย่างมีประสิทธิภาพแทนที่ด้วยbegin
main
ลองใช้gcc -E source.c
ผลลัพธ์ลงท้ายด้วย:
int main()
{
printf("Ha HA see how it is?? ");
}
ดังนั้นmain()
ฟังก์ชันจึงถูกสร้างขึ้นโดยตัวประมวลผลล่วงหน้า
โปรแกรมในคำถามไม่โทรmain()
เนื่องจากการขยายตัวแมโคร แต่สมมติฐานของคุณเป็นข้อบกพร่อง - มันไม่ได้มีการเรียกร้องmain()
ที่ทุกคน!
พูดอย่างเคร่งครัดคุณสามารถมีโปรแกรม C และสามารถรวบรวมได้โดยไม่ต้องมีmain
สัญลักษณ์ main
เป็นสิ่งที่c library
คาดว่าจะกระโดดเข้ามาหลังจากเสร็จสิ้นการเริ่มต้นของตัวเอง โดยปกติคุณจะกระโดดเข้ามาmain
จากสัญลักษณ์ libc ที่เรียกว่า_start
. เป็นไปได้เสมอที่จะมีโปรแกรมที่ถูกต้องซึ่งดำเนินการแอสเซมบลีโดยไม่ต้องมี main ดูที่นี้:
/* 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 */
}
รวบรวมข้อมูลข้างต้นด้วยgcc -nostdlib without_main.c
และดูว่ากำลังพิมพ์Hello World!
บนหน้าจอเพียงแค่เรียกระบบ (ขัดจังหวะ) ในชุดประกอบแบบอินไลน์
สำหรับข้อมูลเพิ่มเติมเกี่ยวกับปัญหานี้โปรดดูที่ไฟล์ บล็อก ksplice
อีกประเด็นที่น่าสนใจคือคุณสามารถมีโปรแกรมที่รวบรวมโดยไม่ต้องมีmain
สัญลักษณ์ที่ตรงกับฟังก์ชัน C ตัวอย่างเช่นคุณสามารถมีสิ่งต่อไปนี้เป็นโปรแกรม C ที่ถูกต้องซึ่งทำให้คอมไพเลอร์สะอื้นเมื่อคุณเพิ่มระดับคำเตือน
/* 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
};
ค่าในอาร์เรย์เป็นไบต์ที่สอดคล้องกับคำสั่งที่จำเป็นในการพิมพ์ Hello World บนหน้าจอ หากต้องการทราบรายละเอียดเพิ่มเติมเกี่ยวกับวิธีการทำงานของโปรแกรมนี้โปรดดูที่บล็อกโพสต์ซึ่งเป็นที่ที่ฉันอ่านก่อน
ฉันต้องการแจ้งให้ทราบเป็นครั้งสุดท้ายเกี่ยวกับโปรแกรมเหล่านี้ ฉันไม่รู้ว่าพวกเขาลงทะเบียนเป็นโปรแกรม C ที่ถูกต้องตามข้อกำหนดของภาษา C หรือไม่ แต่การรวบรวมสิ่งเหล่านี้และเรียกใช้งานนั้นเป็นไปได้อย่างแน่นอนแม้ว่าจะละเมิดข้อกำหนดก็ตาม
_start
ส่วนหนึ่งของมาตรฐานที่กำหนดไว้หรือเป็นเพียงการนำไปใช้งานโดยเฉพาะ? แน่นอนว่า "main as an array" ของคุณเป็นสถาปัตยกรรมเฉพาะ สิ่งสำคัญอีกอย่างคือเคล็ดลับ "main as an array" ของคุณจะล้มเหลวในขณะทำงานเนื่องจากข้อ จำกัด ด้านความปลอดภัย (แม้ว่าจะมีโอกาสมากกว่าหากคุณไม่ได้ใช้const
Qualifier และยังมีระบบอีกหลายระบบที่อนุญาต)
_start
ไม่ได้อยู่ในมาตรฐานเอลฟ์ แต่ AMD64 psABI ประกอบด้วยการอ้างอิงไป_start
ที่3.4 กระบวนการเริ่มต้น อย่างเป็นทางการ ELF รู้เฉพาะเกี่ยวกับที่อยู่e_entry
ในส่วนหัวของ ELF _start
เป็นเพียงชื่อที่เลือกใช้งาน
const
จะไม่สำคัญหนึ่งบิต - main
ชื่อสัญลักษณ์ในแฟ้มที่ปฏิบัติการที่ไบนารี ไม่มากไม่น้อย. const
เป็นโครงสร้าง C ที่ไม่มีความหมายในเวลาดำเนินการ
มีคนพยายามทำตัวเหมือนนักเวทย์ เขาคิดว่าเขาหลอกเราได้ แต่เราทุกคนรู้ว่าการเรียกใช้โปรแกรม c เริ่มต้นด้วยmain()
.
int begin()
จะถูกแทนที่ด้วยdecode(a,n,i,m,a,t,e)
โดยหนึ่งผ่านเวที preprocessor จากนั้นอีกครั้งdecode(a,n,i,m,a,t,e)
จะถูกแทนที่ด้วย m ## a ## i ## n ในฐานะที่เป็นโดยสมาคมตำแหน่งของสายแมโคร ประสงค์มีค่าของตัวละครs
a
ในทำนองเดียวกันu
จะถูกแทนที่ด้วย 'i' และt
จะถูกแทนที่ด้วย 'n' และนั่นคือวิธีที่m##s##u##t
จะกลายเป็นmain
เกี่ยวกับ##
สัญลักษณ์ในการขยายมาโครเป็นตัวดำเนินการก่อนการประมวลผลและทำการวางโทเค็น เมื่อขยายแมโครโทเค็นทั้งสองที่อยู่ด้านใดด้านหนึ่งของตัวดำเนินการ '##' แต่ละตัวจะรวมกันเป็นโทเค็นเดียวซึ่งจะแทนที่โทเค็น '##' และโทเค็นดั้งเดิมสองตัวในการขยายมาโคร
หากคุณไม่เชื่อฉันคุณสามารถรวบรวมรหัสของคุณด้วย-E
แฟล็ก จะหยุดกระบวนการรวบรวมหลังจากประมวลผลล่วงหน้าและคุณสามารถเห็นผลลัพธ์ของการวางโทเค็น
gcc -E FILENAME.c
decode(a,b,c,d,[...])
shuffles dacb
สี่ข้อโต้แย้งแรกและร่วมกับพวกเขาที่จะได้รับการระบุตัวตนใหม่ในการสั่งซื้อ (ที่เหลืออีกสามข้อโต้แย้งจะถูกละเว้น.) ยกตัวอย่างเช่นให้ระบุdecode(a,n,i,m,[...])
main
โปรดทราบว่านี่คือสิ่งที่begin
มาโครกำหนดให้เป็น
ดังนั้นbegin
มาโครจึงถูกกำหนดให้เป็นmain
ไฟล์.
ในตัวอย่างของคุณมีmain()
ฟังก์ชันอยู่เนื่องจากbegin
เป็นมาโครที่คอมไพลเลอร์แทนที่ด้วยdecode
มาโครซึ่งจะแทนที่ด้วยนิพจน์ m ## s ## u ## t การใช้การขยายตัวแมโคร##
คุณจะเข้าถึงคำจากmain
decode
นี่คือร่องรอย:
begin --> decode(a,n,i,m,a,t,e) --> m##parameter1##parameter3##parameter2 ---> main
เป็นเพียงเคล็ดลับmain()
เท่านั้น แต่การใช้ชื่อmain()
สำหรับฟังก์ชั่นการเข้าโปรแกรมนั้นไม่จำเป็นในการเขียนโปรแกรมภาษาซี ขึ้นอยู่กับระบบปฏิบัติการของคุณและตัวเชื่อมโยงเป็นหนึ่งในเครื่องมือของมัน
ใน Windows คุณไม่เคยใช้main()
แต่ค่อนข้างWinMain
หรือwWinMain
แม้ว่าคุณสามารถใช้main()
แม้จะมี toolchain ใน Linux คุณสามารถใช้_start
ไฟล์.
ขึ้นอยู่กับตัวเชื่อมโยงเป็นเครื่องมือระบบปฏิบัติการเพื่อกำหนดจุดเริ่มต้นไม่ใช่ภาษาตัวเอง คุณสามารถกำหนดจุดเข้าใช้งานของเราเองและคุณสามารถสร้างไลบรารีที่เรียกใช้งานได้ด้วย !
main()
ฟังก์ชันกับภาษาโปรแกรม C ซึ่งไม่ถูกต้อง