前言:本文的目的是记录C语言中那些容易被忽略的细节。我打算每天抽出一点时间看书整理,坚持下去,今天是第一篇,也许下个月的今天是第二篇,明年的今天又是第几篇呢?……我坚信,好记性不如烂笔头。第四篇了,fight~...
第一篇链接:C语言中容易被忽略的细节(第一篇)
第二篇链接:C语言中容易被忽略的细节(第二篇)
第三篇链接:C语言中容易被忽略的细节(第三篇)
1、void*类型的指针不能参与算术运算,只能进行赋值、比较和sizeof操作的原因?
指针的算术运算还要包含指针所指对象的字节数信息。
2、不能把“&”单独用于一个非变量的东西。不能对字面常量使用“&”来取其地址,因为字面常量保存在符号表中,没有地址概念。数组名是一个指针常量,不能修改数组名的值。例如以下语句1与语句2是等价的:
int a[10]; //语句1 int *const a; //语句2 int b[3][4]; //语句1 int (*const b)[4]; //语句2 int c[3][4][5]; //语句1 int (*const c)[4][5]; //语句2
3、return 0与exit(0)的几点区别
(1)exit结束正在运行的整个程序,只要调用exit就结束,它将参数返回给OS,把控制权交给操作系统;return退出当前函数,返回函数值,把控制权交给调用函数;
(2)exit是系统调用级别,它表示一个进程的结束;return是语言级别的,它表示调用堆栈的返回。
(3)main函数结束时也会隐式地调用exit函数,它将删除进程使用的内存空间,同时把错误信息返回给父进程。
(4)main函数返回一个整型值与该值调用exit是等价的。
4、static修饰符是一个能够减少命名冲突的有用工具。例如:static int a;将a的作用域限制在一个源文件内,对于其他源文件,a是不可见的。可以在多个源文件中定义同名的变量a,只要所有的变量a都被定义为static,或者仅仅只有其中一个变量不是static。static修饰符也适用于函数。
5、C语言允许程序员写操作前控制产生的输出数据量,这种控制能力是通过库函数setbuf实现的。例如:setbuf(stdout, buf);语句将通知输入/输出库,所有写入到stdout的输出都使用buf作为输出缓冲区,直到buf缓冲区被填满或程序员直接调用fflush,buf缓冲区的内容才实际写入到stdout中。缓冲区的大小由系统头文件<stdio.h>中的BUFSIZ定义。《C陷阱与缺陷》中有一个例子:
#include <stdio.h> int main(void) { char c; char buf[BUFSIZ]; setbuf(stdout, buf); while ((c = getchar()) != EOF) putchar(c); return 0; }
以上程序是错误的。buf缓冲区最后一次被清空是在main函数结束之后,作为程序交回控制给操作系统之前C运行时库所必须进行清理工作的一部分。在此之前buf字符数组已经被释放了。修改的方法有3种:
(1)定义缓冲数组为静态数组static char buf[BUFSIZ];
(2)将缓冲数组拿到main函数外;
(3)动态分配缓冲区,程序中并不主动释放分配的缓冲区。setbuf(stdout, malloc(BUFSIZ));
备注:
(1)由于缓冲区是动态分配的,所以main函数结束时并不会释放该缓冲区,这样C运行时库进行清理工作时就不会发生缓冲区已释放的情况。
(2)如果malloc调用失败,返回NULL指针,此时标准输出不需要进行缓冲。
(3)对于由写操作打开的文件,调用fflush将导致输出缓冲区的内容被实际地写入该文件。
6、为什么向函数传递多维数组时候不需要说明第一维长度而必须说明其他维长度?
(1)数组元素在内存中按照“行优先”规则存储,需要列数确定一行有多少元素;
(2)编译器在计算元素地址时不需要数组第一维的长度,但需要所有其他维的长度信息;
(3)C/C++不对数组进行越界访问检查,对编译器来说不需要知道第一维的长度。
7、C语言将多维数组转换为数组指针。以下4种表达方式是等价的:
a[i][j]; *(a[i] + j); (*(a + i))[j]; *(*(a + i) + j);
8、在语义上,下标操作符返回的是一个元素的引用。例如语句1和语句2是等价的:
//语句1 a[3] = 100; //语句2 int &ri = a[3]; ri = 100;
9、不能使用一个元素类型的指针来接收动态创建的多维数组的返回地址,因为一个多维数组在语义上并不等价于一个指向其元素类型的指针,它等价于一个“指向数组的指针”。例如以下用法是正确的:
char *q = new char[5]; delete []q; char (*p)[4] = new char[5][4]; delete []p;
10、(1)标号(lable)是具有函数作用域的唯一一种标识符。无论标号定义在函数哪行,函数内嵌套多深,它都能在函数体内任何一个地方访问到。标号一般用在goto语句中,如果goto语句没有使用到该标号,那么该标号将被忽略。(2)当局部变量与某一个全局变量同名时,在函数内部将遮蔽该全局变量。此时在函数内部可通过一元作用域解析运算符来引用全局变量,如::a。