看一道海康威視的筆試題,題目很簡(jiǎn)單:
題目是多選題。
我覺得大部分同學(xué)看到這個(gè)題目的時(shí)候,應(yīng)該覺得它是一個(gè)送分題。
main函數(shù)如果提供參數(shù)的話,有兩個(gè)參數(shù),一個(gè)是argc,一個(gè)是argv,其中,argc表示命令行參數(shù)的個(gè)數(shù),argv是個(gè)指針數(shù)組,每個(gè)指針指向一個(gè)參數(shù)。
#include我們經(jīng)常寫這樣的代碼,把所有命令行參數(shù)打印出來。int main(int argc, char *argv[]) { for (int i = 0; i < argc; i++) { printf("%s ", argv[i]); } return 0; }
結(jié)果就是這樣的:
root@Turbo:test# ./1 hello world ./1 hello world root@Turbo:test#但是當(dāng)我們提交答案后,它居然是錯(cuò)的,main函數(shù)竟然有三個(gè)參數(shù),第三個(gè)參數(shù)是envp。
這到底是個(gè)什么?
于是我找了一份glibc的源碼,找到了libc_start_main函數(shù),我們平時(shí)寫的main函數(shù),就是由它來調(diào)用的。
STATIC int LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL), int argc, char **argv, #ifdef LIBC_START_MAIN_AUXVEC_ARG ElfW(auxv_t) *auxvec, #endif __typeof (main) init, void (*fini) (void), void (*rtld_fini) (void), void *stack_end) { /* Result of the 'main' function. */ int result; __libc_multiple_libcs = &_dl_starting_up && !_dl_starting_up; #ifndef SHARED _dl_relocate_static_pie (); char **ev = &argv[argc + 1]; __environ = ev; /* Store the lowest stack address. This is done in ld.so if this is the code for the DSO. */ __libc_stack_end = stack_end; # ifdef HAVE_AUX_VECTOR /* First process the auxiliary vector since we need to find the program header to locate an eventually present PT_TLS entry. */ # ifndef LIBC_START_MAIN_AUXVEC_ARG ElfW(auxv_t) *auxvec; { char **evp = ev; while (*evp++ != NULL) ; auxvec = (ElfW(auxv_t) *) evp; } # endif _dl_aux_init (auxvec); if (GL(dl_phdr) == NULL) # endif { /* Starting from binutils-2.23, the linker will define the magic symbol __ehdr_start to point to our own ELF header if it is visible in a segment that also includes the phdrs. So we can set up _dl_phdr and _dl_phnum even without any information from auxv. */ extern const ElfW(Ehdr) __ehdr_start __attribute__ ((weak, visibility ("hidden"))); if (&__ehdr_start != NULL) { assert (__ehdr_start.e_phentsize == sizeof *GL(dl_phdr)); GL(dl_phdr) = (const void *) &__ehdr_start + __ehdr_start.e_phoff; GL(dl_phnum) = __ehdr_start.e_phnum; } } /* Initialize very early so that tunables can use it. */ __libc_init_secure (); __tunables_init (__environ); ARCH_INIT_CPU_FEATURES (); /* Perform IREL{,A} relocations. */ ARCH_SETUP_IREL (); /* The stack guard goes into the TCB, so initialize it early. */ ARCH_SETUP_TLS (); /* In some architectures, IREL{,A} relocations happen after TLS setup in order to let IFUNC resolvers benefit from TCB information, e.g. powerpc's hwcap and platform fields available in the TCB. */ ARCH_APPLY_IREL (); /* Set up the stack checker's canary. */ uintptr_t stack_chk_guard = _dl_setup_stack_chk_guard (_dl_random); # ifdef THREAD_SET_STACK_GUARD THREAD_SET_STACK_GUARD (stack_chk_guard); # else __stack_chk_guard = stack_chk_guard; # endif # ifdef DL_SYSDEP_OSCHECK if (!__libc_multiple_libcs) { /* This needs to run to initiliaze _dl_osversion before TLS setup might check it. */ DL_SYSDEP_OSCHECK (__libc_fatal); } # endif /* Initialize libpthread if linked in. */ if (__pthread_initialize_minimal != NULL) __pthread_initialize_minimal (); /* Set up the pointer guard value. */ uintptr_t pointer_chk_guard = _dl_setup_pointer_guard (_dl_random, stack_chk_guard); # ifdef THREAD_SET_POINTER_GUARD THREAD_SET_POINTER_GUARD (pointer_chk_guard); # else __pointer_chk_guard_local = pointer_chk_guard; # endif #endif /* !SHARED */ /* Register the destructor of the dynamic linker if there is any. */ if (__glibc_likely (rtld_fini != NULL)) __cxa_atexit ((void (*) (void *)) rtld_fini, NULL, NULL); #ifndef SHARED /* Call the initializer of the libc. This is only needed here if we are compiling for the static library in which case we haven't run the constructors in `_dl_start_user'. */ __libc_init_first (argc, argv, __environ); /* Register the destructor of the program, if any. */ if (fini) __cxa_atexit ((void (*) (void *)) fini, NULL, NULL); /* Some security at this point. Prevent starting a SUID binary where the standard file descriptors are not opened. We have to do this only for statically linked applications since otherwise the dynamic loader did the work already. */ if (__builtin_expect (__libc_enable_secure, 0)) __libc_check_standard_fds (); #endif /* Call the initializer of the program, if any. */ #ifdef SHARED if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, 0)) GLRO(dl_debug_printf) (" initialize program: %s ", argv[0]); #endif if (init) (*init) (argc, argv, __environ MAIN_AUXVEC_PARAM); #ifdef SHARED /* Auditing checkpoint: we have a new object. */ if (__glibc_unlikely (GLRO(dl_naudit) > 0)) { struct audit_ifaces *afct = GLRO(dl_audit); struct link_map *head = GL(dl_ns)[LM_ID_BASE]._ns_loaded; for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt) { if (afct->preinit != NULL) afct->preinit (&link_map_audit_state (head, cnt)->cookie); afct = afct->next; } } #endif #ifdef SHARED if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS)) GLRO(dl_debug_printf) (" transferring control: %s ", argv[0]); #endif #ifndef SHARED _dl_debug_initialize (0, LM_ID_BASE); #endif #ifdef HAVE_CLEANUP_JMP_BUF /* Memory for the cancellation buffer. */ struct pthread_unwind_buf unwind_buf; int not_first_call; not_first_call = setjmp ((struct __jmp_buf_tag *) unwind_buf.cancel_jmp_buf); if (__glibc_likely (! not_first_call)) { struct pthread *self = THREAD_SELF; /* Store old info. */ unwind_buf.priv.data.prev = THREAD_GETMEM (self, cleanup_jmp_buf); unwind_buf.priv.data.cleanup = THREAD_GETMEM (self, cleanup); /* Store the new cleanup handler info. */ THREAD_SETMEM (self, cleanup_jmp_buf, &unwind_buf); /* Run the program. */ result = main (argc, argv, __environ MAIN_AUXVEC_PARAM); } else { /* Remove the thread-local data. */ # ifdef SHARED PTHFCT_CALL (ptr__nptl_deallocate_tsd, ()); # else extern void __nptl_deallocate_tsd (void) __attribute ((weak)); __nptl_deallocate_tsd (); # endif /* One less thread. Decrement the counter. If it is zero we terminate the entire process. */ result = 0; # ifdef SHARED unsigned int *ptr = __libc_pthread_functions.ptr_nthreads; # ifdef PTR_DEMANGLE PTR_DEMANGLE (ptr); # endif # else extern unsigned int __nptl_nthreads __attribute ((weak)); unsigned int *const ptr = &__nptl_nthreads; # endif if (! atomic_decrement_and_test (ptr)) /* Not much left to do but to exit the thread, not the process. */ __exit_thread (); } #else /* Nothing fancy, just call the function. */ result = main (argc, argv, __environ MAIN_AUXVEC_PARAM); #endif exit (result); }
果然,libc_start_main函數(shù)的第一個(gè)參數(shù),就是待會(huì)要調(diào)用的main函數(shù),從聲明可以看出,這個(gè)函數(shù)確實(shí)有三個(gè)參數(shù),第一個(gè)是int類型,后面兩個(gè)都是char **類型。
STATICint LIBC_START_MAIN(int(*main)(int,char**,char**MAIN_AUXVEC_DECL), intargc,char**argv, ElfW(auxv_t)*auxvec, __typeof (main) init, void (*fini) (void), void (*rtld_fini) (void), void *stack_end) { //.... }
而且程序最后調(diào)用main函數(shù)的時(shí)候,也確實(shí)是傳了三個(gè)參數(shù)。
result = main (argc, argv, __environ MAIN_AUXVEC_PARAM);
第三個(gè)參數(shù)指向的是環(huán)境變量,在代碼里面確實(shí)有函數(shù)對(duì)它做初始化。
envp類型和argv一樣,都是指針數(shù)組,每個(gè)指針指向一個(gè)環(huán)境變量,并且最后以NULL結(jié)尾。
要是寫代碼的話,可以通過循環(huán)來輸出各個(gè)字符串:
#include運(yùn)行的結(jié)果就是這樣的。跟我們用env命令看到的基本一樣。 ? ?int main(int argc, char *argv[], char *envp[]) { int i = 0; while (envp[i]) { printf("%s ", envp[i++]); } return 0; }
審核編輯:湯梓紅
-
參數(shù)
+關(guān)注
關(guān)注
11文章
1829瀏覽量
32195 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4327瀏覽量
62573 -
代碼
+關(guān)注
關(guān)注
30文章
4779瀏覽量
68525 -
命令行
+關(guān)注
關(guān)注
0文章
77瀏覽量
10387
原文標(biāo)題:main函數(shù)有三個(gè)參數(shù)!
文章出處:【微信號(hào):學(xué)益得智能硬件,微信公眾號(hào):學(xué)益得智能硬件】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論