1. 程式人生 > >12個有趣的C語言面試題(中英文對照)

12個有趣的C語言面試題(中英文對照)

In this article, we will discuss some interesting problems on C language that can help students to brush up their C programming skills and help them prepare their C fundamentals for interviews.

1. gets() function

Question: There is a hidden problem with the following code. Can you detect it?

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

Answer: The hidden problem with the code above is the use of the function gets(). This function accepts a string from stdin without checking the capacity of buffer in which it copies the value. This may well result in buffer overflow. The standard function fgets() is advisable to use in these cases.

2. strcpy() function

Question: Following is the code for very basic password protection. Can you break it without knowing the password?

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

Answer: Yes. The authentication logic in above password protector code can be compromised by exploiting the loophole of strcpy() function. This function copies the password supplied by user to the ‘passwd’ buffer without checking whether the length of password supplied can be accommodated by the ‘passwd’ buffer or not. So if a user supplies a random password of such a length that causes buffer overflow and overwrites the memory location containing the default value ’0′ of the ‘flag’ variable then even if the password matching condition fails, the check of flag being non-zero becomes true and hence the password protection is breached.

For example :

$ ./psswd aaaaaaaaaaaaa

 Password cracked

So you can see that though the password supplied in the above example is not correct but still it breached the password security through buffer overflow.

To avoid these kind of problems the function strncpy() should be used.

Note from author : These days the compilers internally detect the possibility of stack smashing and so they store variables on stack in such a way that stack smashing becomes very difficult. In my case also, the gcc does this by default so I had to use the the compile option ‘-fno-stack-protector’ to reproduce the above scenario.

3. Return type of main()

Question: Will the following code compile? If yes, then is there any other problem with this code?

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

Answer: The code will compile error free but with a warning (by most compilers) regarding the return type of main()function. Return type of main() should be ‘int’ rather than ‘void’. This is because the ‘int’ return type lets the program to return a status value. This becomes important especially when the program is being run as a part of a script which relies on the success of the program execution.

4. Memory Leak

Question: Will the following code result in memory leak?

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

Answer: Well, Though the above code is not freeing up the memory allocated to ‘ptr’ but still this would not cause a memory leak as after the processing is done the program exits. Since the program terminates so all the memory allocated by the program is automatically freed as part of cleanup. But if the above code was all inside a while loop then this would have caused serious memory leaks.

Note : If you want to know more on memory leaks and the tool that can detect memory leaks, read our article onValgrind.

5. The free() function

Question: The following program seg-faults (crashes) when user supplies input as ‘freeze’ while it works fine with input ‘zebra’. Why?

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

Answer: The problem here is that the code changes the address in ‘ptr’ (by incrementing the ‘ptr’) inside the while loop. Now when ‘zebra’ is supplied as input, the while loop terminates before executing even once and so the argument passed to free() is the same address as given by malloc(). But in case of ‘freeze’ the address held by ptr is updated inside the while loop and hence incorrect address is passed to free() which causes the seg-fault or crash.

6. atexit with _exit

Question: In the code below, the atexit() function is not being called. Can you tell why?

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

Answer: This behavior is due to the use of function _exit(). This function does not call the clean-up functions like atexit() etc. If atexit() is required to be called then exit() or ‘return’ should be used.

7. void* and C structures

Question: Can you design a function that can accept any type of argument and returns an integer? Also, is there a way in which more than one arguments can be passed to it?

Answer: A function that can accept any type of argument looks like :

 int func(void *ptr)

if more than one argument needs to be passed to this function then this function could be called with a structure object where-in the structure members can be populated with the arguments that need to be passed.

8. * and ++ operators

Question: What would be the output of the following code and why?

#include<stdio.h>

int main(void)
{
    char *ptr = "Linux";
    printf("\n [%c] \n",*ptr++);
    printf("\n [%c] \n",*ptr);

    return 0;
}

Answer: The output of the above would be :

[L] 

[i]

Since the priority of both ‘++’ and ‘*’ are same so processing of ‘*ptr++’ takes place from right to left. Going by this logic, ptr++ is evaluated first and then *ptr. So both these operations result in ‘L’. Now since a post fix ‘++’ was applied on ptr so the next printf() would print ‘i’.

