这篇文章主要介绍了C语言中的细节分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。
①:
#include <stdio.h> int main() { char a = 3; char b = 127; char c = a + b; printf("结果是:%d",c); return 0; }
您想想这个题的答案是多少?先不要看后面的答案哦
答案是 -126, 嘿嘿,是不是答错了呢?先不要着急,继续看下面的题
②:
#include <stdio.h> int main() { char a = 0xb6; short b = 0xb600; int c = 0xb6000000; if(a==0xb6) printf("a"); if(b==0xb600) printf("b"); if(c==0xb6000000) printf("c"); return 0; }
您想想这个题的答案是什么呢? 先不要看后面的答案哦
答案是 c,嘿嘿,是不是又回答错误,先不要着急,再看看后面的题
③:
#include <stdio.h> int main() { char c = 1; printf("%u\n", sizeof(c)); printf("%u\n", sizeof(+c)); printf("%u\n", sizeof(-c)); return 0; }
您想想这个题的答案是什么呢? 先不要看后面的答案哦
答案是1 4 4,嘿嘿,是不是又回答错误,先不要着急,再看看后面的题
①:
#include <stdio.h> int main() { int c = 3; int ret = c + --c; printf("%d",ret); return 0; }
您想想这个题的答案是什么呢? 先不要看后面的答案哦
答案是 5 或者 4,是不确定
的,嘿嘿,是不是又回答错误,先不要着急,再看看后
面的题
②:
int main() { int i = 10; i = i-- - --i * ( i = -3 ) * i++ + ++i; printf("i = %d\n", i); return 0; }
您想想这个题的答案是什么呢? 先不要看后面的答案哦
答案有很多个,在不同的编译器结果不同,也就是说还是不能确定结果
,嘿嘿,是不是仍然回答错误了,先不要着急,再看看后面的题
③:
#include <stdio.h> int fun() { static int count = 1; return ++count; } int main() { int answer; answer = fun() - fun() * fun(); printf( "%d\n", answer);//输出多少? return 0; }
您想想这个题的答案是什么呢? 先不要看后面的答案哦
答案是不同编译器不同结果
,嘿嘿,是不是又回答错误,先不要着急,再看看后面的题
④:
#include <stdio.h> int main() { int i = 1; int ret = (++i) + (++i) + (++i); printf("%d\n", ret); printf("%d\n", i); return 0; }
这是最后一个题了,你目前答对了几道了呢??请在评论回答试试.嘿嘿
这个题的答案是不同编译器不同结果
表达式求值的顺序一部分是由操作符的优先级和结合性决定。
同样,有些表达式的操作数在求值的过程中可能需要转换为其他类型。
什么是隐式类型转换,整型提升,整型截断?
C的整型算术运算总是
至少以满足整型类型的精度
来进行的。
为了获得这个精度,表达式中若有char
和short
类型必须在使用之前转换称为整型,这个过程叫做整型提升
.
一个较大数据类型存储在较小数据类型中的过程叫做整型截断
,比如整型a = 500
,但是a把他的值放到了字符型b
中,b不能完全存放a,就会发生整型截断
.
而这个转换行为叫做隐式类型转换
1.1.1 第一题讲解
#include <stdio.h> int main() { char a = 3; char b = 127; char c = a + b; printf("结果是:%d",c); return 0; }
我们知道计算机中一切都是操作的补码.所以这里也是一样,(不明白原码补码反码的自己去百度下哦).
其中 正数的 原码 反码 补码 一模一样
… …负数的 补码是反码加一,反码是原码除了符号位所有位都按位取反
a只有1个字节,他的补码是00000011
(二进制)
b只有1个字节,他的补码是01111111
(二进制)
a加b,进行了算术操作,需要提升到4个字节.而整型提升有两种方式
算术提升:在最前面补符号位数字,直到32位
逻辑提升:无论什么都只补0,直到32位
大多数计算器都是进行的算术提升
,这里便讲解算术提升
.
a算术提升后就是00000000000000000000000000000011
b算术提升后就是00000000000000000000000001111111
a+b的结果是------00000000000000000000000010000010
存进c里面,c又只有1个字节,所以发生整型截断,只存取最后8位10000010
此时c存取的是补码,且c是有符号型,记住了!!
,打印显示时候会还原位原码的,该补码对应的数字就是 -126
下面有张字符类型原码补码反码
的对应理解图
所以有符号的范围是 [-128,127]
无符号的范围是[0,255]
变成圆圈更好理解,自己按照有无符号对应去找就行.
1.1.2 第二题讲解
#include <stdio.h> int main() { char a = 0xb6; short b = 0xb600; int c = 0xb6000000; if(a==0xb6) printf("a"); if(b==0xb600) printf("b"); if(c==0xb6000000) printf("c"); return 0; }
0xb6的二进制是 00000000000000000000000010110110
存进去a中,发生截断,得到1011 0110
0xb600的二进制是00000000 00000000 10110110 00000000
存进去b中发生截断,得到10110110 00000000
0xb600000010110110 00000000 00000000 00000000
存进去c中刚刚好.
a此时存的是补码,a又与0xb6比较,参与了运算,所以又整型提升为11111111 11111111 11111111 10110110
,
整型提升后的数字与0xb6的二进制不一样,所以不等.
b此时存的是补码,b又与0xb600比较,参与了运算,所以又整型提升为11111111 11111111 10110110 00000000
整型提升后的数字与0xb600的二进制不一样,所以不等
c此时存的是10110110 00000000 00000000 00000000
,注意哦~,有人可能会问0xb6000000
是正数啊,不!!!!!
,字面常量只要不带符号就是默认int类型.所以此时0xb6000000
的整数值是超过int的,就会认为它此时的二进制是负数的补码.所以c与0xb6000000
是相等的.
1.1.3 第三题讲解
#include <stdio.h> int main() { char c = 1; printf("%u\n", sizeof(c)); printf("%u\n", sizeof(+c)); printf("%u\n", sizeof(-c)); return 0; }
sizeof
测量的是类型属性的值.c
的类型是char
,只有一个字节,所以答案是1+c
,+
是单目操作符,与c在一起发生了整型提升,变成了int,所以是4个字节,答案是 4-c
,同样的道理,仍然是4
上面我们说到,如果操作数大小 小于int,会发生整型提升,但是如果都是大于等于int大小的类型参与算术运算呢?? 这里就会涉及到
算术转换
.
什么是算术转换呢? 比如下面的例子:
#include <stdio.h> int main() { int a = -127; unsigned int b = 129; if(a > b) { printf("a会大于b"); } return 0; }
结果会打印 a会大于b
因为这就是算术转换,即还是需要满足同类型运算,unsigned int 比int 高,所以a的值会默认是 无符号的,a就会比b大.
算术提升方向:
复杂表达式的求值有三个影响的因素。
操作符的优先级操作符的结合性是否控制求值顺序。
1.3.1 什么是优先级?
就是决定先计算什么.比如d = a + b*c
. 因为*
的优先级高于+
,所以,先算b*c
,再算+
1.3.2 什么是结合性?
就是同样优先级,就决定从哪个方向计算.比如d = a * b * c
,因为连续的*
,优先级已经没有用了,所以此时就是结合性,*
的结合性是从左到右.也就是说先计算a*b
然后计算*c
.
1.3.3 什么是求值顺序?
就是只计算哪边.c语言的操作符具有求值顺序的只有寥寥几个,比如||, && , !
那求值顺序
到底什么意思呢?
比如a等于0,b等于2,c等于3,d = a && b && c
,d的值最后是0,但是在运算时候只到a就完结了,因为&&
是只要碰到假就是假,后面的真假已经无关,a为0,是假,所以后面不用再管.这就是求值顺序.
下面有两个关于求值顺序的小练习:
#include <stdio.h> int main() { int i = 0,a=0,b=2,c =3,d=4; i = a++ && ++b && d++; printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d); return 0; }
答案是 1 2 3 4
.理由: a是后置++,先使用a=0的值,一开始就遇到假了,后面不再执行.但是a还是增加了的,因为后置加加是 先使用再加加
#include <stdio.h> int main() { int i = 0,a=0,b=2,c =3,d=4; i = ++a || ++b || d++; printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d); return 0; }
答案:1 2 3 4
,前置++,即先加加,a成了1,因为||
一遇到真就结束,不再管后面真假.所以只有a变化了.
这里有张操作符属性的表:
其中优先级从上往下逐渐降低
1.3.4 第四题讲解
#include <stdio.h> int main() { int c = 3; int ret = c + --c; printf("%d",ret); return 0; }
ret = c + --c
中有两个操作符号,先看优先级
,--
的优先级高于+
,所以决定了先算–c,但是+
号左边的c是什么时候准备的呢? 我们知道,c语言是编译性语言,在代码写好以后是需要先进行编译为机器语言,然后执行的.那么在编译时候,+号左边的值是在--c
之前就已经编译好了呢,还是--c
之后编译好了呢?这是不确定的.
在vs编译器下答案是 4,他是在--c
之后准备好了c
在gcc编译器下答案是 5,他是在--c
之前准备好了c
所以: 这是问题代码,我们以后不要写这样的垃圾代码.
1.3.5 第五题讲解
int main() { int i = 10; i = i-- - --i * ( i = -3 ) * i++ + ++i; printf("i = %d\n", i); return 0; }
这个是同样的道理,虽然知道优先级,但是结合性中的操作数什么时候准备不确定,你看看在不同的编译器操作的结果:
同样是个垃圾代码
1.3.6 第六题讲解
#include <stdio.h> int fun() { static int count = 1; return ++count; } int main() { int answer; answer = fun() - fun() * fun(); printf( "%d\n", answer);//输出多少? return 0; }
()
函数调用符号优先级最高,但是这里有三个,到底先调用哪个呢??这又不确定了.
vs编译器是从左往右依次调用的,结果为-10.
但是其他编译器呢??大家去试试gcc,codeblocks,Devc++,你一定会发现完全不一样.
1.3.7 第七题讲解
再说这个题之前,博主一定要批判某些高校,为什么呢??学校教大家++
符号时候是不是最喜欢用这种类型来考大家?在这明确告诉大家,这完全是在浪费时间!!!因为这种代码根本没有意义!!!.就是垃圾代码
#include <stdio.h> int main() { int i = 1; int ret = (++i) + (++i) + (++i); printf("%d\n", ret); printf("%d\n", i); return 0; }
首先,()
括号的优先级最高,但是有三个,到底先计算哪个?
其次,一个i
的变化,会不会影响另外的?这里也不确定.和第四题我们说的那个编译之前c
到底什么时候准备一样.不确定.!!!
vs编译器的值是 12 4gcc编译器的值是 10 4
不同的编译器不同的值,结果不一样,这样的代码不配叫代码.!!!
所以,以后看到这种题直接跳过,没有意义.
感谢你能够认真阅读完这篇文章,希望小编分享的“C语言中的细节分析”这篇文章对大家有帮助,同时也希望大家多多支持亿速云,关注亿速云行业资讯频道,更多相关知识等着你来学习!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。