几道C/C++考题
题目来自水木BBS
1. 以下两个定义及初始化有什么区别和联系:
char pch[] = "Hello world";
char *str = "Hello world";
2. static关键字用在哪几种场合,各起什么作用
3. 在典型的系统,比如win32,在调用C的main之前发生了什么事情,另外有时候main函数是有int的返回值,它的返回值做什么用途?
4. C语言程序编译完之后,运行起来,其存储映像包括哪几部分,各起什么作用?
5. C语言传参数的机制是什么,也就是通过什么机制传递参数的?
解答:
1. 第一个是字符数组,字符内容可以被修改;第二个指向"Hello world"常量的指针,字符内容不能被修改。
详细的解释:http://blog.chinaunix.net/u3/110791/showart_2248052.html
2. 略
3. main函数之前--真正的函数执行入口或开始
一种解释
实际上,在可执行文件被加载之后,控制权立即交给由编译器插入的Start函数,它将对后面这些全局变量进行准备:
_osver 操作系统的构件编号
_winmajor 操作系统的主版本号
_winminor 操作系统的次版本号
_winver 操作系统完全版本号
__argc 命令行参数个数
__argv 指向参数字符串的指针数组
_environ 指向环境变量字符串的指针数组
Start函数初始化堆并调用main函数.mian函数返回之后,Start函数调用Exit函数结束该进程.
启动函数Start的源代码在:
crt0.c Microsoft Visual C++
c0w.asm Borladn C++
另一种解释
Some of the stuff that has to happen before main():
set up initial stack pointer
initialize static and global data
zero out uninitialized data
run global constructors
Some of this comes with the runtime library's crt0.o file or its __start() function. Some of it you need to do yourself.
Crt0 is a synonym for the C runtime library.
Depending on the system you're using the follwing may be incomplete, but it should give you an idea. Using newlib-1.9.0/libgloss/m68k/crt0.S as an outline, the steps are:
1. Set stack pointer to value of __STACK if set
2. Set the initial value of the frame pointer
3. Clear .bss (where all the values that start at zero go)
4. Call indirect of hardware_init_hook if set to initialize hardware
5. Call indirect of software_init_hook if set to initialize software
6. Add __do_global_dtors and __FINI_SECTION__ to the atexit function so destructors and other cleanup functions are called when the program exits by either returning from main, or calling exit
7. setup the paramters for argc, argv, argp and call main
8. call exit if main returns
第三种解释:囫囵C语言(三):谁调用了我的 main?
现在最重要的是要跟得上潮流,所以套用比较时髦的话,谁动了我的奶酪。谁调用了我的 main?不过作为计算机工作者,我劝大家还是不要赶时髦,今天Java热,明天 .net 流行,什么时髦就学什么。我的意思是先花几年把基本功学好,等你赶时髦的时候也好事半功倍。废话不多说了。
我们都听说过一句话:“main是C语言的入口”。我至今不明白为什么这么说。就好像如果有人说:“挣钱是泡妞”,肯定无数砖头拍过来。这句话应该是“挣钱是泡妞的一个条件,只不过这个条件特别重要”。那么上面那句话应该是 “main是C语言中一个符号,只不过这个符号比较特别。”
1. 编译器缺省是找 __start 符号,而不是 main
2. __start 这个符号是程序的起始点
3. main 是被标准库调用的一个符号
再来思考一个问题:
我们写程序,比如一个模块,通常要有 initialize 和 de-initialize,但是我们写 C 程序的时候为什么有些模块没有这两个过程么呢?比如我们程序从 main 开始就可以 malloc,free,但是我们在 main 里面却没有初始化堆。再比如在 main 里面可以直接 printf,可是我们并没有打开标准输出文件啊。
聪明的人民群众会想,一定是在 main 之前干了些什么。使这些函数可以直接调用而不用初始化。通常,我们会在编译器的环境中找到一个名字类似于 crt0.o 的文件,这个文件中包含了我们刚才所说的 __start 符号。(crt 大概是 C Runtime 的缩写)
那么真正的 crt0.s 是什么样子呢?下面我们给出部分伪代码:
///////////////////////////////////////////////////////
section .text:
__start:
:
init stack;
init heap;
open stdin;
open stdout;
open stderr;
:
push argv;
push argc;
call _main; (调用 main)
:
destory heap;
close stdin;
close stdout;
close stderr;
:
call __exit;
////////////////////////////////////////////////////
实际上可能还有很多初始化工作,因为都是和操作系统相关的,笔者就不一一列出了。
注意:
1. 不同的编译器,不一定缺省得符号都是 __start。
2. 汇编里面的 _main 就是 C 语言里面的 main,是因为汇编器和C编译器对符号的命名有差异(通常是差一个下划线'_')。
总结:
main函数执行之前,主要就是初始化系统相关资源:
1.设置栈指针
2.初始化static静态和global全局变量,即data段的内容
3.将未初始化部分的赋初值:数值型short,int,long等为0,bool为FALSE,指针为NULL,等等,即.bss段的内容
4.运行全局构造器,估计是C++中构造函数之类的吧
5.将main函数的参数,argc,argv等传递给main函数,然后才真正运行main函数
main函数执行之后,释放资源,结束进程。
备注:全局的未初始化变量存在于.bss段中,具体体现为一个占位符;全局的已初始化变量存于.data段中;而函数内的自动变量都在栈上分配空间。.bss是不占用.exe文件空间的,其内容由操作系统初始化(清零);而.data却需要占用,其内容由程序初始化。
4. 代码段、只读数据段、读写数据段、未初始化数据段属于静态区域,而堆和栈属于动态区域。代码段、只读数据段和读写数据段将在连接之后产生,未初始化数据段将在程序初始化的时候开辟,而堆和栈将在程序的运行中分配和释放。在静态的映像文件中,各个部分称之为节(Section),而在运行时的各个部分称之为段(Segment)。如果不详细区分,可以统称为段。
详细的解释:http://book.51cto.com/art/200902/111807.htm
5. 通过堆栈操作实现参数的传递。
1. 进程的堆栈存储区是主调函数和被调函数进行通信的主要区域。
2. C语言中参数是从右向左进栈的。
3. 由主调函数在调用后清理堆栈。(备注2)
4. 函数的返回值一般是放在寄存器中的。
参看文章:
http://witmax.cn/c-function-heap-stack.html
http://read.newbooks.com.cn/info/178095.html
http://nforcex.blogbus.com/logs/7537173.html
备注:
1. C语言中传递参数为值传递(包括指针传递);C++添加了引用传递。要注意指针传递和引用传递的区别。
2. C语言使用的是cdcel调用,但是windows驱动程序中使用的是stdcall调用。cdcel是主调函数在调用后恢复堆栈,stdcall是被调用函数返回时自行恢复堆栈。
3. Linux汇编使用的是AT&T语法与Intel或MASM汇编语法不同。比如:指令操作的源地址和目的地址的位置刚好是反的。