9. Making changes in Code(or read-only) segment

Question: The following code seg-faults (crashes). Can you tell the reason why?

#include<stdio.h>

int main(void)
{
    char *ptr = "Linux";
    *ptr = 'T';

    printf("\n [%s] \n", ptr);

    return 0;
}

Answer: This is because, through *ptr = ‘T’, the code is trying to change the first byte of the string ‘Linux’ kept in the code (or the read-only) segment in the memory. This operation is invalid and hence causes a seg-fault or a crash.

10. Process that changes its own name

Question: Can you write a program that changes its own name when run?

Answer: Following piece of code tries to do the required :

#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. Returning address of local variable

Question: Is there any problem with the following code?If yes, then how it can be rectified?

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

Answer: Though the above program may run perfectly fine at times but there is a serious loophole in the function ‘inc()’. This function returns the address of a local variable. Since the life time of this local variable is that of the function ‘inc()’ so after inc() is done with its processing, using the address of its local variable can cause undesired results. This can be avoided by passing the address of variable ‘a’ from main() and then inside changes can be made to the value kept at this address.

12. Processing printf() arguments

Question: What would be the output of the following code?

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

Answer: The output of the above code would be :

110..40..60

This is because the arguments to the function are processed from right to left but are printed from left to right.

1.gets()函式

