看一道海康威视的笔试题,题目很简单:
题目是多选题。
我觉得大部分同学看到这个题目的时候,应该觉得它是一个送分题。
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文章
1747浏览量
32015 -
函数
+关注
关注
3文章
4276浏览量
62303 -
代码
+关注
关注
30文章
4717浏览量
68197 -
命令行
+关注
关注
0文章
77浏览量
10371
原文标题:main函数有三个参数!
文章出处:【微信号:学益得智能硬件,微信公众号:学益得智能硬件】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
单片机main函数在中断函数里执行?
最近看了硬汉分享的一个内容:为什么复位中断服务程序里面直接调用的main函数,难道所有程序都在复位中断里面执行的?
发表于 10-24 11:04
•912次阅读
请问这两个函数的后三个参数是怎么算出来的?
); RasterVparamConfig(SOC_LCDC_0_REGS, 480, 10, 21, 22);这两个函数的 后三个参数怎么算出来的,是依据LCD屏幕文档吗,我依据文档
发表于 07-01 10:42
main函数的末尾没有return语句会有什么影响
c语言中,如果main函数的末尾没有return语句将会有什么影响?":
问题的本质
回答这个问题其实只要理解一个东西就行了:
那就是带有返回值的函数请务必提供返回值
C语言的main函数有几种写法?
从学习C语言开始就一直写个一个函数,那么你知道它的标准写法什么什么样吗? main函数,又称主函数,是程序执行的起点,我们平时写的
range里面三个参数的含义
在Python中,range()是一个内置函数,用于生成一个整数序列。range()的三个参数分别代表起始值、终止值和步长。下面将详尽、详实
c语言源程序main函数的位置
C语言源程序中的main函数是程序的入口点,它被认为是C语言程序的起点。在执行程序时,操作系统将首先定位到main函数,并从该函数开始执行程
sumif函数三个条件怎么填
函数包含三个条件,我们可以使用SUMIF函数的数组形式来完成。 首先,让我们了解一下SUMIF函数的基本结构: SUMIF(range, criteria, [sum_range])
if函数三个条件怎么填
IF函数是Excel中最常用的函数之一,它根据一个逻辑条件的返回结果来决定应该执行哪一个动作。在Excel中使用IF函数可以实现复杂的逻辑判
评论