因为需要研究PostgreSQL源码,故需要了解C的工程知识,虽说C的基础很简单,语法集合很少,但是对于大型系统来说,但由于C缺少一些现代语言的包,类等概念,在源码经常看到一些约定俗成的“利用C来实现高级语言语法特性”的知识,在《C语言的495个问题》中,有详细的说明,下面我们就一起看看吧。
# include
int main() {
printf("char: %lu\n", sizeof(char)); // 1
printf("short int: %lu\n", sizeof(short int)); // 2
printf("int: %lu\n", sizeof(int)); // 4
printf("long: %lu\n", sizeof(long)); // 8
printf("long long: %lu\n", sizeof(long long)); // 8
return 0;
}
C语言相对低级,其设计理念认为类型大小应该有具体实现来决定, 虽然这很容易出矛盾
是的,但其实标准头文件
中已经定义了int16_t
和uint_32_t
类型
char *p1, p2
有什么问题char *p1, *p2;
正确方式如下, 注意malloc函数返回的是指针
#include
int main() {
char *p;
p = malloc(10);
return 0;
}
extern int i;
extern int f();
int i = 0;
int f() {return 1;}
见2.4
尽量这样做
extern int f();
和int f();
没有区别没啥用,过时了
typedef
和#define
的区别?#define
主要是配合#ifdef
宏使用的struct node {
char *item;
struct node *next;
};
typedef struct node *NodePtr;
int main() {
NodePtr a;
NodePtr b;
return 1;
}
struct node;
typedef struct node *NodePtr;
struct node {
char *item;
NodePtr *next;
};
int main() {
NodePtr a;
NodePtr b;
return 1;
}
typedef struct node {
char *item;
struct node *next;
} *NodePtr;
int main() {
NodePtr a;
NodePtr b;
return 1;
}
以下两种均可
struct a {
int field;
struct b *bpointer;
};
struct b {
int bfield;
struct a *apointer;
};
int main() {
struct a aa;
struct b bb;
return 1;
}
struct a;
struct b;
typedef struct a *APTR;
typedef struct b *BPTR;
struct a {
int afield;
BPTR bpointer;
};
struct b {
int bfield;
APTR pointer;
};
int main() {
APTR aa;
BPTR bb;
return 1;
}
见2.1
typedef int (*funcptr) ();
是什么意思?funcptr fp1, fp2;
int (*pf1) (), (*pf2) ();
typedef char *charp;
const charp p;
const修饰的是指针p,而不是指针p所指向的变量
const int n = 5;
int a[n];
是不允许,会报错
const char *p, char const *p, char *const p
的区别?见11.10和1.21
略
不能
// file1.c
int array[] = {1,2,3};
// file2.c
extern int array[];
// file1.c
int array[] = {1, 2, 3};
int arraysz = sizeof(array);
// file2.c
# include
# include
extern int array[];
extern int arraysz;
int main() {
// int sz = sizeof(array);
// printf("%d\n", sz);
printf("%d\n", arraysz);
return 1;
}
// file1.h
#define ARRAYSZ 3
// file1.c
# include "file1.h"
int array[ARRAYSZ] = {1, 2, 3};
// file2.c
# include "file1.h"
# include
extern int array[];
int main() {
printf("%d\n", ARRAYSZ);
return 1;
}
或者给数组末尾加哨兵位(如-1, NULL等)
可能是函数没有声明,或者和引入的某头文件中的函数同名了。
不对,见11.7
见11.4
见10.9
double array[256][256]
见19.28和7.20
略 不要用下划线开头,容易出问题
如果char[] a = "";
不行的话,可以用char *a = "";
或者char a[10]; strcpy(a, "");
不明白
char[] a = "";
和char *a = "";
,字符串字面量是内存中只读区域的,有的编译器会控制其是否可写,所以对a的写操作是否报错,取决于编译器的种类
对,是合法的
extern int func();
int (*fp)() = func;
fp是指向函数的指针
是,可以
用struct定义的话,使用时也得带上struct关键字,反之则不用
struct x {};
struct x a;
// 或
typedef struct x{} y;
y a;
// 或可以重名
typedef struct x{} x;
x a;
可以,见1.14和1.15
不明
见11.6
通过struct,然后提供一个函数,可以初始化数组, 详细见数上的4个例子
这种方式传递的是浅拷贝,struct内的指针成员指向的还是原地址,而不是复制出新的一份儿
遇到未使用的hole洞时会出问题,所以一般char*都是用strcmp比较而不是==
可以用匿名struct
plotpoint((struct point){1,2});
或
plotpoint((struct point){.x=1, .y=2});
# define offsetof(type, f) ((size_t) \ ((char *)&((type *)0)->f -(char *)(type *) 0))
offsetb = offsetof(struct a, b);
*(int *) ((char *)structp + offsetb) = value;
注意struct定义的末尾要加分号;
可以初始化union的任意成员
没有
#define
有啥区别?移植性很好
没有,可以自己写一个函数,把枚举常量值映射到字符串,或者用调试器
节省内存空间,用于struct内部
因为位域的移植性差
a[i] = i++
是未定义的printf("%d\n", i++ * i++);
的结果出乎意料尽量不要在一行里写多个i++之类的值,和人想象的结果可能有差异
i = i++
行为未定义最好不要这么写
a ^= b ^= a ^= b
行为未定义尽量不要这么写,在一个表达式中两次修改变量a的值,此行为是未定义的
只有部分作用
即若左边的子表达式即可决定最终结果,则右边的子表达式不会计算
如下是非常常见的正确用法
if (d != 0 && n / d > 0) {
// sth
}
if (p == NULL || *p == '\0') {
// sth
}
如下,可能f2()比f1()先执行
printf("%d %d", f1(), f2());
书中的表述有些绕,就是尽量不要写类似a[i] = i++
之类的复杂表达式,避免歧义类似
i++
: i+1,返回的是原i值++i
:i+1,返回的是+1后的值正确的方式是
if (a < b && b < c) {
}
而不是if(a
3.16 类型溢出或截短
正确的方式是提前转换,如下是正确的
long int c = (long int) a * b;
// 或
long int c = (long int) a * (long int) b;