英文原文来自:http://www.thegeekstuff.com/2012/08/c-interview-questions/
本篇只是简要摘录和翻译:
----------------------------------------------------------------分割线-------------------------------------------------------------------------------
在这片文章中,我们将讨论一些有趣的C语言问题,以帮助同学们复习他们的C语言技能、帮助他们准备C语言面试。
1.gets()函数
问题:在下面的代码中,有一个潜在的问题,你能看出来么?
#include<stdio.h>
int main(void)
{
char buff[10];
memset(buff,0,sizeof(buff));
gets(buff);
printf("\n The buffer entered is [%s]\n",buff);
return 0;
}
答案:上面的代码的潜在的问题在于gets()函数的使用。此函数从stdin中接受字符串,但并没有检测buffer拷贝值的能力。这可能导致buffer溢出。在这些情况下,使用fgets()要好一些,推荐使用。
2.strcpy()函数
问题:下面是一段密码检测程序,你能在不知道密码的情况下破译它么?
#include<stdio.h>
int main(int argc, char *argv[])
{
int flag = 0;
char passwd[10];
memset(passwd,0,sizeof(passwd));
strcpy(passwd, argv[1]);
if(0 == strcmp("LinuxGeek", passwd))
{
flag = 1;
}
if(flag)
{
printf("\n Password cracked \n");
}
else
{
printf("\n Incorrect passwd \n");
}
return 0;
}
答案:能够破译。我们可以依靠strcpy()函数的缺陷来解决这个问题。这个程序将用户输入的内容拷贝到passwd中而没有检查输入密码的长度是否超过了passwd的承受范围。因此,如果用户输入了长度超过passwd承受范围的随机密码,就有可能造成buffer溢出,并且重写包含有flag的值的那块内存。最后,即使检测到密码输入错误,但是这时flag的值是非0的,因而能正确破译这个密码保护程序。
举个例子:
#include<stdio.h>
int main(int argc, char *argv[])
{
int flag = 0;
char passwd[10];
memset(passwd,0,sizeof(passwd));
strcpy(passwd, argv[1]);
if(0 == strcmp("LinuxGeek", passwd))
{
flag = 1;
}
if(flag)
{
printf("\n Password cracked \n");
}
else
{
printf("\n Incorrect passwd \n");
}
return 0;
}
$ ./psswd aaaaaaaaaaaaa
Password cracked
因此你能看到尽管密码输入错误,但是仍能打破密码安全机制通缓冲区溢出。
#include<stdio.h>
void main(void)
{
char *ptr = (char*)malloc(10);
if(NULL == ptr)
{
printf("\n Malloc failed \n");
return;
}
else
{
// Do some processing
free(ptr);
}
return;
}
#include<stdio.h>
void main(void)
{
char *ptr = (char*)malloc(10);
if(NULL == ptr)
{
printf("\n Malloc failed \n");
return;
}
else
{
// Do some processing
}
return;
}
5.free()函数
问题:当用户输入"freeze"时,下面的程序会出现段错误。但是输入"zebra"时,程序正常,为何?
#include<stdio.h>
int main(int argc, char *argv[])
{
char *ptr = (char*)malloc(10);
if(NULL == ptr)
{
printf("\n Malloc failed \n");
return -1;
}
else if(argc == 1)
{
printf("\n Usage \n");
}
else
{
memset(ptr, 0, 10);
strncpy(ptr, argv[1], 9);
while(*ptr != 'z')
{
if(*ptr == '')
break;
else
ptr++;
}
if(*ptr == 'z')
{
printf("\n String contains 'z'\n");
// Do some more processing
}
free(ptr);
}
return 0;
}
#include<stdio.h>
int main(int argc, char *argv[])
{
char *ptr = (char*)malloc(10);
if(NULL == ptr)
{
printf("\n Malloc failed \n");
return -1;
}
else if(argc == 1)
{
printf("\n Usage \n");
}
else
{
memset(ptr, 0, 10);
strncpy(ptr, argv[1], 9);
while(*ptr != 'z')
{
if(*ptr == '')
break;
else
ptr++;
}
if(*ptr == 'z')
{
printf("\n String contains 'z'\n");
// Do some more processing
}
free(ptr);
}
return 0;
}
答案:这段程序的问题在于它在while循环中改变了存放在'ptr'中的地址值(通过'ptr'自增)。当'zebra'作为输入值的时候,while内部连一次都没有执行过就被终止了。所以free时,ptr的值和malloc后,ptr的值是相同的。但是当输入'freeze'时,ptr的值在while循环中会发生变化,这样,在free的时候,就会出现段错误。
#include<stdio.h>
void func(void)
{
printf("\n Cleanup function called \n");
return;
}
int main(void)
{
int i = 0;
atexit(func);
for(;i<0xffffff;i++);
_exit(0);
}
int func(void *ptr)
#include<stdio.h>
int main(void)
{
char *ptr = "Linux";
printf("\n [%c] \n",*ptr++);
printf("\n [%c] \n",*ptr);
return 0;
}
答案:输入结果是:
[L]
[i]
由于‘++’和‘*’的优先级相同,所以‘*ptr++’按照从右向左的顺序执行。按照这个逻辑,ptr++先执行,然后才是*ptr。这两个操作符都将是运算结果为'L'。但由于后置算子'++',所以下一行的输出是'i'。
9.操作只读存储区
问题:下面这段代码会导致段错误,你能说出为什么么?
#include<stdio.h>
int main(void)
{
char *ptr = "Linux";
*ptr = 'T';
printf("\n [%s] \n", ptr);
return 0;
}
10.你能写一个程序改变,在运行时改变它自己的程序名么?
答案:
#include<stdio.h>
int main(int argc, char *argv[])
{
int i = 0;
char buff[100];
memset(buff,0,sizeof(buff));
strncpy(buff, argv[0], sizeof(buff));
memset(argv[0],0,strlen(buff));
strncpy(argv[0], "NewName", 7);
// Simulate a wait. Check the process
// name at this point.
for(;i<0xffffffff;i++);
return 0;
}
11.返回局部变量的地址:
问题:下面这段代码有问题么?如果有,该如何调整?
#include<stdio.h>
int* inc(int val)
{
int a = val;
a++;
return &a;
}
int main(void)
{
int a = 10;
int *val = inc(a);
printf("\n Incremented value is equal to [%d] \n", *val);
return 0;
}
答案:虽然这段程序有时能正常运行,但是在inc()函数中有严重的缺陷。这段程序返回局部变量值的地址。因为局部变量的生存时间在inc()中。所以,一旦inc()函数销毁,使用局部变量的地址会造成不可预知的结果。这种情况能够被避免,通过将变量a的地址传入inc()函数中,在inc()中,在保持地址不变的情况下,对值进行改变。
12. 处理printf()的参数:
#include<stdio.h>
int main(void)
{
int a = 10, b = 20, c = 30;
printf("\n %d..%d..%d \n", a+b+c, (b = b*2), (c = c*2));
return 0;
}
答案:
110..40..60
这是因为参数的处理是从右向左的,但是打印却是从左向右。