汕头住房和城乡建设厅网站,网站建设种类 优帮云,怎么做购物平台网站,如何建立网站数据库连接C语言深度剖析:关键字C语言深度剖析:关键字前言定义与声明#xff08;补充内容#xff09;最宏大的关键字-auto最快的关键字-register关键字static被冤枉的关键字-sizeof整型在内存中的存储原码、反码、补码大小端补充理解变量内容的存储和取出为什么都是补码整型取值范围关于…
C语言深度剖析:关键字C语言深度剖析:关键字前言定义与声明补充内容最宏大的关键字-auto最快的关键字-register关键字static被冤枉的关键字-sizeof整型在内存中的存储原码、反码、补码大小端补充理解变量内容的存储和取出为什么都是补码整型取值范围关于if判断的问题bool变量bool变量和零值的比较float类型数据的比较float变量与零值的比较不安全函数问题switch语句的小细节三大循环中的细节打破对goto语句的偏见void的一些细节void*的运算操作关于return的细节return怎样将值返回const使用的几种场景const修饰普通变量const修饰数组const修饰指针const修饰函数volatile-最易变的关键字struct关键字柔性数组union关键字enum枚举类型typedef关键字typedef和#define宏的区别总结数据类型关键字12个控制语句关键字12个存储类型关键字5个其他关键字3个C语言深度剖析:关键字
前言 本篇内容既是对之前C语言学习的重点复习内容也是对之前内容的补充大体内容逻辑参考了《C语言深度解剖》这本书所以这节内容更像是个对C语言学习的一个重点内容复盘也就是将C语言重点知识重新串一遍同时也对之前一些掌握不太好的知识的补充。所以下面几乎都是一个一个的知识点友情提示不是很适合零基础的同学。基础不太好的请将C语言的基础内容部分系统进行学习。 主要将C89/C99标准的主要的32个关键字整体上来总结复习一遍就是我们的目的 定义与声明补充内容 1.程序运行需要加载到内存中 2.程序计算需要使用变量 定义变量的本质在内存中开辟一块空间用来保存数据。 变量声明extern关键字声明一个变量或函数已经存在可以改变其作用域。 变量初始化与赋值 变量的初始化在为变量开辟空间的同时将数据存放进去 变量的赋值将数据放入已经开辟好的空间里面 最宏大的关键字-auto auto这个关键字是只能用来修饰局部变量的意思就是表示局部变量只能在其所在的代码块内有效出代码块则销毁。也就是限制了局部变量的生命周期auto已经是一个非常原始的关键字了由于现在编译器已经很智能的解决这些了所以已经不再需要它了。 最快的关键字-register register建议将变量放入寄存器中 那么什么样的变量可以采用register呢 1.局部的(全局会导致CPU寄存器被长时间占用) 2.不会被写入的(写入就需要写回内存后续还要读取检测的话register的意义在哪呢) 3.高频被读取的(提高效率所在) 4.如果要使用请不要大量使用因为寄存器数量有限 关键字static 修饰全局变量只能在文件内部使用改变了作用域 修饰局部变量相当于改变了声明周期 修饰函数只能在文件内部使用相当于改变了作用域 被冤枉的关键字-sizeof
首先记住sizeof不是函数不是函数不是函数
三种写法
sizeof(int)
sizeof(a)
sizeof asizeof(i)内部i不会自增。 整型在内存中的存储
原码、反码、补码 C语言操作符大全 在讲操作符时有详细说明不再赘述。 大小端补充 数据低位放在低地址处则为小端口诀小小小。不符合则为大端。 理解变量内容的存储和取出 数据存入时和类型无关存入二进制即可取出时才看类型。 为什么都是补码 计算机在存储数据都是二进制原因 1.CPU内只集成了加法器只有补码才能解决减法问题。 2.补码与原码相互转换其运算过程是相同的不需要额外的硬件电路。 整型取值范围 记住char类型有符号-128~127无符号0 ~255理解怎么计算。 关于if判断的问题 例如语句if(flag1),从正常逻辑上来讲我们通常想的是如果flag1则…但是实际上如果更系统地来描述的话应该是 先计算括号内的表达式逻辑值然后进行判断。 bool变量 布尔变量C99中引入的概念由于我们目前仍主要按C89/C90标准所以使用并不多但是不能一口咬死C语言没有bool类型。 _Bool就是一个类型不过在新增头文件stdbool.h中被重新用宏写成了bool为了保证C/C兼容性 。 bool a0;sizeof(a);可以求出布尔变量的大小理论上来讲表示真假一个字节就可以但是具体还是取决于编译器。 但是还有一个全部大写的BOOL,TRUE,FALSE, 这是在VS中看到的源文件微软自己搞出的一套定义不要使用为了保证代码的可移植性。 bool变量和零值的比较 bool变量与零值的比较有多种方法例如if(passtrue);if(pass 0);if(pass false);(pass为一个布尔变量)但是这样的写法都不推荐我们推荐以下两种写法 if(pass)
if(!pass);float类型数据的比较 关于浮点型数据存储的细节不再赘述链接C进阶数据在内存中的存储 很多浮点型数据在内存中不能够精确存储因此一定不能将两个浮点数直接比较例如if(a0.2)这是绝对不能正确判断的正确判断两个浮点数存储的方式是定义一个宏EPS假设为0.0000001如果fabs((a-0.2))EPS,fabs是一个函数取绝对值意思就是只要差值在一定范围内则视为相等。下面举一个完整的代码例子 #define EPS 0.00000000001
#include math.h
#include float.h
int main()
{float a 3.14f;/*第一种写法*/if (fabs(a - 3.1) EPS){printf(you can see me!\n);}/*第二种写法*/if ((a - 3.1) DBL_EPSILON){printf(you can see me!\n);}return 0;
}其中DBL_EPSILON是一个很小的正数一般用来判断比较浮点数。 float变量与零值的比较 有了上面基础就很好理解了float变量与零值的比较就相当于 if(fabs(x-0.0)DBL_EPSILON)或if(fabs(x)DBL_EPSILON)即可。 不安全函数问题 两种解决方法 #define _CRT_SECURE_NO_WARNINGS 1//(注意必须要放在头文件之前)
#pragma warning(disable: 4996)switch语句的小细节 case后面的语句中可以多语句但是不可以定义变量如果一定有需求的话封装成一个函数。 三大循环中的细节 do while循环注意最后分号不能省略for循环while循环。 三种循环中的break作用一致都是跳出循环。 重点注意for循环中的continue是跳过本次循环到条件调整处而在while和do while循环都是continue直接跳到条件判断处。 打破对goto语句的偏见 在市面上几乎所有的书里面都会有不建议使用goto语句实际上很多公司内部的代码规范也是命令禁止使用goto语句但是goto语句绝对不仅仅是毫无用处不推荐使用goto语句仅仅是因为很多实力程度不够的程序员使用会造成逻辑混乱很容易出错但是在一些大型工程里面goto语句也是非常重要的一部分。例如 这是Linux内核中的部分源代码可以看到其实goto语句是使用非常频繁的如果你要用工具去数一下数量的话可能达到几十万行。 所以goto语句不是炸弹仅仅是因为我们的公司当前的业务逻辑并不复杂。 void的一些细节 定义变量的本质开辟空间 而void作为空类型理论上是不应该开辟空间的即使开了空间也仅仅作为一个占位符看待 所以既然无法开辟空间那么也就无法作为正常变量使用既然无法使用编译器干脆不让他定义变量。 在vs2013中sizeof(void)0 在Linux中sizeof(void)1但编译器依旧理解成无法定义变量 所以不要一口咬死sizeof(void)大小一定是0这个是没有明确标准规定的。 自定义函数的默认返回值是int函数返回值使用void时也不可以不写返回值 没有返回值如果不写void会让阅读你代码的人产生误解他是忘了写还是想默认int 在函数参数部分不写void的话就是明确指定不能给函数传参数 void*的运算操作 在vs中对void*的变量是不允许的例如 int main()
{void *p NULL;p; //报错p 1; //报错return 0;
} 而在gcc编译器下编译就是能通过的由于编译器对void的大小认定不一致导致了这样的现象。 Linux 上可用的 C 编译器是 GNU C 编译器它建立在自由软件基金会的编程许可证的基础上因此可以自由发布。 GNUC 对标准 C 进行一系列扩展以增强标准 C 的功能 。 关于return的细节 在使用上由于函数在栈上开辟空间在出函数后函数内的临时变量也会释放(并没有销毁数据而是将数据设置为无效数据之后使用直接覆盖)不要返回函数内部创建的指针在后续使用是非法访问。 return怎样将值返回 可以看到是通过一个寄存器来实现的所以当我们写函数有返回值但是不return的话这次函数的返回值依旧在寄存器中当下次使用时有可能出现一些意想不到的错误。 const使用的几种场景
const修饰普通变量 变量直接为不可直接修改的值没什么好说的。 const修饰数组 const arr[]{1,2,3,4,5} 数组中的每一个元素都不可直接被修改。 const修饰指针 只需要看const修饰谁即可。 const修饰函数 const修饰函数参数函数参数不可直接被修改 const修饰函数返回值函数返回值不可直接被修改 volatile-最易变的关键字 volatile这个关键字可以说是个非常冷门的关键字很多人甚至都没有听说过或者听说过但是也没有用过今天我就详细来解释一下这个关键字 volatile这个关键字其实作用有些特殊 总结来说作用是防止内存被覆盖保证内存的可见性 我们用下图来理解 那么这个现象怎么验证呢 我们可以在linus环境下用gcc编译器详细看到 验证过程 gcc test.c -O2 -g //以O2级别进行代码优化
objdump -S -d a.out a.s //对形成的a.out可执行程序进行优化 const volatile int a 10; 这句代码看起来怪怪的但其实是可以编过去的。 const要求你不要进行写入就可以。volatile意思是你读取的时候每次都要从内存读。两者并不冲突。 虽然volatile就叫做易变关键字但这里仅仅是描述它修饰的变量可能会变化要编译器注意并不是它要求对应变量必须变化这点要特别注意。 可能有人会疑惑的是我在死循环的过程中变量怎么可能改变呢 这是因为我们目前写的程序都是单进程如果有多进程的程序的话变量的值是有可能改变的。 struct关键字 基本知识不再废话重要要注意结构体的大小计算问题,结构体内存对齐 注意一个空结构体问题在VS下是不被允许定义的但是在Linux中gcc环境下是可以的计算大小为0 柔性数组 柔性数组在C99中才有但是大多编译器对C99标准支持并不完全所以不再过多的赘述细节请看 自定义类型结构体、枚举、联合体 union关键字 需要重点理解的就是联合体在内存中的保存经典问题如何用程序确认当前系统的存储模式 还有个位域的问题但是不太重要详情见上链接自定义类型结构体、枚举、联合体 enum枚举类型 详情见上链接自定义类型结构体、枚举、联合体 typedef关键字 1.对一般类型进行重命名 2.对结构体类型进行重命名 3.对指针进行重命名 4.对复杂结构进行重命名(数组类型为例先不要搞那么复杂 typedef和#define宏的区别 #define定义的宏只是简单的文本替换 而typedef定义了一个全新的变量 对比
总结 以上就是所有的32个关键字总结可能关键字并不是详细但是还是那句话这节更是对C语言知识的复盘一个查漏补缺的过程。 数据类型关键字12个 char 声明字符型变量或函数 short 声明短整型变量或函数 int 声明整型变量或函数 long 声明长整型变量或函数 signed 声明有符号类型变量或函数 unsigned 声明无符号类型变量或函数 float 声明浮点型变量或函数 double 声明双精度变量或函数 struct 声明结构体变量或函数 union 声明共用体联合数据类型 enum 声明枚举类型 void 声明函数无返回值或无参数声明无类型指针 控制语句关键字12个 循环控制5个 for 一种循环语句 do 循环语句的循环体 while 循环语句的循环条件 break 跳出当前循环 continue 结束当前循环开始下一轮循环 条件语句3个 if : 条件语句 else 条件语句否定分支 goto 无条件跳转语句 开关语句 3个 switch :用于开关语句 case 开关语句分支 default 开关语句中的“其他”分支 返回语句1个 return 函数返回语句(可以带参数也看不带参数) 存储类型关键字5个
存储类型关键字5个 auto 声明自动变量一般不使用 extern 声明变量是在其他文件中声明 register 声明寄存器变量 static 声明静态变量 typedef 用以给数据类型取别名(但是该关键字被分到存储关键字分类中虽然看起来没什么相关性) 注意存储关键字不可以同时出现也就是说在一个变量定义的时候只能有一个 例如auto static int a这是绝对错误的 其他关键字3个
其他关键字3个 const 声明只读变量 sizeof 计算数据类型长度 volatile 说明变量在程序执行中可被隐含地改变