Shammer's Philosophy

My private adversaria

文字列 printf で Segmentation fault 実験

EchoClientV2_Debug - Shammerismのように、文字列の画面出力でちょっと謎の動作をしているので実験。
まずは以下のコード。

#include <stdio.h>

int main(int argc, char *args[]){
    char x[] = "abc";
    printf("%s\n", x);
    return 0;
}

この実行結果は以下。

$ gcc segfault.c 
$ ./a.out
abc

問題なし。次は以下のコード。

#include <stdio.h>

int main(int argc, char *args[]){
    char x[256] = "abc";
    printf("%s\n", x);
    return 0;
}

この実行結果は以下。

$ gcc segfault.c 
$ ./a.out
abc
$

これも問題なし。次は以下のコード。

#include <stdio.h>

int main(int argc, char *args[]){
    char *x[256] = "abc";
    printf("%s\n", *x);
    return 0;
}

これはコンパイルエラーになった。

$ gcc segfault.c 
segfault.c:4:11: error: array initializer must be an initializer list
    char *x[256] = "abc";
          ^
1 error generated.
$

次は以下のコード。

#include <stdio.h>

int main(int argc, char *args[]){
    char x[256] = "abc";
    printf("%s\n", *x);
    return 0;
}

結果は以下。

$ gcc segfault.c 
segfault.c:5:20: warning: format specifies type 'char *' but the argument has type 'char' [-Wformat]
    printf("%s\n", *x);
            ~~     ^~
            %c
1 warning generated.
$ ./a.out
Segmentation fault: 11
$

コンパイル時にWarningが出て、しかもSegumentation faultとなった。でも、先に書いたEchoClientはWarningは出ないから微妙に違う原因かも。
次は以下のコード。

#include <stdio.h>

int main(int argc, char *args[]){
    char x[256] = {'a','b','c'};
    printf("%s\n", x);
    return 0;
}

実行結果は以下。

$ ./a.out
abc
$

これは問題ないのか。。。次はこれ。

#include <stdio.h>

int main(int argc, char *args[]){
    char x[256];
    x = {'a','b','c'};
    printf("%s\n", x);
    return 0;
}

一見すると、一つ上のパターンと同じだが、変数の宣言と値の代入を分けた。実行結果は以下。

$ gcc segfault.c 
segfault.c:5:9: error: expected expression
    x = {'a','b','c'};
        ^
1 error generated.

なんかエラーが起きたぞ。なぜダメなんだ?理由はよくわからないが、正規表現扱いされているようだ。宣言と代入を分けた場合はこれじゃダメなのか。じゃあ1つずつ入れるしかない。

#include <stdio.h>

int main(int argc, char *args[]){
    char x[256];
    x[0] = 'a';
    x[1] = 'b';
    x[2] = 'c';
    printf("%s\n", x);
    return 0;
}

これは問題なかった。

$ gcc segfault.c 
$ ./a.out
abc
$

次はこれ。

#include <stdio.h>

int main(int argc, char *args[]){
    char *x[256];
    x[0] = 'a';
    x[1] = 'b';
    x[2] = 'c';
    printf("%s\n", x);
    return 0;
}

これは、新しいパターンだ。一文字しか表示されない。

$ gcc segfault.c 
segfault.c:5:10: warning: incompatible integer to pointer conversion assigning to 'char *' from 'int' [-Wint-conversion]
    x[0] = 'a';
         ^ ~~~
segfault.c:6:10: warning: incompatible integer to pointer conversion assigning to 'char *' from 'int' [-Wint-conversion]
    x[1] = 'b';
         ^ ~~~
segfault.c:7:10: warning: incompatible integer to pointer conversion assigning to 'char *' from 'int' [-Wint-conversion]
    x[2] = 'c';
         ^ ~~~
segfault.c:8:20: warning: format specifies type 'char *' but the argument has type 'char **' [-Wformat]
    printf("%s\n", x);
            ~~     ^
4 warnings generated.
$ ./a.out
a
$ 

次はこれ。

#include <stdio.h>

int main(int argc, char *args[]){
    char *x[256];
    x[0] = 'a';
    x[1] = 'b';
    x[2] = 'c';
    printf("%s\n", *x);
    return 0;
}

実行結果はこれ。

$ gcc segfault.c 
segfault.c:5:10: warning: incompatible integer to pointer conversion assigning to 'char *' from 'int' [-Wint-conversion]
    x[0] = 'a';
         ^ ~~~
segfault.c:6:10: warning: incompatible integer to pointer conversion assigning to 'char *' from 'int' [-Wint-conversion]
    x[1] = 'b';
         ^ ~~~
segfault.c:7:10: warning: incompatible integer to pointer conversion assigning to 'char *' from 'int' [-Wint-conversion]
    x[2] = 'c';
         ^ ~~~
3 warnings generated.
$ ./a.out
Segmentation fault: 11
$

いろいろやって思ったが、自分の書いたEchoClientと近いのは、

  • char[]のポインタとして宣言
  • 宣言と代入は分ける
  • printf では * を使用して参照する

というパターンで、一番最後のもの。もう一つ、Segfault になったパターンも、printf で * を使用して参照するという点は共通だ。EchoClient で Warning が出ないのは、コード上で値を代入していないためで本質とは関係ないのかもしれない。とりあえず、最後のパターンで問題ないようにできれば EchoClient の問題も解決するかもしれない。