問:請找出下面程式碼裡的問題:

  1. #include<stdio.h> 
  2. int main(void
  3.     char buff[10]; 
  4.     memset(buff,0,sizeof(buff)); 
  5.     gets(buff); 
  6.     printf("\n The buffer entered is [%s]\n",buff); 
  7.     return 0; 

答:上面程式碼裡的問題在於函式gets()的使用,這個函式從stdin接收一個字串而不檢查它所複製的快取的容積,這可能會導致快取溢位。這裡推薦使用標準函式fgets()代替。

2.strcpy()函式

問:下面是一個簡單的密碼保護功能,你能在不知道密碼的情況下將其破解嗎?

  1. #include<stdio.h> 
  2. int main(int argc, char *argv[]) 
  3.     int flag = 0; 
  4.     char passwd[10]; 
  5.     memset(passwd,0,sizeof(passwd)); 
  6.     strcpy(passwd, argv[1]); 
  7.     if(0 == strcmp("LinuxGeek", passwd)) 
  8.     { 
  9.         flag = 1; 
  10.     } 
  11.     if(flag) 
  12.     { 
  13.         printf("\n Password cracked \n"); 
  14.     } 
  15.     else 
  16.     { 
  17.         printf("\n Incorrect passwd \n"); 
  18.     } 
  19.     return 0; 

答:破解上述加密的關鍵在於利用攻破strcpy()函式的漏洞。所以使用者在向“passwd”快取輸入隨機密碼的時候並沒有提前檢查“passwd”的容量是否足夠。所以,如果使用者輸入一個足夠造成快取溢位並且重寫“flag”變數預設值所存在位置的記憶體的長“密碼”,即使這個密碼無法通過驗證,flag驗證位也變成了非零,也就可以獲得被保護的資料了。例如:

  1. $ ./psswd aaaaaaaaaaaaa 
  2. Password cracked 

雖然上面的密碼並不正確,但我們仍然可以通過快取溢位繞開密碼安全保護。

要避免這樣的問題,建議使用 strncpy()函式。

作者注:最近的編譯器會在內部檢測棧溢位的可能,所以這樣往棧裡儲存變數很難出現棧溢位。在我的gcc裡預設就是這樣,所以我不得不使用編譯命令‘-fno-stack-protector’來實現上述方案。

3.main()的返回型別

問:下面的程式碼能 編譯通過嗎?如果能,它有什麼潛在的問題嗎?

  1. #include<stdio.h> 
  2. void main(void
  3.     char *ptr = (char*)malloc(10); 
  4.     if(NULL == ptr) 
  5.     { 
  6.         printf("\n Malloc failed \n"); 
  7.         return
  8.     } 
  9.     else 
  10.     { 
  11.         // Do some processing 
  12.         free(ptr); 
  13.     } 
  14.     return

答:因為main()方法的返回型別,這段程式碼的錯誤在大多數編譯器裡會被當作警告。main()的返回型別應該是“int”而不是“void”。因為“int”返回型別會讓程式返回狀態值。這點非常重要,特別當程式是作為依賴於程式成功執行的指令碼的一部分執行時。

4.記憶體洩露

問:下面的程式碼會導致記憶體洩漏嗎?

  1. #include<stdio.h> 
  2. void main(void
  3.     char *ptr = (char*)malloc(10); 
  4.     if(NULL == ptr) 
  5.     { 
  6.         printf("\n Malloc failed \n"); 
  7.         return
  8.     } 
  9.     else 
  10.     { 
  11.         // Do some processing 
  12.     } 
  13.     return

答:儘管上面的程式碼並沒有釋放分配給“ptr”的記憶體,但並不會在程式退出後導致記憶體洩漏。在程式結束後,所有這個程式分配的記憶體都會自動被處理掉。但如果上面的程式碼處於一個“while迴圈”中,那將會導致嚴重的記憶體洩漏問題!

提示:如果你想知道更多關於記憶體洩漏的知識和記憶體洩漏檢測工具,可以來看看我們在Valgrind上的文章。

5.free()函式

問:下面的程式會在使用者輸入'freeze'的時候出問題,而'zebra'則不會,為什麼?

  1. #include<stdio.h> 
  2. int main(int argc, char *argv[]) 
  3.     char *ptr = (char*)malloc(10); 
  4.     if(NULL == ptr) 
  5.     { 
  6.         printf("\n Malloc failed \n"); 
  7.         return -1; 
  8.     } 
  9.     else if(argc == 1) 
  10.     { 
  11.         printf("\n Usage  \n"); 
  12.     } 
  13.     else 
  14.     { 
  15.         memset(ptr, 0, 10); 
  16.         strncpy(ptr, argv[1], 9); 
  17.         while(*ptr != 'z'
  18.         { 
  19.             if(*ptr == ''
  20.                 break
  21.             else 
  22.                 ptr++; 
  23.         } 
  24.         if(*ptr == 'z'
  25.         { 
  26.             printf("\n String contains 'z'\n"); 
  27.             // Do some more processing 
  28.         } 
  29.        free(ptr); 
  30.     } 
  31.     return 0; 

答:這裡的問題在於,程式碼會(通過增加“ptr”)修改while迴圈裡“ptr”儲存的地址。當輸入“zebra”時,while迴圈會在執行前被終止,因此傳給free()的變數就是傳給malloc()的地址。但在“freeze”時,“ptr”儲存的地址會在while迴圈裡被修改,因此導致傳給free()的地址出錯,也就導致了seg-fault或者崩潰。

6.使用_exit退出

問:在下面的程式碼中,atexit()並沒有被呼叫,為什麼?

  1. #include<stdio.h> 
  2. void func(void
  3.     printf("\n Cleanup function called \n"); 
  4.     return
  5. int main(void
  6.     int i = 0; 
  7.     atexit(func); 
  8.     for(;i<0xffffff;i++); 
  9.     _exit(0); 

這是因為_exit()函式的使用,該函式並沒有呼叫atexit()等函式清理。如果使用atexit()就應當使用exit()或者“return”與之相配合。

7.void*和C結構體

問:你能設計一個能接受任何型別的引數並返回interger(整數)結果的函式嗎?

答:如下:

  1. int func(void *ptr) 

如果這個函式的引數超過一個,那麼這個函式應該由一個結構體來呼叫,這個結構體可以由需要傳遞引數來填充。

8.*和++操作

問:下面的操作會輸出什麼?為什麼?

  1. #include<stdio.h> 
  2. int main(void
  3.     char *ptr = "Linux"
  4.     printf("\n [%c] \n",*ptr++); 
  5.     printf("\n [%c] \n",*ptr); 
  6.     return 0; 

答:輸出結果應該是這樣:

  1. [L]  
  2. [i] 

因為“++”和“*”的優先權一樣,所以“*ptr++”相當於“*(ptr++)”。即應該先執行ptr++,然後才是*ptr,所以操作結果是“L”。第二個結果是“i”。

9.問:修改程式碼片段(或者只讀程式碼)

問:下面的程式碼段有錯,你能指出來嗎?

  1. #include<stdio.h> 
  2. int main(void
  3.     char *ptr = "Linux"
  4.     *ptr = 'T'
  5.     printf("\n [%s] \n", ptr); 
  6.     return 0; 

答:這是因為,通過*ptr = ‘T’,會改變記憶體中程式碼段(只讀程式碼)“Linux”的第一個字母。這個操作是無效的,因此會造成seg-fault或者崩潰。

10.會改變自己名字的程序

問:你能寫出一個在執行時改變自己程序名的程式嗎?

答:參見下面這段程式碼:

  1. #include<stdio.h> 
  2. int main(int argc, char *argv[]) 
  3.     int i = 0; 
  4.     char buff[100]; 
  5.     memset(buff,0,sizeof(buff)); 
  6.     strncpy(buff, argv[0], sizeof(buff)); 
  7.     memset(argv[0],0,strlen(buff)); 
  8.     strncpy(argv[0], "NewName", 7); 
  9.     // Simulate a wait. Check the process 
  10.     // name at this point. 
  11.     for(;i<0xffffffff;i++); 
  12.     return 0; 

11.返回本地變數的地址

問:下面程式碼有問題嗎?如果有,該怎麼修改?

  1. #include<stdio.h> 
  2. int* inc(int val) 
  3.   int a = val; 
  4.   a++; 
  5.   return &a; 
  6. int main(void
  7.     int a = 10; 
  8.     int *val = inc(a); 
  9.     printf("\n Incremented value is equal to [%d] \n", *val); 
  10.     return 0; 

答:儘管上面的程式有時候能夠正常執行,但是在“inc()”中存在嚴重的漏洞。這個函式返回本地變數的地址。因為本地變數的生命週期就是“inc()”的生命週期,所以在inc結束後,使用本地變數會發生不好的結果。這可以通過將main()中變數“a”的地址來避免,這樣以後還可以修改這個地址儲存的值。

12.處理printf()的引數

問:下面程式碼會輸出什麼?

  1. #include<stdio.h> 
  2. int main(void
  3.     int a = 10, b = 20, c = 30; 
  4.     printf("\n %d..%d..%d \n", a+b+c, (b = b*2), (c = c*2)); 
  5.     return 0; 

答:輸出結果是:

  1. 110..40..60 

這是因為C語言裡函式的引數預設是從右往左處理的,輸出時是從左往右。


相關推薦

12有趣C語言試題(中英文對照)

In this article, we will discuss some interesting problems on C language that can help students to brush up their C programming skills

12有趣C語言試題及答案

轉載自:http://blog.csdn.net/sjin_1314/article/details/48418483 12個,涉及指標、程序、運算、結構體、函式、記憶體,看看你能做出幾個! 1.gets()函式 問:請找出下面程式碼裡的問題: #include&l

12有趣c語言試題

1.gets()函式 問:請找出下面程式碼裡的問題: #include<stdio.h>int main(void) { char buff[10]; memset(buff,0,sizeof(buff)); gets(buff);

12C語言試題

1.gets()函式 問:請找出下面程式碼裡的問題: #include<stdio.h>int main(void) {     char buff[10];     memset(buff,0,sizeof(buff));     gets(buff);     printf("\n Th

C語言試題:查詢字串中第一不重複的字元

char findChar(char *s) { char *p,*q; p=q=s; while(*s != '\0') { while(*p++ != '\0')

閑來無事看兩好玩的C語言試題

當前 下午 沒有 fontsize 機器 比較 linu 需要 -s 昨天最後一天工作日,讓我碰到了兩個有意思的C語言編程問題,周末前權當輕松一下,挺有意思就過去了,因為今晚雨夜通宵,就把它們記錄了下來。 事件1:關於結構體對齊和packed屬性 下午跟同事下樓抽煙,聊

C語言試題分類->排序算法

OS sort 試題 時間復雜度 ups popup popu 使用 body 1.選擇排序。   每次將最小的數,與剩余數做比較。找到更小的,做交換。   時間復雜度:O(n2)   空間復雜度:O(1)   優缺點:耗時但內存空間使用小。 void selectSor

C語言試題分類->回調

指向 name 傳輸 join 接口 但是 PE AS 一個數 本文主要講解如果實現回調,特別是在封裝接口的時候,回調顯得特別重要,我們首先假設有兩個程序員在寫代碼,A程序員寫底層驅動接口,B程序員寫上層應用程序,然而此時底層驅動接口A有一個數據d需要傳輸給B,此時有兩種方

智郵普創c語言試題 ---- 字母概率

arr apple void != stdio.h app words 遊戲 包含 題目描述 小明最近對概率問題很感興趣。一天,小明和小紅一起玩一個概率遊戲,首先小明給出一個字母和一個單詞,然後由小紅計算這個字母在這個單詞中出現的概率。字母不區分大小寫。 例如,給定的字母是

C語言試題---指標篇(三)

版本宣告:本文轉載於公眾號TeachPlus C語言面試題---指標篇(三) 了解了記憶體空間,接下來我們就一起看一下指標自身用法的一些題目,先來看這樣一道題目: 分析下面程式碼:` # include <stdio.h> # include

C語言試題---指標篇(一)

版本宣告:本文轉載於公眾號TeachPlus 指標的使用,一直是c語言面試題中必考的部分, 因為指標本身使用的複雜性與普適性,所以考點非常多,而且也可以與其他知識相互結合, 因此我們將會使用五篇專題的篇幅來介紹指標。分析下面的程式,指出程式中的錯誤:

C語言試題2

1 預處理 問題1:什麼是預編譯?何時需要預編譯? 答: 預編譯又稱預處理,是整個編譯過程最先做的工作,即程式執行前的一些預處理工作。主要處理#開頭的指令。如拷貝#include包含的檔案程式碼、替換#define定義的巨集、條件編譯#if等。. 何時需要預編譯: 1、總是使用不經常改動的

C語言試題---指標篇(二)

# include <stdio.h># include <stdlib.h># include <string.h>void getMemory( char **p , int num){ *p = malloc(num);} int main() {

100道c語言試題

題目來源: 1、中興、華為、慧通、英華達、微軟亞洲技術中心等中 外企業面試題目; 2、C 語言面試寶典(林銳《高質量程式設計第三版》)。 說明: 1、部分C 語言面試題中可能會參雜部分和C++ 相關的知 識,為了保持題目的靈活性故保留,但選題最終還是 會以C

C語言試題大彙總 (影象處理方向)

1、區域性變數能否和全域性變數重名?    答:能,區域性會遮蔽全域性。要用全域性變數,需要使用"::" ;區域性變數可以與全域性變數同名,在函式內引用這個變數時,會用到同名的區域性變數,而不會用到全域性變數。對於有些編譯器而言,在同一個函式內可以定義多個同名的區域性變數,比如在兩個迴圈體內都定義一個同名的

C語言試題

關於“一年有多少秒”的巨集定義 網上關於這個問題的答案都是(365*24*60*60)UL,是錯誤的。 正解:數字和型別是一個整體,()是外人。 #include <stdio.h> #define SECONDS_PER_YEAR (3

9道常見C語言試題

1、區域性變數能否和全域性變數重名?      答:能,區域性會遮蔽全域性。要用全域性變數,需要使用"::" ;區域性變數可以與全域性變數同名,在函式內引用這個變數時,會用到同名的區域性變數,而不會用到全域性變數。對於有些編譯器而言,在同一個函式內可以定義多個同

C語言試題 4 (查詢整數陣列中第二大的數)

題目:寫一個函式找出一個整數陣列中,第二大的數。【Mirosoft】 PS:1、” 66,66,66,66,66 ”,則沒有第二大數。 2、” 99,99,88,86,68,66 ”,則最大數是88。 下面我先給出查詢最大數字的程式: int GetFirstMaxNumbe

c語言試題1

> 前處理器(Preprocessor) 1. 用預處理指令#define 宣告一個常數,用以表明1年中有多少秒(忽略閏年問題)     #define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL 我在這想看到幾件事情: 1).

2016 C語言試題——字串函式的實現

2016 C語言面試題——字串函式 前言:最近準備找工作了,所以需要做一些準備。先從基本的字串函式的實現開始。如果有發現什麼問題或有什麼疑惑,請及時評論。 strcat /************