熊熊出没 发表于 2024-5-19 03:55:36

对C语言符号的一些冷门知识运用的剖析和总结

符号


目次

[*]符号

[*]注释

[*]奇怪的注释
[*]C风格的注释无法嵌套
[*]一些特殊的注释
[*]注释的规则建议

[*]反斜杠'\'

[*]反斜杠有续行的作用,但要注意续行后不能添加空格

[*]回车也能起到换行的作用,那续行符的意义在哪?

[*]反斜杠的转义功能

[*]单引号和双引号

[*]字面值,字符串,字符,字符变量的大小
[*]为什么sizeof('1')的大小是4 ?
[*]char类型一直在发生截断.
[*]不允许定义空字符常量
[*]单引号内一样平常是一个字符,还可以有多个字符

[*]短路
[*]位运算

[*]位运算和逻辑运算区别
[*]位运算的几点注意
[*]异或的运算规则
[*]异或的经典问题:两数交换
[*]位操纵建议使用宏定义好后使用
[*]位运算整型提升问题

[*]左移和右移

[*]概念
[*]移位运算容易误解成移位赋值

[*]++(--)

[*]后置的++(--)真的是先使用,后自增吗?
[*]后置++和前置++的差别

[*]复杂表达式

[*]++i+++i+++i
[*]贪心法
[*]编译器的贪心规则不是保证一定正确的规则

[*]C语言的四种取整方式:

[*]零向取整

[*]trunc函数(C99)

[*]trunc的使用


[*]地板取整

[*]floor函数的使用

[*]向上取整

[*]ceil函数的使用

[*]四舍五入

[*]round函数(C99)

[*]round函数的使用


[*]四种取整方式代码

[*]负数"取模"

[*]基本概念
[*]修正定义
[*]取整规则决定商的值
[*]取模和取余不一样.



注释

C语言风格的注释: /* 这是注释 */
C++风格的注释: //这是注释
奇怪的注释

int main()
{
int /* */ i;                  //1.正确
char *s = "abcdefgh //hijklmn"; //2.正确://是字符串的一部分
//Is it a\
valid comment?                  //3.正确注释可以通过续行符续行
in/* */t j;                     //4.报错
system("pause");
return 0;
}gcc4.8.5的语法检查:
https://img2023.cnblogs.com/blog/2921710/202405/2921710-20240505153120755-420107743.png
为什么4是错的?看预编译后的结果:
实行预编译命令:gcc -E test.c -o test.i
https://img2023.cnblogs.com/blog/2921710/202405/2921710-20240505153120329-703600001.png
(发现预处理阶段gcc并没有报错,说明问题是出在编译后的)
https://img2023.cnblogs.com/blog/2921710/202405/2921710-20240505153119907-39455605.png
可以发现 第9行 int中间多了空格,说明注释在预编译可能会被转化成空格.
而第5行的int i中间只有一个空格,说明注释是直接被删除或归并成一个空格.
结论:注释在预编译后会替换成空格,具体替换方式由编译器决定
C风格的注释无法嵌套

