看一道海康威視的筆試題,題目很簡單:
題目是多選題。
我覺得大部分同學看到這個題目的時候,應該覺得它是一個送分題。
main函數如果提供參數的話,有兩個參數,一個是argc,一個是argv,其中,argc表示命令行參數的個數,argv是個指針數組,每個指針指向一個參數。
#include我們經常寫這樣的代碼,把所有命令行參數打印出來。int main(int argc, char *argv[]) { for (int i = 0; i < argc; i++) { printf("%s ", argv[i]); } return 0; }
結果就是這樣的:
root@Turbo:test# ./1 hello world ./1 hello world root@Turbo:test#但是當我們提交答案后,它居然是錯的,main函數竟然有三個參數,第三個參數是envp。

這到底是個什么?
于是我找了一份glibc的源碼,找到了libc_start_main函數,我們平時寫的main函數,就是由它來調用的。
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函數的第一個參數,就是待會要調用的main函數,從聲明可以看出,這個函數確實有三個參數,第一個是int類型,后面兩個都是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) { //.... }
而且程序最后調用main函數的時候,也確實是傳了三個參數。
result = main (argc, argv, __environ MAIN_AUXVEC_PARAM);
第三個參數指向的是環境變量,在代碼里面確實有函數對它做初始化。
envp類型和argv一樣,都是指針數組,每個指針指向一個環境變量,并且最后以NULL結尾。
要是寫代碼的話,可以通過循環來輸出各個字符串:
#include運行的結果就是這樣的。跟我們用env命令看到的基本一樣。int main(int argc, char *argv[], char *envp[]) { int i = 0; while (envp[i]) { printf("%s ", envp[i++]); } return 0; }

審核編輯:湯梓紅
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。
舉報投訴
-
參數
+關注
關注
11文章
1865瀏覽量
32841 -
函數
+關注
關注
3文章
4367瀏覽量
64078 -
代碼
+關注
關注
30文章
4886瀏覽量
70209 -
命令行
+關注
關注
0文章
80瀏覽量
10523
原文標題:main函數有三個參數!
文章出處:【微信號:學益得智能硬件,微信公眾號:學益得智能硬件】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
熱點推薦
單片機main函數在中斷函數里執行?
最近看了硬漢分享的一個內容:為什么復位中斷服務程序里面直接調用的main函數,難道所有程序都在復位中斷里面執行的?
發表于 10-24 11:04
?1246次閱讀
請問這兩個函數的后三個參數是怎么算出來的?
); RasterVparamConfig(SOC_LCDC_0_REGS, 480, 10, 21, 22);這兩個函數的 后三個參數怎么算出來的,是依據LCD屏幕文檔嗎,我依據文檔
發表于 07-01 10:42
main函數的末尾沒有return語句會有什么影響
c語言中,如果main函數的末尾沒有return語句將會有什么影響?":
問題的本質
回答這個問題其實只要理解一個東西就行了:
那就是帶有返回值的函數請務必提供返回值
C語言的main函數有幾種寫法?
從學習C語言開始就一直寫個一個函數,那么你知道它的標準寫法什么什么樣嗎? main函數,又稱主函數,是程序執行的起點,我們平時寫的
如何在MAIN函數實現OLED顯示
最近在玩AB32VG1,在做OLED顯示實驗時,在csdn搜索了很多關于AB32VG1驅動OLED的文章,很多都是官方例程操作,在msh窗口中輸入測試指令,測試驅動是否正常。很少有關于在main函數中直接實現的,本編文章直接在MAIN

range里面三個參數的含義
在Python中,range()是一個內置函數,用于生成一個整數序列。range()的三個參數分別代表起始值、終止值和步長。下面將詳盡、詳實
c語言源程序main函數的位置
C語言源程序中的main函數是程序的入口點,它被認為是C語言程序的起點。在執行程序時,操作系統將首先定位到main函數,并從該函數開始執行程
sumif函數三個條件怎么填
函數包含三個條件,我們可以使用SUMIF函數的數組形式來完成。 首先,讓我們了解一下SUMIF函數的基本結構: SUMIF(range, criteria, [sum_range])
if函數三個條件怎么填
IF函數是Excel中最常用的函數之一,它根據一個邏輯條件的返回結果來決定應該執行哪一個動作。在Excel中使用IF函數可以實現復雜的邏輯判
評論