您的位置:首页 > 产品设计 > UI/UE

12 Interesting C Interview Questions and Answers

2012-09-02 10:11 363 查看
英文原文来自: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的,因而能正确破译这个密码保护程序。

举个例子:

$ ./psswd aaaaaaaaaaaaa

Password cracked


因此你能看到尽管密码输入错误,但是仍能打破密码安全机制通缓冲区溢出。
为了避免这类问题,strncpy()函数推荐使用。

3.main()函数的返回值

问题:下面这段代码能编译通过么?如果能,这段代码有其他的问题么?
#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;
}


答案:这段代码没有编译问题但是对于main函数的返回值问题会有警告(对于大多数编译器来说)。main函数的返回值应该是int而不是void。这是因为int型返回值要求程序返回一个状态值。这在程序作为脚本的一部分运行的时候显得特别重要,因为它要反映程序运行的正确与否。

4.内存泄漏

问题:下面这段代码将导致内存泄漏么?

#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;
}


答案:虽然给‘ptr’分配的内存并没有被释放,但是这仍然不会导致内存泄漏,因为随着进程终止程序也就退出了。由于程序终止,程序分配的内存会被自动释放。但是如果上述代码处在一个while循环里边,那么将会导致严重的内存泄漏。

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;
}

答案:这段程序的问题在于它在while循环中改变了存放在'ptr'中的地址值(通过'ptr'自增)。当'zebra'作为输入值的时候,while内部连一次都没有执行过就被终止了。所以free时,ptr的值和malloc后,ptr的值是相同的。但是当输入'freeze'时,ptr的值在while循环中会发生变化,这样,在free的时候,就会出现段错误。

6.atexit 和 _exit



问题:在下面这段代码中,atexit()函数不会被调用,这是为什么?
#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);
}


答案:这是因为_exit函数的使用。如果我们需要调用atexit的话,应该使用exit() 或者 'return'而不是_exit().

7.void* 和 C structures

问题:你能设计一个能接受任何类型的参数并且返回整形的函数么?那如果是有不同数量的参数呢?

答案:能接受任何类型的参数的函数应该是这样:
int func(void *ptr)

对于第二个问题,我们可以构造一个结构体。

8.*和++ 操作符

问题:下面这段程序的输入结果是什么,为何?

#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;
}


答案:这是因为,'*ptr'='T'这句代码想要改变只读存储区中的内容。这种操作是无效的,并且会出现段错误。

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
这是因为参数的处理是从右向左的,但是打印却是从左向右。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: