12 Interesting C Interview Questions and Answers

英文原文来自: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
这是因为参数的处理是从右向左的,但是打印却是从左向右。










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值