面白いC言語面接問題(個人整理コレクション)
1.gets()関数
質問:次のコードの質問を見つけてください.
答え:上のコードの問題は関数gets()の使用です.この関数はstdinからコピーしたキャッシュの容積をチェックせずに文字列を受信し、キャッシュオーバーフローを引き起こす可能性があります.ここでは、標準関数fgets()の代わりに使用することを推奨します.
2.strcpy()関数
問:次は簡単なパスワード保護機能ですが、パスワードを知らずに解読できますか?
答え:上記の暗号化を解読する鍵はstrcpy()関数の脆弱性を突破することにある.したがって、ユーザは、「passwd」キャッシュにランダムパスワードを入力する際に、「passwd」の容量が十分であるかどうかを事前にチェックしていない.したがって、ユーザがキャッシュオーバーフローを生じさせ、「flag」変数のデフォルト値が存在する場所を書き換えるのに十分な長い「パスワード」を入力すると、このパスワードが検証に合格できなくても、flag検証ビットはゼロではなく、保護されたデータを得ることができる.例:
上記のパスワードは正しくありませんが、キャッシュオーバーフローによってパスワードを迂回して安全に保護できます.
このような問題を回避するにはstrncpy()関数を使用することをお勧めします.
最近のコンパイラでは、スタックオーバーフローの可能性が内部で検出されるため、スタックに変数を格納するとスタックオーバーフローが発生しにくくなります.私のgccではデフォルトでそうなので、コンパイルコマンド'-fno-stack-protector'を使用して上記のスキームを実現しなければなりません.
3.main()の戻りタイプ
問:次のコードはコンパイルできますか.もしできるなら、何か潜在的な問題がありますか?
答え:main()メソッドの戻りタイプのため、このコードのエラーは多くのコンパイラで警告されます.main()の戻りタイプは「void」ではなく「int」であるべきである.「int」戻りタイプは、プログラムにステータス値を返すからです.この点は、特にプログラムがプログラムに依存して正常に実行されたスクリプトの一部として実行される場合に重要である.
4.メモリ漏洩
問:次のコードはメモリの漏洩を招きますか?
答え:上記のコードは「ptr」に割り当てられたメモリを解放していませんが、プログラムが終了した後にメモリが漏れることはありません.プログラムが終了すると、このプログラムが割り当てたメモリは自動的に処理されます.しかし、上記のコードが「whileサイクル」にある場合、深刻なメモリ漏洩の問題が発生します.
ヒント:メモリ漏洩に関する知識とメモリ漏洩検出ツールをもっと知りたい場合は、Valgrindの記事を見てみましょう.
5.free()関数
問:次のプログラムはユーザーが「freeze」を入力したときに問題が発生しますが、「zebra」はできません.なぜですか.
答え:ここでの問題は、コードが(ptrを増やすことによって)whileサイクルに「ptr」が格納されているアドレスを変更することです.「zebra」を入力すると、whileサイクルは実行前に終了するので、free()に伝達される変数はmalloc()に伝達されるアドレスです.しかし、「freeze」の場合、「ptr」に格納されているアドレスはwhileサイクルで変更されるため、free()に送信されるアドレスエラー、seg-faultまたはクラッシュを引き起こす.
6.使用_exit終了
問:次のコードでは、atexit()は呼び出されていません.なぜですか.
なぜならexit()関数の使用は、atexit()などの関数クリーンアップを呼び出さなかった.atexit()を使用する場合はexit()またはreturnを使用します.
7.void*とC構造体
質問:任意のタイプのパラメータを受け入れてinterger(整数)の結果を返す関数を設計できますか?
答え:以下:
この関数のパラメータが1つを超える場合、この関数は1つの構造体によって呼び出されるべきであり、この構造体は必要に応じてパラメータを伝達して埋め込むことができる.
8.*および++操作
問:次の操作で何が出力されますか.どうして?
答え:出力結果は次のようになります.
「++」は「*」の優先権と同じなので「*ptr++」は「*(ptr+)」に相当します.すなわち、ptr++を先に実行してから*ptrとなるので、操作結果は「L」となる.2つ目の結果は「i」です.
9.質問:コードフラグメントの修正(または読み取り専用コード)
問:次のコードセグメントに間違いがあります.指摘してもらえますか.
答え:これは*ptr=‘T’によって、メモリ内のコードセグメント(読み取り専用コード)「Linux」の最初のアルファベットが変更されるためです.この操作は無効なため、seg-faultまたはクラッシュをもたらします.
10.自分の名前を変えるプロセス
質問:実行時に自分のプロセス名を変更するプログラムを書くことができますか?
A:次のコードを参照してください.
11.ローカル変数のアドレスを返す
問:次のコードに問題がありますか.もしあったら、どうやって修正しますか?
答え:上記のプログラムは正常に動作する場合がありますが、「inc()」には深刻な脆弱性があります.この関数はローカル変数のアドレスを返します.ローカル変数のライフサイクルは「inc()」のライフサイクルであるため、incが終了するとローカル変数を使用すると悪い結果が発生します.これはmain()の変数「a」のアドレスを避けることで、後でこのアドレスに格納されている値を変更することもできます.
12.printf()を扱うパラメータ
問:次のコードは何を出力しますか?
答え:出力結果は:
これは,C言語では関数のパラメータがデフォルトで右から左に処理され,出力時には左から右に処理されるためである.原文:http://www.thegeekstuff.com/2012/08/c-interview-questions/
質問:次のコードの質問を見つけてください.
- #include<stdio.h>
- int main(void)
- {
- char buff[10];
- memset(buff,0,sizeof(buff));
-
- gets(buff);
-
- printf("
The buffer entered is [%s]
",buff);
-
- return 0;
- }
答え:上のコードの問題は関数gets()の使用です.この関数はstdinからコピーしたキャッシュの容積をチェックせずに文字列を受信し、キャッシュオーバーフローを引き起こす可能性があります.ここでは、標準関数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("
Password cracked
");
- }
- else
- {
- printf("
Incorrect passwd
");
-
- }
- return 0;
- }
答え:上記の暗号化を解読する鍵はstrcpy()関数の脆弱性を突破することにある.したがって、ユーザは、「passwd」キャッシュにランダムパスワードを入力する際に、「passwd」の容量が十分であるかどうかを事前にチェックしていない.したがって、ユーザがキャッシュオーバーフローを生じさせ、「flag」変数のデフォルト値が存在する場所を書き換えるのに十分な長い「パスワード」を入力すると、このパスワードが検証に合格できなくても、flag検証ビットはゼロではなく、保護されたデータを得ることができる.例:
- $ ./psswd aaaaaaaaaaaaa
-
- Password cracked
上記のパスワードは正しくありませんが、キャッシュオーバーフローによってパスワードを迂回して安全に保護できます.
このような問題を回避するにはstrncpy()関数を使用することをお勧めします.
最近のコンパイラでは、スタックオーバーフローの可能性が内部で検出されるため、スタックに変数を格納するとスタックオーバーフローが発生しにくくなります.私のgccではデフォルトでそうなので、コンパイルコマンド'-fno-stack-protector'を使用して上記のスキームを実現しなければなりません.
3.main()の戻りタイプ
問:次のコードはコンパイルできますか.もしできるなら、何か潜在的な問題がありますか?
- #include<stdio.h>
-
- void main(void)
- {
- char *ptr = (char*)malloc(10);
-
- if(NULL == ptr)
- {
- printf("
Malloc failed
");
- return;
- }
- else
- {
- // Do some processing
- free(ptr);
- }
-
- return;
- }
答え:main()メソッドの戻りタイプのため、このコードのエラーは多くのコンパイラで警告されます.main()の戻りタイプは「void」ではなく「int」であるべきである.「int」戻りタイプは、プログラムにステータス値を返すからです.この点は、特にプログラムがプログラムに依存して正常に実行されたスクリプトの一部として実行される場合に重要である.
4.メモリ漏洩
問:次のコードはメモリの漏洩を招きますか?
- #include<stdio.h>
-
- void main(void)
- {
- char *ptr = (char*)malloc(10);
-
- if(NULL == ptr)
- {
- printf("
Malloc failed
");
- return;
- }
- else
- {
- // Do some processing
- }
-
- return;
- }
答え:上記のコードは「ptr」に割り当てられたメモリを解放していませんが、プログラムが終了した後にメモリが漏れることはありません.プログラムが終了すると、このプログラムが割り当てたメモリは自動的に処理されます.しかし、上記のコードが「whileサイクル」にある場合、深刻なメモリ漏洩の問題が発生します.
ヒント:メモリ漏洩に関する知識とメモリ漏洩検出ツールをもっと知りたい場合は、Valgrindの記事を見てみましょう.
5.free()関数
問:次のプログラムはユーザーが「freeze」を入力したときに問題が発生しますが、「zebra」はできません.なぜですか.
- #include<stdio.h>
-
- int main(int argc, char *argv[])
- {
- char *ptr = (char*)malloc(10);
-
- if(NULL == ptr)
- {
- printf("
Malloc failed
");
- return -1;
- }
- else if(argc == 1)
- {
- printf("
Usage
");
- }
- else
- {
- memset(ptr, 0, 10);
-
- strncpy(ptr, argv[1], 9);
-
- while(*ptr != 'z')
- {
- if(*ptr == '')
- break;
- else
- ptr++;
- }
-
- if(*ptr == 'z')
- {
- printf("
String contains 'z'
");
- // Do some more processing
- }
-
- free(ptr);
- }
-
- return 0;
- }
答え:ここでの問題は、コードが(ptrを増やすことによって)whileサイクルに「ptr」が格納されているアドレスを変更することです.「zebra」を入力すると、whileサイクルは実行前に終了するので、free()に伝達される変数はmalloc()に伝達されるアドレスです.しかし、「freeze」の場合、「ptr」に格納されているアドレスはwhileサイクルで変更されるため、free()に送信されるアドレスエラー、seg-faultまたはクラッシュを引き起こす.
6.使用_exit終了
問:次のコードでは、atexit()は呼び出されていません.なぜですか.
- #include<stdio.h>
-
- void func(void)
- {
- printf("
Cleanup function called
");
- return;
- }
-
- int main(void)
- {
- int i = 0;
-
- atexit(func);
-
- for(;i<0xffffff;i++);
-
- _exit(0);
- }
なぜならexit()関数の使用は、atexit()などの関数クリーンアップを呼び出さなかった.atexit()を使用する場合はexit()またはreturnを使用します.
7.void*とC構造体
質問:任意のタイプのパラメータを受け入れてinterger(整数)の結果を返す関数を設計できますか?
答え:以下:
- int func(void *ptr)
この関数のパラメータが1つを超える場合、この関数は1つの構造体によって呼び出されるべきであり、この構造体は必要に応じてパラメータを伝達して埋め込むことができる.
8.*および++操作
問:次の操作で何が出力されますか.どうして?
- #include<stdio.h>
-
- int main(void)
- {
- char *ptr = "Linux";
- printf("
[%c]
",*ptr++);
- printf("
[%c]
",*ptr);
-
- return 0;
- }
答え:出力結果は次のようになります.
- [L]
-
- [i]
「++」は「*」の優先権と同じなので「*ptr++」は「*(ptr+)」に相当します.すなわち、ptr++を先に実行してから*ptrとなるので、操作結果は「L」となる.2つ目の結果は「i」です.
9.質問:コードフラグメントの修正(または読み取り専用コード)
問:次のコードセグメントに間違いがあります.指摘してもらえますか.
- #include<stdio.h>
-
- int main(void)
- {
- char *ptr = "Linux";
- *ptr = 'T';
-
- printf("
[%s]
", ptr);
-
- return 0;
- }
答え:これは*ptr=‘T’によって、メモリ内のコードセグメント(読み取り専用コード)「Linux」の最初のアルファベットが変更されるためです.この操作は無効なため、seg-faultまたはクラッシュをもたらします.
10.自分の名前を変えるプロセス
質問:実行時に自分のプロセス名を変更するプログラムを書くことができますか?
A:次のコードを参照してください.
- #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("
Incremented value is equal to [%d]
", *val);
-
- return 0;
- }
答え:上記のプログラムは正常に動作する場合がありますが、「inc()」には深刻な脆弱性があります.この関数はローカル変数のアドレスを返します.ローカル変数のライフサイクルは「inc()」のライフサイクルであるため、incが終了するとローカル変数を使用すると悪い結果が発生します.これはmain()の変数「a」のアドレスを避けることで、後でこのアドレスに格納されている値を変更することもできます.
12.printf()を扱うパラメータ
問:次のコードは何を出力しますか?
- #include<stdio.h>
-
- int main(void)
- {
- int a = 10, b = 20, c = 30;
- printf("
%d..%d..%d
", a+b+c, (b = b*2), (c = c*2));
-
- return 0;
- }
答え:出力結果は:
- 110..40..60
これは,C言語では関数のパラメータがデフォルトで右から左に処理され,出力時には左から右に処理されるためである.原文:http://www.thegeekstuff.com/2012/08/c-interview-questions/