1.对于++ , --运算符
y=(4+x++)+(6+x++);
C语言没有明确的指出x在每个子表达式被计算后增加还是在整个表达式计算后增加,不同的系统执行的结果不同.可以用以下原则来避免:
.如果一个变量出现在同一个函数的多个参数中,不要对此变量用++ , --操作.
.如果一个变量多次出现在一个表达式中,不要对此变量用++ , --操作.
2.scanf()函数没有精度控制
.对于%d: 当遇到空格,tab,回车或非法数据时,认为输入结束.
.对于%c: 当遇到空格时,认为是合法字符
3.数组初始化
数组在定义时的初始化:
int a[]={1,3,4};
int b[][3]={{1,2,3},{4,3,2}};
必须指出第二维的元素数,C会自动确定第一维数值.
char s[]="abc";定义时初始化
char s[]; s="abc";分开是错误的,数组声明后要在循环中对每一个元素进行初始化
char *p="abc";对指针初始化
char *p; p="abc";是正确的,可以分开
4.指向动态分配的内存单元的指针p
int num=10;
char *p=(char*)malloc(sizeof(char)*num);
free(p);
此时p所指向的内存单元已经被回收,如果p没有被置NULL,则p指向未知内存地址,此时再对p指向的单元进行操作会产生不可预料的结果
5.sizeof()返回数据类型占据的字节数
sizeof(char) 1
sizeof(int) 4
sizeof(long) 4
sizeof(short) 2
char str[]="abcd";
char *p="defg";
sizeof("abcd") 5 (包含字符串尾的'\0')
sizeof(str) 5
sizeof(p) 4 (指针本身占的字节数,十六进制整型)
sizeof(*p) 1 (字符'd'占的字节数)
6.指针指向的字符串不能通过指针进行修改
char *p="abc";
p[1]="d"; //出错
char s[]="abc";
s[1]="d"; //正确(数组对应的内存区可以修改)
7.结构体初始化中注意的问题
struct stu{
double value;
char name[10];
};
struct stu s1={.value=88.5}; //只初始化value
struct stu s2={.name="chen",
.value=99.9,
"zhang"};
最后name值为"zhang",因为name在结构声明中紧紧跟在value之后,对成员最后一次赋值是它实际所获得的值.
8.位操作的两个工具:位运算符和位字段
位操作符:~ & | ^ >> << &= |= >>= <<= ^=
位字段:是singned int或unsigned int中的一组相邻的位,位字段由一个结构声明建立,为每个字段提供标签并决定字段的宽度.
struct {
unsigned int filed1:1;
unsigned int :2;
unsigned int filed2:1;
unsigned int filed3:1;
}stuff;
stuff.filed1和stuff.filed2之间有一个2位的间隙,filed3存储在下一个int中.不允许位段跨越两个unsigned int 的边界,用未命名的字段填充空洞.
不同的机器位段放在int中的顺序不同,典型的用于不可移植的用途.
9.各种指针
int *a; //指向整数a的指针
int **a; //指向整型指针的指针
int *a[10]; //指针数组,数组中的每个元素指向一个整型变量
int (*a[10])(char*) //指针数组,数组中的每个元素指向一个参数为char*返回值为int的函数
int (*a)[10] //数组指针,指针指向一个有10个元素的整型数组
int (*a)(char*) //函数指针,指针指向一个参数为char*返回为int的函数
10.修饰符const的含义
11.修饰符volatile的含义
用于保证读取数据的可靠性
volatile int a;
变量a,可能会被编译器未知的原因更改,用volatile修饰,优化器在使用此变量时必须每次重新读取内存,而不是读取寄存器中的备份.
如果变量a是寄存器变量或表示一个端口的数据,要用volatile以保证对数据的可靠访问.
12.整数的自动转换
unsigned int a=6;
int b=-20;
(a+b >0) ? printf("ok"):printf("no");
结果是:ok.当表达式中同时存在有符号数和无符号数时,都将自动转换为无符号数进行运算,-20被转换成了一个非常大的正数.