预编译:
https://img2023.cnblogs.com/blog/2921710/202405/2921710-20240505153119567-1065498201.png
可以发现预编译后上面两个\*没有了,下面还有一个*\.
原因: C语言把遇到的第一个/*之后的数据都当作注释,直到遇到第一个/*,因此嵌套在中间的/*不会再被检查出来.且在预处理阶段会删除掉这些注释.所以嵌套的注释会留下一个*.
C风格注释采用的是就近原则.如果要能嵌套,就得使用就远原则才足以容纳多个嵌套
至于为什么不选择像代码块这样的匹配规则,可能就是汗青遗留问题了,有知道的朋友可以分享在评论区.
一些特殊的注释


[*]宏中间的注释
https://img2023.cnblogs.com/blog/2921710/202405/2921710-20240505153119203-466598787.png

[*]除号和*号同时使用
https://img2023.cnblogs.com/blog/2921710/202405/2921710-20240505153118043-2001095525.png
解决方法:
1.在/和*后加上一个空格int b = 20/ *p
2.用圆括号把(*)括起来int b = 20/(*p) ,推荐这种方式,更为直观
注释的规则建议


[*]注释应当正确、易懂,防止有二义性。错误的注释不但无益反而有害。
[*]边写代码边注释,修改代码的同时修改相应的注释,以保证注释与代码的一致性。不再有用的注释要即使删除。
[*]注释是对代码的的“提示”,而不是文档。程序中的注释应当简单明白,注释太多了会让人眼花缭乱。
[*]对于全局数据(全局变量、常量定义等)必须要加注释。
[*]注释的位置应与被描述的代码相邻,可以与语句在同一行,也可以在上行,但不可以放在下方。同一结构中不同域的注释要对齐。
[*]当代码比力长,特别是有多重嵌套时,应当在一些段落的竣事处加注释,便于阅读。
[*]注释代码段时应注意“为何做(why) ",而不是怎么做”(how)“
说明怎么做的注释一样平常停顿在编程语言的条理,而不是为了说明问题。努力阐述“怎么做”的注释一样平常没有告诉我们操纵的意图,而指明“怎么做”的注释通常是冗余的。
[*]数值的单位一定要注释。如时、分、秒等单位。
[*]对变量的范围给出注释,尤其是参数。
[*]对一系列的数字编号给出注释,尤其在编写底层驱动程序的时候(比如引脚编号)。
[*]在复杂的函数中,在分支语句、循环语句竣事之后需要适当的注释,方便区分各分支或循环体。(很多时候代码写长后,一个屏幕内看不到下面的代码,不知道哪个才是相应代码块匹配的花括号)
反斜杠'\'

反斜杠有续行的作用,但要注意续行后不能添加空格

如图,在续行符前加多个空格没有影响:
https://img2023.cnblogs.com/blog/2921710/202405/2921710-20240505153117672-1627688362.png
但续行符后不能加空格
https://img2023.cnblogs.com/blog/2921710/202405/2921710-20240505153117305-1861332192.png
回车也能起到换行的作用,那续行符的意义在哪?

续航符本身就有自描述性.这是续行符最大的意义,就是要明白告诉别人这里是换行的,没有二义性.如果使用回车,别人在阅读的时候有可能需要思索是不是故意换行的或者是不是不小心误敲了.
反斜杠的转义功能

转义,即对某些字符进行反斜杠,可以使之拥有超过本身的功能,
转义有两种,一种是诸如\n,\t这样的转义字符,转义后具有本身字面值之外的特殊功能;另一种诸如\",\\这种,本身字面值没有意义,而是其他功能,转义后具有字面值的意义.
转义的功能简单描述就是 (a. 字面转特殊b. 特殊转字面)
单引号和双引号

基本概念:单引号内的是字符,双引号的是字符串
字面值,字符串,字符,字符变量的大小

sizeof分别盘算大小:
https://img2023.cnblogs.com/blog/2921710/202405/2921710-20240505153116886-721431003.png
为什么sizeof('1')的大小是4 ?

C99标准的规定,'a'这种单引号引起来的字符叫做整型字符常量(integer character constant),会被编译器当作整型.因此大小是4个字节
(注意:在C++中字符常量大小为1,不同平台/语言解释是不一样的.)
char类型一直在发生截断.

char类型基本上都是接收整型字符常量,而char类型只占一个字节大小,因此只能保留整型的一个字节(首字节),即截断.
不允许定义空字符常量

单引号内至少要有一个字面值
https://img2023.cnblogs.com/blog/2921710/202405/2921710-20240505153116565-797009755.png
单引号内一样平常是一个字符,还可以有多个字符

如下图,可以发现是没有警告的.
https://img2023.cnblogs.com/blog/2921710/202405/2921710-20240505153116162-350098366.png
打印结果:
https://img2023.cnblogs.com/blog/2921710/202405/2921710-20240505153115790-1951153555.png
但是最多4个字符,多了会告警
https://img2023.cnblogs.com/blog/2921710/202405/2921710-20240505153115489-187897440.png
为什么能这样用呢? 也是上文所说的,字符常量被C语言当作整型来看待,分配了4个字节.
一个字符字面值就占一个字节,4个字符就刚好占了4个字节,整型分配的空间能够满足.
而char类型又是截断了整型的低地址字节位,因此只会输出最后一个字符字面值(小端).
短路

逻辑与&&和逻辑或||的左边条件不满足,则不需要再看后续条件的情况,就叫做短路.
int show()
{
    printf("aaaaaaa");
    return 1;
}

int main()
{
   int flag = 0;
   scanf("%d",&flag);
   flag && show();//使用短路实现的if语句效果
   //如果表达式左边为假,则表达式右边不再执行,这样的语句就称为短路.

   scanf("%d",&flag);
   flag || show();//使用短路实现的if not语句

    return 0;
}位运算

位运算和逻辑运算区别

位运算是一位对应一位的对全部位逐一进行运算(逐比特位进行运算).逻辑运算是以盘算表达式的真假为主进行运算.
位运算的几点注意


[*]呆板都是使用补码进行运算,遇到负数时不要混淆
[*]~(-1)的结果是0,~(0)的结果是-1,按位取反全部位都要取反,不论是否有符号.
异或的运算规则


[*]运算规则: 相同为假, 相异为真
0000 0000 ... 0100
0000 0000 ... 0011
0000 0000 ... 0111
printf("%d\n",4^3); //结果:7
2. 任何数据和0异或,结果都是它本身
```C
printf("%d\n",1 ^ 0);//结果:1
printf("%d\n",6 ^ 0);//结果:6
printf("%d\n",7 ^ 0);//结果:7
printf("%d\n",-1 ^ 0); //结果:-1
[*]异或运算支持交换律和联合律
printf("%d\n",5 ^ 5 ^ 4);   //结果:4
printf("%d\n",5 ^ 4 ^ 5);   //结果:4 => 交换律
printf("%d\n",5 ^ (5 ^ 4)); //结果:4 => 结合律
[*]异或自己的结果是0 (消消乐)
异或的经典问题:两数交换

问题:两个变量int a = 10; int b = 20;再不使用第三个变量的前提下,怎么讲两个数进行交换?
方法一:加减法
    a = a + b;// ①
    b = a - b;// ②
    a = a - b;// ③剖析:
(在盘算机运算中,运算过程可以当作第三个变量,只是这个过程必须赋值才有意义,我们可以盘算出各种组合的值用来匹配和验证)

[*]①是将a和b的值保存到a中.原理:a+b-b = a,只要b还在,a就不会丢失.(借助a当作第三个容器)
[*]②:验证发现新a-b = 旧a,赋值给b后就能实现b变成了旧a.
[*]③:a变量保存着旧a和旧b,新b保存旧a,于是新a-新b就等于旧b.
缺点: 加法可能会有比特位递增,如果发生溢出,则会发生截断,导致数据丢失.所以这种方法仅实用于一定范围的数据
方法二:异或法
    a = a ^ b; //①
    b = a ^ b; //②
    a = a ^ b; //③
一次记住它: 等号左边是aba,右边全是a^b剖析:(消消乐)

[*]①是a和b的值保存在a中.原理:a^b^b=a,只要b还在a就不会丢失.
[*]②: 新a=10^20,对b=a^b=10^20^20,利用联合律,先盘算20^20,就可以得到a,然后再赋值给b,b变量就得到了旧a的值.
[*]③:有了旧a的值,交换律+消消乐把新a中旧a的值消掉,新a就可以得到旧b的值,完成交换.
相对于加减法的长处: 异或不会进位,不会出现比特位递增、溢出的问题
位操纵建议使用宏定义好后使用

// 0|0 = 0 ; 0|1 = 1//规律:任何数或0结果都是它本身      //用途:// 1|0 = 1 ; 1|1 = 1//规律:任何数或1,结果都是被设置为1//用途:特定 比特位 置1// 组合用途:让特定比特位置1,其他位不变// 0&0 = 0 ; 0&1 = 0; //规律:任何书与0,结果都是被设置成0//用途:特定比特位置0// 1&0 = 0 ; 1&1 = 1; //规律:任何书与1,结果都是它本身   //用途:获取特定比特位的值// 组合用途:没有干扰地获取特定比特位的值//一样平常都是用1(000...1)比力方便,通过移动1的位置,加上不同的位运算符,能够实现不同的功能.#define SETBIT(x,n)(x |= (1
页: [1]
查看完整版本: 对C语言符号的一些冷门知识运用的剖析和总结