一些C语言的puzzle,对于理解C语言和巩固知识有很好的帮助,题目可参见 http://www.gowrikumar.com/c/ 。
puzzle 1:
#define TOTAL_ELEMENTS (sizeof(array)/sizeof(array[0])) int main(void) for (d = -1; d <= (TOTAL_ELEMENTS - 2); d++) { return 0; } |
编译运行后没有数组元素输出,这里有两个地方需要注意,该puzzle取自《c专家编程》中,涉及到sizeof和类型提升两个内容:
1 sizeof()是运算符,返回的是unsigned类型,这样TOTAL_ELEMENTS-2就为5;
2 d是int类型,在与无符号数TOTAL_ELEMENTS-2作比较的时候会进行无符号类型保护,转换为unsigned类型,所以for的条件判断为假,跳出循环。
puzzle 2:
#include <stdio.h> #include <unistd.h> int main(void) while (1) { return 0; |
编译运行后没有"hello-out"字符串的输出:
标准流输入输出中,stdout是采用缓冲区的方式来存储打印内容,所以需要使用'\n'或者fflush(stdout)来刷新流输出缓冲区。
puzzle 3:
#include <stdio.h> int main(void) |
编译运行后输出为4321:
man printf之后可以记住,printf的返回值是打印的字符数(不包括'\0')。
puzzle 4:
#include <stdio.h> void foobar2() int main(void) |
能够正常编译运行,然而当foobar1()写为foobar1(33, ch)则无法编译通过:
c语言中,形参为void和形参为空是两个概念。形参为空仅表示形参的个数和类型不确定,这容易带来安全隐患,例如堆栈溢出。指定为void类型后,编译器会进行形参检查。
puzzle 5:
#include <stdio.h> int main(void) |
输出为"b is 0":
switch语句紧跟着执行case语句或者default语句,局部变量b的初始化被略过,在这里b的数值是不确定的,其它编译环境可能输出某个魔术数字。
puzzle 6:
#include <stdio.h> int main() { int n; printf("Enter a number:\n"); scanf("%d\n", &n); printf("You entered %d \n", n); return 0; } |
编译能够运行,但与预期不符合:
原因在于scanf中使用了'\n'字符,scanf是从输入流缓冲区里面读取数值的。'\n'在scanf格式串中不表示等待换行符,而是读取并放弃连续的空白字符,因此"%d\n"中的\n会让scanf读到非空字符为止,我们只需要使用"%d"格式串即可。
puzzle 7:
#include <stdio.h> #define SIZEOF(arr) (sizeof(arr)/sizeof(arr[0])) #define PrintInt(expr) printf("%s:%d\n",#expr,(expr)) |
编译运行后输出数字为1,8,64,1000:
注意在C语言中数字常量前面如果有0的话,编译器会认为这是一个八进制数字。
puzzle 8:
#include <stdio.h> #define ROUNDUP(x,n) ((x+n-1)&(~(n-1))) int main() return 0; |
ROUNDUP宏的用途是什么:
这里有一些数学推导,但本身这个宏的用途为将x按照n进行对齐,返回大于x的一个数字,该数字是n的整数倍且离x最近。使用该宏时对x和n有些限制,n需要为2的幂(例如2,4,8,16等),x为整数等。可以通过编写程序来验证我们的分析。
总结(1):
1 由于C语言的本身产生过程,对于宏的使用仍然是大量嵌入式研发中提高可读性和效率的一个方法,对这方面需要多多思考和问个为什么;
2 C语言本身很灵活,面面俱到是很困难的,对于拿不准的地方,多动手实践可以得出结论,关键在于总结。