接上一篇:《C 语言中的一些谜题(一)》
1. puzzle 12
下面两个文件中的简单程序,当两个文件同时参与编译能通过吗?结果又是什么?
file1.c
int arr[80];
file2.c
#include <stdio.h>
extern int *arr;
int main()
{
arr[1] = 100;
return 0;
}
这两个文件编译时可以通过的,但是运行时会出错。原因是在file2.c 中声明 extern int *arr 一个数组并不能得到实际的期望值,因为它们的类型并不匹配,所以导致指针实际并没有指向那个数组。修改:extern int arr[];
2. puzzle 13
#include <stdio.h>
#define SIZE 10
void size(int arr[SIZE])
{
printf("size of array is:%d\n",sizeof(arr));
}
int main()
{
int arr[SIZE];
size(arr);
return 0;
}
提问:上面这段程序将输出什么内容?
回答:4 或 8
对于 size() 函数来说,参数为一个数组,而这个数组的地址是根据实参来的,形参中指定的数目并没有实际意义,所以在编译的时候会提示 warning:
test.cpp: In function ‘void size(int*)’:
test.cpp:7:46: warning: ‘sizeof’ on array function parameter ‘arr’ will return size of ‘int*’ [-Wsizeof-array-argument]
printf("size of array is:%d\n",sizeof(arr));
在size() 函数中arr 只会被看成实参的地址进行运行,sizeof 也会将 arr 看成 int*。所以,最终输出为 32系统中的 4个字节和64位系统中的 8个字节。
3. puzzle 14
#include <stdio.h>
int main()
{
char str[80];
printf("Enter the string:");
scanf("%s",str);
printf("You entered:%s\n",str);
return 0;
}
提问:上面这段代码会有什么样潜在的危险?
回答:当str 输入超过 80 字符时会出现越界问题。
4. puzzle 15
#include <stdio.h>
int main()
{
int i;
i = 10;
printf("i: %d\n",i);
printf("sizeof(i++) is: %d\n", sizeof(i++));
printf("i : %d\n",i);
return 0;
}
提问:这段代码将输出什么内容?
输出结果:
i: 10
sizeof(i++) is: 4
i : 10
如果认为输出是:10,4,11,那就错了,最容易错在第三个。因为 sizeof() 不是一个函数,而是一个操作符,其求 i++ 的类型的size,在编译时就完成的事情,编译的时候会优化掉 i++,因为 i 的类型就是 int,无需进行运算。
5. puzzle 16
#include <stdio.h>
void foo(const char **p) { }
int main(int argc, char **argv)
{
foo(argv);
return 0;
}
这段代码会有什么样的问题呢?
在编译的时候会进行提示,c 和 c++ 中编译提示不同,在C 中会warning 提示,而在 C++ 中会进行报错。
test.cpp: In function ‘int main(int, char**)’:
test.cpp:5:19: error: invalid conversion from ‘char**’ to ‘const char**’ [-fpermissive]
foo(argv);
^
test.cpp:2:8: note: initializing argument 1 of ‘void foo(const char**)’
void foo(const char **p) { }
^~~
Makefile:6: recipe for target 'test.o' failed
make: *** [test.o] Error 1
main 中的参数为 char** 表示该变量是指向字符串的指针变量,foo() 中的参数是指向 const char* 的指针,与原本的实际参数不符。 对于 const char** 和 char**来说,二者都是没有限定符的指针类型,但是它们指向的类型不一样,后者指向 char*,而前者指向const char*,因此它们不相容,所以 char** 类型的操作数不能赋值给 const char** 类型的操作数。
6. puzzle 17
#include <stdio.h>
int main()
{
int i;
i = 1,2,3;
printf("i: %d\n",i);
return 0;
}
上面这段代码将输出什么内容呢?i 的值最终为多少呢?
这段程序应该很简单,最终i 的值为1,因为赋值运算符的优先级比逗号运算符高,或者说逗号运算符是运算优先级里面最低的。
7. puzzle 18
#include <stdio.h>
#include <stdlib.h>
#define SIZEOF(arr) (sizeof(arr)/sizeof(arr[0]))
#define PrintInt(expr) printf("%s:%d\n",#expr,(expr))
int main()
{
/* The powers of 10 */
int pot[] = {
0001,
0010,
0100,
1000
};
int i;
for(i=0;i<SIZEOF(pot);i++)
PrintInt(pot[i]);
return 0;
}
提问:上面这段代码的输出结果是什么?
输出结果:
pot[i]:1
pot[i]:8
pot[i]:64
pot[i]:1000
这里考察的是,在 C/C++ 中,以0开头的数字,都是 8 进制数。
8. puzzle 19
#include <stdio.h>
#include <stdlib.h>
#define PrintInt(expr) printf("%s : %d\n",#expr,(expr))
int main()
{
int y = 100;
int *p;
p = (int*)malloc(sizeof(int));
*p = 10;
y = y/*p; /*dividing y by *p */;
PrintInt(y);
return 0;
}
上面这段代码会输出什么内容呢?
输出值为100,理论上 y 为100, *p 为10,y 的值应该为 10.
注意这里 y = y/*p; 因为没有空格,编译时会将 /* 看成是注释符。所以在编写代码的时候还是规范,可以修改为: y = y / *p; 或者是 y = y / (*p);
9. puzzle 20
#include <stdio.h>
int main()
{
int i = 6;
if( ((++i < 7) && ( i++/6)) || (++i <= 9))
;
printf("%d\n",i);
return 0;
}
这段代码 i 的值最终为多少呢?
答案是8.
首先需要明白 || 当前面的条件为 true,那么后面的条件是不执行的。另外就是 && 的逻辑运算优先级是比算术运算符的优先级低的,所以在进行 &&运算之前,会先进行算术运算,最终经历 ++i 和 i++ 两次算术运算。
若条件稍微修改下更明显,把 ++i < 7 中的自加去掉,改成:
if((i < 7 && ++i/6) || (++i <= 9))
最终i 的结果为 7.
10. puzzle 21
#include <stdio.h>
int main()
{
int n;
printf("Enter a number:\n");
scanf("%d", n);
printf("You entered %d \n",n);
return 0;
}
这段代码有没有问题?有什么样的问题?
代码比较简单,主要问题出现在scanf() 中,正常情况下要想输入 n 的值,应该是使用 &n,这里使用 n 编译也是允许的,但运行的时候会出现段错误。scanf() 将 n 所在地址中的随机值看成了地址,并试图将该地址中写入一个 stdin 中的整数。而这个随机值所指向的地址,有可能是核心地址或是重要地址,无法进行修改的。
11. puzzle 22
#include <stdio.h>
#define PrintInt(expr) printf("%s : %d\n",#expr,(expr))
int FiveTimes(int a)
{
int t;
t = a<<2 + a;
return t;
}
int main()
{
int a = 1, b = 2,c = 3;
PrintInt(FiveTimes(a));
PrintInt(FiveTimes(b));
PrintInt(FiveTimes(c));
return 0;
}
下面程序试图使用 “位操作” 来完成 “乘5” 的操作,但是程序中有个bug,知道是什么吗?
本段程序问题主要出在 FiveTimes() 中表达式 “t = a << 2 + a”,位移运算符的优先级没有 + 优先级高,所以,该表达式就成了 “t = a << (2 + a)”,所以正确做法应该是将位移运算用括号括起来。
12. puzzle 23
#include <stdlib.h>
#include <stdio.h>
#define SIZE 15
int main()
{
int *a, i;
a = (int*)malloc(SIZE*sizeof(int));
for (i=0; i<SIZE; i++)
*(a + i) = i * i;
for (i=0; i<SIZE; i++)
printf("%d\n", *a++);
free(a);
return 0;
}
这段代码输出什么内容?有个bug 知道是什么吗?
输出内容应该没有问题,申请了一个堆上连续的空间,形成一个数组,第一个 for 将每个数组的元素都赋值为 ,第二个for 循环将数组中的元素都打印出来。但是在free() 的时候会出现大问题,读者看出来了吗?
13. puzzle 24
#include <stdio.h>
int main()
{
char dummy[80];
printf("Enter a string:\n");
scanf("%[^d]",dummy);
printf("%s\n",dummy);
return 0;
}
上面这段代码将输出什么内容(假如输入内容为:Hello World)?
输出结果为:Hello Worl
scanf() 中的 “%[^d]” 表示输入截止到字符 d,当然这里的d 也可以改成其他的字符。
接上一篇:《C 语言中的一些谜题(一)》