看一道海康威视的笔试题,题目很简单:
题目是多选题。
我觉得大部分同学看到这个题目的时候,应该觉得它是一个送分题。
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); }
STATIC int LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL), int argc, char **argv, ElfW(auxv_t) *auxvec, __typeof (main) init, void (*fini) (void), void (*rtld_fini) (void), void *stack_end) { //.... }
result = main (argc, argv, __environ MAIN_AUXVEC_PARAM);
要是写代码的话,可以通过循环来输出各个字符串:
#include运行的结果就是这样的。跟我们用env命令看到的基本一样。int main(int argc, char *argv[], char *envp[]) { int i = 0; while (envp[i]) { printf("%s ", envp[i++]); } return 0; }
审核编辑:汤梓红
全部0条评论
快来发表一下你的评论吧 !