C语言深度解剖记录
前言
一上来肯定要吹逼了,当鸡汤了,振奋一下自己。
第一章 关键字
- 声明和定义的区别:创建对象并分配内存,是定义;否则为声明。
感觉分这么细也没啥用
- register寄存器变量,速度快,必须是单个的值,长度小于等于整型的长度,register可能不在内存中,不要用&获取它的地址
没用过。不用&获取地址,第一次知道
- static
和之前了解的没什么分别
-
数据类型的作用,把它比喻成了模子,差不多吧
-
变量的命名规则 介绍的类似匈牙利法的命名规则,有前缀、后缀。看看谷歌 kernel的说明吧。链接如下:
https://www.kernel.org/doc/html/v4.10/process/coding-style.html
https://google.github.io/styleguide/cppguide.html
-
不要用8进制 确实,平时不怎么用,也不容易分清8进制具体的值,还是十进制和十六进制香
-
sizeof是关键字, 函数求值是在运行时,sizeof求值是在编译时,不明觉厉
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xrkZfhx7-1638170548779)(C:\Users\fj2021\AppData\Roaming\Typora\typora-user-images\image-20211129091755990.png)]
从上往下的结果: 4 4 400 4数组溢出但不出错,编译时运行的内容 4 4 4
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2dHD9iDl-1638170548789)(C:\Users\fj2021\AppData\Roaming\Typora\typora-user-images\image-20211129095422306.png)]
结果是255 因为a[255] = 0
考察补码,对补码不是很熟,不能第一时间反应过来
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hyxCaiQ5-1638170548793)(C:\Users\fj2021\AppData\Roaming\Typora\typora-user-images\image-20211129095654200.png)]
正0 负0 都是 0000 0000 -20转为无符号数,10010100为148 +10 = 158 或236+ 10=246
还有一个问题 死循环,无符号整数 条件是大于等于0
- 布尔变量与0值比较,书中的意思是不要判断变量是0还是1,写成 if( isFlag )这样的形式
感觉有些多余,因为单片机里虽然兼容C99标准,但是很少使用布尔变量,多用0表示假,非0表示真。就算在vs2019里也得包含stdbool才能使用布尔量。
- 浮点数和0比较,常见的fabs(相减)<=实现定好的值,float一般是1e-6就行
- 指针和0比较,用NULL,但是C++里,值是一样,但是这样看起来更好 p != NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
- 1.7.4——1.7.6 介绍了一堆if else 和编程风格,大括号换行的问题,和google正相反,无所谓了
- 1.8 switch case语句 case后面只能是整型或字符型的常量或常量表达式,容易忘,但是平时用的时候很少主动用其他的
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qja2XT5I-1638170548796)(C:\Users\fj2021\AppData\Roaming\Typora\typora-user-images\image-20211129102352763.png)]
这个问题还真不确定,应该是0吧
-
const 别把它当成常量,当成只读变量吧。不能用在switch语句里。应该属于RO data
修饰指针的时候,const和谁近就修饰谁。const也可以用在函数参数或者函数返回值
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gWkW949Q-1638170548799)(C:\Users\fj2021\AppData\Roaming\Typora\typora-user-images\image-20211129112535886.png)]
-
volatile:防止编译器优化,必须要去内存中取这个值
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ShNxVZW0-1638170548801)(C:\Users\fj2021\AppData\Roaming\Typora\typora-user-images\image-20211129104258181.png)]
- 1.15 extern 1.16 1.17
extern是声明 柔性数组的概念,不常用,用到再看 union,会判断大小端即可 位域用的比较少,但是挺复杂
- enum 枚举成员里是枚举常量,枚举常量的值为整型常量表达式
- 1.19 typedef
typedef struct stu
{
int score;
int age;
}STU,*STU_P;//一个是结构体变量,一个是结构体指针
int main(void)
{
STU stutest;
const STU_P stu3;
STU_P const stu4;
stu3 = &stutest;//用了typedef,不是替换,如果是替换,const修饰stu3 stu4指向的内容,不能改变;现在STU_P作为整体是一个类型,const修饰stu3 stu4,即指针本身不能被修改,所以修改这两行会报错,下面的赋值不会报错
stu4 = &stutest;
stu3->age = 22;
stu3->age = 90;
printf("%d %d\n", stu3->age, stu3->score);
}
typedef static int int32;不对,vs报错不能指定多个存储类
typedef和复杂的声明在一起,挺麻烦,复杂的声明总是心里没底,不敢保证每一次都对,1.19.4后面是一堆习题,测试typedef和#define,有时间再做
第二章 符号
2.4逻辑运算符
逻辑运算符的短路效应,运算符有函数调用的话,把函数调用作为第一个操作数,因为有短路效应,一旦另一个操作数可以判断出整个逻辑表达式的结果,那么这个函数调用可能就不会执行。如下,函数func有可能不执行
if( x==1 && func() )
2.5位移运算符
书中位移运算符介绍的不太好,有点乱。有一个例子不理解
uint8_t已经是8为了,为什么会变成0xffa5呢?在stm32上这样写过,没什么问题啊。在vs2019里用如下代码测试,也正常
typedef unsigned char uint8_t;
int main(void)
{
uint8_t port = 0x5a;
uint8_t result_8 = (~port) >> 4;
printf("%x", result_8);
}
2.7++ --操作符
搞了一堆++i+++i+++i,头大,好像是UB,没必要吧
2.8 2/(-2)
作者没有给答案,写了一堆,有点故弄玄虚。VS2019运行,2 / (-2) == -1 2 % (-2)==0.
2.9 优先级
给出了优先级表和常见容易出错的地方
第三章 预处理指令
3.1 宏定义
介绍了挺多,挺详细的,但是用不上啊。反斜杠换行,do-while语句,多用小括号
3.2——3.5
条件编译,文件包含,用过,没什么特殊。#error没用过 #line没用过
3.6 #pragma
pragma的用法真是好复杂啊,现在用到的就是结构体对齐,
3.7# 和##
在别的地方看过,这两个的用法比书上写的复杂一些,之前看的有些懵。一个是把宏定义里的字符串变成值,第二个是拼接,尤其是带参数的宏,更麻烦
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tcoXkl4g-1638170548810)(C:\Users\fj2021\AppData\Roaming\Typora\typora-user-images\image-20211129142608589.png)]
第四章 指针和数组
4.1指针与内存布局
举了个例子,但是在vs2019里执行不了,在指定位置写入值,这时候程序执行不了,理解作者的想法就行了
4.2数组
用省政府和市政府类比了&a[0]和&a的区别。步长不同,一个是数组首元素的地址,一个是数组首地址
数组名a可以作为右值,不能左值
4.3指针和数组
指针是指针,数组是数组 数组指针和指针数组,可以理解
char a[5] = { 'A','B','C','D','E' };
char(*p3)[5] = &a;
char(*p4)[5] = a;
printf("a=%p p3=%p p4=%p\n", a, p3, p4);
printf("a=%p p3+1=%p p4+1=%p\n", a, p3+1, p4+1);
char a[5] = { 'A','B','C','D','E' };
char(*p3)[3] = &a;
char(*p4)[3] = a;
printf("a=%p p3=%p p4=%p\n", a, p3, p4);
printf("a=%p p3+1=%p p4+1=%p\n", a, p3+1, p4+1);
char a[5] = { 'A','B','C','D','E' };
char(*p3)[10] = &a;
char(*p4)[10] = a;
printf("a=%p p3=%p p4=%p\n", a, p3, p4);
printf("a=%p p3+1=%p p4+1=%p\n", a, p3+1, p4+1);
根据测试结果,p3+1 p4+1增加对应的步长
地址的强制转换,第一个没问题,第二个一开始弄错了,+1之后不是数组的第二个元素,而是从第一个元素的第二个字节开始,往后数4个字节,作为一个int,这样*ptr2的值取决于大小端了
题,第二个一开始弄错了,+1之后不是数组的第二个元素,而是从第一个元素的第二个字节开始,往后数4个字节,作为一个int,这样*ptr2的值取决于大小端了
可以根据图片