1. 赋值约束
要使赋值形式合法,必须满足下列条件之一: 两个操作数都是指向限定符或无限定符的相容类型指针, 左边指针所指向的类型必须具有右边指针所指向类型的全部限定符
char *cp;
const char *ccp;
ccp = cp;
这个赋值是合法的, 而 cp = ccp; 产生生警告
const float* 类型并不是一个有限定符的类型,它的类型是 指向一个具有const 限定符的float类型的指针, 也就是说const修饰符石修饰指针所指向的类型,而不是指针本身
const char** 也是一个没有限定为的指针类型,他的类型是指向有const限定符的char类型的指针的指针
所以, char** , 和 const char** 都是没有限定符的指针类型,但是他们所指向的类型不一样 前者是 char*, 后者是 const char* ,因此他们不相容
2.const陷阱
当const出现在*号左边时指针指向的数据为常量
当const出现在*后右边时指针本身为常量
3.算术运算
多个运算符都会引发转换,以类似的方式产生结果类型。称为寻常算术转换
类型为char 或 short 的操作数被转换为 int, float 转换为 double , 如果其中一个操作数是 double,另一个就转换成double. 其中一个是long,另一个就转换long
当执行算术运算时,操作数的类型如果不同,就会发生转换。数据类型一般朝着浮点精度更高,长度更长的方向转换,整数如果转换为signed不会丢失信息,那就转换成他,
否则转换为unsigned
所以当 int a = -1 ; a 与一个无符号类型 unsigned比较时,会升级成无符号类型,所以a的值会是一个很大的整数
这在判断指令中,往往会照成Bug ,当你试图比较 某个函数的返回至 与 -1 时,请注意这个问题,sizeof就返回无符号整数
建议:尽量不要在代码中使用无符号类型,尽量使用像int这样的有符号类型.
优先级与结合性
在表达式中,优先级处于同一级别时,结合型就发挥作用,来解决求值顺序
所有的赋值符都具有右结合性,就是表达式最右边最先执行,然后依次往左边执行,
& , | 这些位操作符具有左结合性,从左往右依次执行
所有优先级相同的操作符,结合性也是相同的
4. gcc 编译器设计者抵制的 #pragma
#pragma 指示符的行为由编译器定义,在GUN c编译器中,他会尝试打开几个游戏
vs 下它可以链接到库
#pragma comment(lib,"DllDemo.lib") or #pragma comment(dll,"DllDemo.dll")
5.小知识
NUL 用于结束一个ACSII字符串
NULL 用于表示什么也不指向 ,空指针
break 跳出最近的一层循环
static 申明静态变量,会在第一次执行时初始化,而后一直保存变量直到程序结束
static 放在函数前,会使此函数在这个文件之外不可见
extern 用于变量表示它在其他地方定义,用于函数表示全局可见此函数,属于多余的,不加也是全局可见
void 用作函数的返回类型表示不返回任何值, 在指针中表示通用指针,在参数列表中表示没有参数
* 乘法运算符, 用于指针,间接引用, 在声明中,表示指针
&取地址操作符, 位与操作符
<<= 左移复合赋值运算符
< 小于运算符, #include指令的左定界符
*指针数组 int * (ap[]) 数组每个成员都是 int* 类型
6.小心Bug
strlen(str)+1 永远记得这个函数只取不包含/0的长度
switch 语句中defaoult可以出现在任何位置,注意每条分支的break
字符串自动合并约定, printf("abcde" "fghijk" "opqrst" ) 相恋的字符串在编译时自动合并,除了最后一个字符串,其余字符串末尾的\0 自动删除
由此引发问题:
char *p[] = {
"abhd" ,
"retd",
"dsfs" //忘记了逗号,所以这2条字符串合并了,
"sds", //末尾使用逗号也没关系
} 由此造成字符串的数组不是我们想要的
p[2] 时候的值不是我们想要的, p[3]还会超出数组下标!!!
if(x>>4) 是什么意思
p = N * sizeof *q // 你能不能马上判断出这是一个乘号还是两个
apple = sizeof( int ; *p ; //这又代表什么
优先级问题
.的优先级高于* , -> 用于解决这个问题
[] 高于 *
函数() 高于 *
== 和 != 高于位操作符
== 和 != 高于赋值赋
算术运算高于位移运算符
逗号在所有运算符中优先级最低
*p.f 容易以为是 (*p).f 实际上是 *(p.f) //. 优先级高于*
int *ap[] 容易以为 int (*ap)[] ap是指向int数组的指针, 实际上 int*(ap[]) ap是元素为int指针的数组, [] 高于 *
int *fp() 容易以为 int (*fp)() 指针函数。 实际上 int* (fp()) 返回值类型为int*的函数 函数() 高于*
(val & mask != 0) 容易以为 (val & mask) != 0 , 实际上 val & (mask != 0) == 和 !=高于位操作符
c = getchar() != EOF 容易以为 (c = getchar()) != EOF ,实际上 c = (getchar != EOF) == != 高于赋值福
msb << 4 + lsb 容易以为 (msb<<4) + lsb , 实际 msb << (4+lsb) 运算高于位移
i = 1,2 容易以为 i = (1,2) 实际上 (i = 1),2 逗号优先级最低
计次顺序问题:
x = f() + g() * h() ;
g() 和 h() 会先执行乘法运算,但是他们的调用可能以任何顺序出现.所以避免这类代码
7.指针与数组
char *j[20] //一个指针数组,数组里所有元素都是char*指针
char (*j)[20] //一个数组指针,数组内元素都是char
const int * a ; //指针所指对象是const
int const * a //指针所指对象是const
int * const a; //指针是const
const在*号右边,指针就是const
数组并非指针!
文件1: int mango[100];
文件2: extern int *mango;
这是错误的,究其原因,要知道数组和指针的访问方式
数组只需要知道数组的起始地址+ 偏移即可 比如 mango[2]
而指针需要先得到知道内保存的地址,再到地址中去相加偏移 mango[2] 指针的
而文件2中只是声明了mango,定义在文件1,所以mango是一个数组
所以文件2的 mango[2] 访问时应该直接取得基址+偏移 ,而编译器却认为这是一个指针,他先读取指针所指向的地址然后加偏移
关键字:
struct 结构名 {} 申明一个结构
union 联合名 {} 申明一个联合,联合与结构的区别在于内存布局,在联合中,所有成员都是从便宜地址0开始存储,这样每个成员重叠在一起
某一时刻,只有一个成员正在存储于该地址。一般用来节省空间,比如2个相互排斥的字段,不可能同时弧线,就可以放在一个联合中爱节省空间
enum 枚举名 {} ; 把一串名字与一串整型值联系在一起,
typedf 为类型引入一个新名字
#define 宏文本替换