C补遗
基础
字符数组拼接
C 预处理器 有字符数组的拼接功能:两个加引号的字符数组领接,中间没有标点, 则会被连接成一个字符数组。可用于将很长的字符内容分行定义。
char str[] = "abc"
"def";
常用宏
#define / #undef#ifdef / #ifndef#if / #else / #endif
函数
函数参数省略名称
函数申明参数可以省略名称, 只有在函数内需要取用时才有效,在声明中的参数名会被编译器忽略。
bool my_function(int, char &, void *)
参数传递
函数参数(除数组)以值传送(存放在临时变量中)。对于数组,其起始元素的地址最为参数被传入函数。
空参数表
C中,空参数表的函数可以带任意参数(任意书目,任意类型)。因此,ANSI C中声明空参数表,使用void关键字,而C++中,意味着不带参数的函数。
指针
指针运算中,其单位是指针所指数据类型大小
指针的算术运算
基础:- 不能将两个指针相加
- 指针相减,结果为两个指针之间间隔的元素个数
- 一个指针可以加上或减去一个整数,以元素为单位移动指针
char和int
c的类型不能是char类型,以能够足够大存放EOF
int c;
while((c = getchar()) != EOF)
putchar(c);
赋值结合次序右到左
a = b = c = 0;
a = (b = (c = 0));
extern
如果在函数中使用全局变量,需extern声明。如果全局变量的定义在函数前,则可忽略。全局变量的定义在其他文件,则必须extern声明。一般将extern声明放在文件头或者单独方在.h文件中
位运算符
主要位运算符:
- &: 与
- |: 或
- ^:异或,两个操作数对应位不同为1,否则为0
- <<: 左移,空位0填补
- >>: 右移
- ~:求反
关键字
register
register关键字建议编译器使用CPU寄存器
程序块结构
基础:- 变量的声明和初始化紧跟在任何标识复合语句开始的左括号后(包括常见的函数定义的左括号后)
- 另外函数参数变量也会隐藏同名的外部变量
int i = 0;
foo(float x) {
x... /* 隐藏同名的x外部变量 */
}
if (n > 0) {
int i; /* 新的变量 */
float x = 1.0;
for (i = 0; i < n; i++) {
...
}
foo(x)
}
初始化
基础:- 不进行初始化,外部变量和静态变量被初始化为0,局部变量和自动变量初始值不确定
- 外部变量和静态变量的初始化表达式必须是常量表达式,且只初始化一次
- 数组初始化:int days[] = {1, 2, 3}; 省略数组长度时,编译器自动计算
- 字符数组是个特殊:char str[] = “hello”; 编译器自动在最后加上’\0’
- 多维数组(特殊的一维数组) int matrix[][] = {{1,2}, {3,4}};
另,以多维数组为参数的函数声明,以下三种相同(可能的区别是定义后内存的分配):
foo(int (*days)[13]) {...} /* 参数是指针,指向具有13个整数的一维数组 */foo(int days[2][13]) {...}foo(int days[][13]) {...}
其它含义:
int *days[13]; /* 声明了一个有13个元素的数组,每个元素是指向整型对象的指针 */
宏
undef指令可取消名字的宏定义
宏作为函数展开的缺点:
- 作为参数的表达式重复计算
- 必须使用圆括号保证计算次序
其它的一些宏特性在C++中已属无用,略。
结构
定义:
struct point {
int x;
int y;
};
使用:
struct point pt;
struct point pt2 = {120, 240};
sizeof
返回一个整形值,等于指向对象或类型所占用的存储空间字节数(无符号整型size_t)
对象:变量,数组或结构类型:基本类型,派生类型
比如计算一个Key结构数组的元素个数(数组名为keys)
size_t count = sizeof(keys) / sizeof(struct Key)
例子(count的值为3):
struct Key {
int x;
int y;
};
struct Key keys[] = {{1,2}, {2, 3}, {5, 7}};
unsigned int count = sizeof(keys)/ sizeof(struct Key);
typedef
typedef类似预处理中的#define,但由编译器处理,故功能更强
例子:
typedef unsigned int Length
typedef char *String; // 将String定义为与char*同义
typedef point {
int x,
int y;
} Point, *PointPtr; // Point与struct point同义, PointPtr是struct point的指针
(可以使srtuct的名字和typedef的名字相同,方便使用)
typedef int (*PFI)(char *, char *) // 类型PFT是函数指针
作用:
- 简洁
- 参数化,提高可移植性(比如不同大小的整形值)
- 提供更好的说明性,如PointPtr.。
注:
考虑PointPtr a, b; 和 int* a, b;的区别。前一个a,b都是指针类型,后一个只有a是指针类型,而b是int类型
联合
- 在单块存储区中管理不同类型的数据
- 联合中的任何类型的对象都可赋值给该联合
- 程序员保证读取的类型是最后一次存入的类型
- 使用:联合名.成员 或 联合指针->成员
- 事实上,联合就是结构(所有成员相对于基地址的偏移都是0,结构空间容纳最大的成员,对齐所有类型的成员)
- 只能用第一个成员的类型初始化
标志位测试
例:
enum {
E = 02;
S = 04;
};
- 将flag的E和S置为1
flag |= (E | S)
- 将flag的E和S置为0
flag &= ~(E | S)
- 测试flag的E和S
(flag &= (E | S)) == 0
可用位字段简化:
struct {
unsigned int E : 1;
unsigned int S : 1;
}
冒号后的数字表示字段的宽度(二进制)
volatile
强制某个实现屏蔽可能的优化
malloc/clloc和free的实现
参考 The C Programming Language 第8章的相关论述
setjmp和longjmp
简单的说:setjmp()保存寄存器的值,而longjmp()将其恢复之
更具体的描述参考 CS360 Lecture notes—Setjmp and Longjmp
具体过程如下:
- 我们调用setjmp(),将寄存器数据存在我们传给setjmp()的结构中,然后返回0
- 调用longjmp(),除了传入先前的存放寄存器数据的结构,另外传入一个返回值,比如说8(非0)
- longjmp()的作用是恢复寄存器数据,即回到刚刚调用完setjmp()保存寄存器数据的地方。
- 不同的是这次longjmp()返回值是longjmp()传入的那个值(非零),以示区分
