- *a是指“a指向的变量”,而不仅是“a指向的变量所拥有的值”。理解这一点相当重
要。例如,*a = *a + 1就是让a指向的变量自增1。甚至可以把它写成(*a)++。注意不要写
成*a++,因为“++”运算符的优先级高于“取内容”运算符“*”,实际上会被解释成*(a++)。 - gdb中显示程序收到了SIGSEGV信号——段错误。这太让人沮丧了!眼看本章就要结束
了,怎么又遇到一个段错误?别急,让我们慢慢分析。我保证,这是本章最后的难点。
你有没有想过,编译后产生的可执行文件里都保存着些什么内容?答案是和操作系统相
关。例如,UNIX/Linux用的ELF格式,DOS下用的是COFF格式,而Windows用的是PE文件格
式(由COFF扩充而来)。这些格式不尽相同,但都有一个共同的概念——段。
“段”(segmentation)是指二进制文件内的区域,所有某种特定类型信息被保存在里面。
可以用size程序 (5) 得到可执行文件中各个段的大小。如刚才的factorial.c,编译出a.exe以后执
行size的结果是:
D:\>size a.exe
text data bss dec hex filename
2756 740 224 3720 e88 a.exe
此结果表示a.exe由正文段、数据段和bss段组成,总大小是3720,用十六进制表示为
e88。这些段是什么意思呢?
提示4-19:在可执行文件中,正文段(Text Segment)用于储存指令,数据段(Data
Segment)用于储存已初始化的全局变量,BSS段(BSS Segment)用于储存未赋值的全局
变量所需的空间。
是不是少了点什么?调用栈在哪里?它并不储存在可执行文件中,而是在运行时创建。
调用栈所在的段称为堆栈段(Stack Segment)。和其他段一样,堆栈段也有自己的大小,不
能被越界访问,否则就会出现段错误(Segmentation Fault)。
这样,前面的错误就不难理解了:每次递归调用都需要往调用栈里增加一个栈帧,久而
久之就越界了。这种情况叫做栈溢出(Stack Overflow)。
提示4-20:在运行时,程序会动态创建一个堆栈段,里面存放着调用栈,因此保存着
函数的调用关系和局部变量。
那么栈空间究竟有多大呢?这和操作系统相关。在Linux中,栈大小是由系统命令ulimit
指定的,例如,ulimit -a显示当前栈大小,而ulimit -s 32768将把栈大小指定为32MB。但在
Windows中,栈大小是储存在可执行文件中的。使用gcc可以这样指定可执行文件的栈大小:
gcc -Wl,--stack=16777216 (6) ,这样栈大小就变为16MB。
提示4-21:在Linux中,栈大小并没有储存在可执行程序中,只能用ulimit命令修改;
在Windows中,栈大小储存在可执行程序中,用gcc编译时可以通过-Wl,--stack=<byte
count>指定。
聪明的读者,现在你能理解为什么在介绍数组时,建议“把较大的数组放在main函数
外”了吗?别忘了,局部变量也是放在堆栈段的。栈溢出不一定是递归调用太多,也可能是
局部变量太大。只要总大小超过了允许的范围,就会产生栈溢出。 - CPU每秒运算速度月1e8/s IO:scanf/printf 1e6/s 非标准方式可能到1e7
- 素数密度n/ln(n)
- 枚举回文数一般枚举前半
C语言小知识点总结
最新推荐文章于 2025-01-05 19:20:29 发布