2014/12/12C语言中你想不到的一些问题
http://github.tiankonguse.com/blog/2014/12/05/c-base/1/12
前言
自己虽然一直交叉的敲着C和cplusplus两种语言,但是其实自己就是使用一下常用的语法。
工作后又没有那么时间来看书,于是研究了一些C语言的细节来学习学习。
建议看的时候先不要看问题分析,这样才能考察自己到底会不会的。
遍历数组
问题
有时候我们要遍历一个不知道大小的数组,但是我们有数组的名字,于是我们可以通过sizeof获得数组的大小了。
有了大小我们就可以遍历这个数组了。
一般情况下大家都是从下标0开始计数,于是从来不会遇到下面的问题。
如果你遇到下面的问题你能想出是什么原因吗?
代码
1.#include2.
3.#defineTOTAL_ELEMENTS(sizeof(array)/sizeof(array[0]))4.
intarray[]={23,34,12,17,204,99,16};5.
6.intmain(){7.
for(intd=-1;d<=(TOTAL_ELEMENTS-2);d++){8.
printf("%d\n",array[d+1]);9.
}10.
11.12.
return0;13.
}
输出
1.2.
Processreturned0(0x0)executiontime:0.050s3.
Pressanykeytocontinue.4.
5.6.
7.8.
9.10.
2014/12/12C语言中你想不到的一些问题
http://github.tiankonguse.com/blog/2014/12/05/c-base/2/12
11.12.
13.14.
15.16.
17.18.
19.20.
end
分析
这个原因我一提,大家也都可以想到。
sizeof返回的类型是unsignedint.
unsignedint与int进行运算还是unsignedint。
然后-1和unsignedint比较,会先把-1转化为unsignedint。
这样-1的unsignedint就很大了,所以没有输出了。
dowhile
问题
大家在dowhile中使用过continue吗?
没有的话来看看这个问题吧。
代码
1.#include2.
3.intmain(){4.
inti=1;5.
do{6.
printf("%d\n",i);7.
i++;8.
if(i<15){9.
continue;10.
}11.
}while(false);12.
return0;13.
}
输出
2014/12/12C语言中你想不到的一些问题
http://github.tiankonguse.com/blog/2014/12/05/c-base/3/12
1.12.
3.Processreturned0(0x0)executiontime:0.041s4.
Pressanykeytocontinue.5.
6.7.
8.9.
10.11.
12.13.
14.15.
16.end
分析
这个需要查看文档了:continue会到循环的哪个地方继续运行。
于是我查看了官方文档
Fortheforloop,continuecausestheconditionaltestandincrement
portionsofthelooptoexecute.
Forthewhileanddo...whileloops,programcontrolpassestothe
conditionaltests.
什么意思呢?
for循环遇到continue会执行for小括号内的第三个语句。
while和do...while则会跳到循环判断的地方。
宏的展开
问题
大多数情况下,我们的宏定义常常是嵌套的。
这就涉及到展开宏定义的顺序了。
下面来看看其中一个问题。
代码
2014/12/12C语言中你想不到的一些问题
http://github.tiankonguse.com/blog/2014/12/05/c-base/4/12
1.#include2.
3.#definef(a,b)a##b4.
#defineg(a)#a5.
#defineh(a)g(a)6.
7.intmain(){8.
printf("%s\n",h(f(1,2)));9.
printf("%s\n",g(f(1,2)));10.
return0;11.
}
输出
1.122.
f(1,2)3.
4.Processreturned0(0x0)executiontime:0.041s5.
Pressanykeytocontinue.6.
7.8.
9.10.
11.12.
13.14.
15.16.
17.end
分析
这个问题又需要查看文档了:Macro是怎么展开的。
于是我查看了官方文档
Macroargumentsarecompletelymacro-expandedbeforetheyare
substitutedintoamacrobody,unlesstheyarestringifiedorpastedwith
othertokens.
Aftersubstitution,theentiremacrobody,includingthesubstituted
arguments,isscannedagainformacrostobeexpanded.
Theresultisthattheargumentsarescannedtwicetoexpandmacrocallsin
them.
简单的说就是宏会扫描一遍,把可以展开的展开,展开一次后会再扫描一次看又没有可以展开的宏。
2014/12/12C语言中你想不到的一些问题
http://github.tiankonguse.com/blog/2014/12/05/c-base/5/12
下面我们模拟一下这个过程就可以明白了。
对于第一个,是下面的过程。
1.↓2.
>h(f(1,2))3.
↓4.
>g(f(1,2))5.
↓6.
>g(12)7.
↓8.
>g(12)9.
↓10.
>"12"
对于第二个,是这个过程。
1.↓2.
>g(f(1,2))3.
↓4.
>"f(1,2)"
print返回值
问题
你知道printf的返回值是什么吗?
猜猜下面的代码输出是什么吧。
代码
1.#include2.
intmain(){3.
inti=43;4.
printf("%d\n",printf("%d",printf("%d",i)));5.
return0;6.
}
输出
1.4321
2014/12/12C语言中你想不到的一些问题
http://github.tiankonguse.com/blog/2014/12/05/c-base/6/12
2.3.
Processreturned0(0x0)executiontime:0.035s4.
Pressanykeytocontinue.5.
6.7.
8.9.
10.11.
12.13.
14.15.
16.17.
18.end
分析
printf的返回值是输出的字符的长度。
所以第一个输出43返回2.
第二个输出2返回1.第三个输出1.于是输出的就是4321了。
数组参数
问题
对于函数传参为数组时,你知道到底传的是什么吗?
代码
1.#include2.
#defineSIZE103.
voidsize(intarr[SIZE][SIZE]){4.
printf("%d%d\n",sizeof(arr),sizeof(arr[0]));5.
}6.
7.intmain(){8.
intarr[SIZE][SIZE];9.
size(arr);10.
return0;11.
}
输出
2014/12/12C语言中你想不到的一些问题
http://github.tiankonguse.com/blog/2014/12/05/c-base/7/12
1.4402.
3.Processreturned0(0x0)executiontime:0.039s4.
Pressanykeytocontinue.5.
6.7.
8.9.
10.11.
12.13.
14.15.
16.17.
18.end
分析
对于第二个输出,应该是40这个大家都没有什么疑问的。
但是第一个是几呢?
你是不是想着是400呢?
答案是4.
这是因为对于数组参数。第一级永远是指针形式。
也就是说数组参数永远是指针数组。
所以第一级永远是指针,而剩下的级数由于需要使用[]运算符,所以不能是指针。
sizeof的参数
问题
当我们有时候想让代码简洁点的时候,会把运算压缩到一起。
但是在sizeof中就要小心了。
代码
1.#include2.
intmain(){3.
inti;4.
i=10;5.
printf("i:%d\n",i);6.
printf("sizeof(i++)is:%d\n",sizeof(i++));7.
printf("i:%d\n",i);8.
return0;
2014/12/12C语言中你想不到的一些问题
http://github.tiankonguse.com/blog/2014/12/05/c-base/8/12
9.}
输出
1.i:102.
sizeof(i++)is:43.
i:104.
5.Processreturned0(0x0)executiontime:0.039s6.
Pressanykeytocontinue.7.
8.9.
10.11.
12.13.
14.15.
16.17.
end
分析
你猜第二个i的输出时什么呢?
11吗?
恭喜你,猜错了。
这个还需要查看文档了。
首先我的印象中sizeof是个宏,在编译器运算的。
Thesizeofisakeyword,butitisacompile-timeoperatorthatdetermines
thesize,inbytes,ofavariableordatatype.
文档上说sizeof是一个关键字,但是在编译器运算。
所以编译器是不会进行我们的那些算术等运算的。
而是直接根据返回值推导类型,然后根据类型推导出大小的。
位运算左移
问题
2014/12/12C语言中你想不到的一些问题
http://github.tiankonguse.com/blog/2014/12/05/c-base/9/12
这个问题没什么说的,你运行一下就会先感到诧异,然后会感觉确实应该是这个样字,甚至会骂这代码写的太不规范
了。
代码
1.#include2.
#definePrintInt(expr)printf("%s:%d\n",#expr,(expr))3.
intFiveTimes(inta){4.
returna<<2+a;5.
}6.
7.intmain(){8.
PrintInt(FiveTimes(1));9.
return0;10.
}
输出
1.FiveTimes(1):82.
3.Processreturned0(0x0)executiontime:0.624s4.
Pressanykeytocontinue.5.
6.7.
8.9.
10.11.
12.13.
14.15.
16.end
分析
需要我提示吗?
三个字:优先级
浮点数
问题
大家经常使用浮点数,知道背后的原理吗?
2014/12/12C语言中你想不到的一些问题
http://github.tiankonguse.com/blog/2014/12/05/c-base/10/12
代码
1.#include2.
intmain(){3.
floata=12.5;4.
printf("%d\n",a);5.
printf("%d\n",(int)&a);6.
return0;7.
}
输出
1.12.5000002.
10952376323.
4.Processreturned0(0x0)executiontime:0.651s5.
Pressanykeytocontinue.6.
7.8.
9.10.
11.12.
13.14.
15.16.
17.end
分析
为了方便大家测试,我提供了一个32位浮点型向二进制的转换器
首先int和float在32位机器上都是四字节的。
对于整数储存,大家都没什么疑问。
比如10的二进制,十六进制如下
1.000000000000000000000000000010102.
0X0000000A
2014/12/12C语言中你想不到的一些问题
http://github.tiankonguse.com/blog/2014/12/05/c-base/11/12
由于最高位代表符号,所以整数可以表示的范围就是
1.0X80000000-2^312.
0XFFFFFFFF-1负整数最小值3.
0X0000000004.
0X000000011正整数最小值5.
0X7FFFFFFF2^31-1正整数最大值
上面的二进制也就决定了4字节的整数范围是-2^31到2^31-1.
对于一个浮点数,可以表示为(-1)^SX2^Y.
其中S是符号,使用一位表示。
X是一个二进制在[1,2)内的小数,一般称为尾数,用23位表示。
Y是一个整数,代表幂,一般称为阶码,用8位表示。
其中Y又涉及符号问题了。
8位的Y可以表示0到255,取指数偏移值127(2^(8-1)-1)作为分界线,小于127的数是负数,大于的是正数。
这里我不明白为什么不使用以前数字的表示方法。
比如12.5的二进制是1100.1。
转化为上面的公式就是(-1)^01.10012^3
下面我们来推导一下这个数字的二进制是什么吧。
符号为正,所以第一位就是0了
3+127就是130了。于是使用10000010可以表示。
1.1001一般不表示小数前的1,于是只需要表示1001即可,于是使用10010000000000000000000就可以表示
了。
于是12.5的float的二进制表示就推算出来了
2014/12/12C语言中你想不到的一些问题
http://github.tiankonguse.com/blog/2014/12/05/c-base/12/12
1.010000010100100000000000000000002.
01000001010010000000000000000000
然后这个二进制对应着整数1095237632。
这样一切都解释清楚了。
当然还要注意一个问题,这里有这么一个特殊规定:阶码Y如果是0,尾数X就不再加1了。
作者「tiankonguse」于2014-12-0514:15:00更新本文」于2014-12-0500:00:00发布本文
文章声明:自由转载-非商用-非衍生-保持署名|BY-NC-SA
如果你觉得这篇文章对你有帮助,欢迎自由付费:
|
|