56461


タイトルの通り，c言語において，配列の宣言時に要素数を明示しない配列について疑問があったので，質問させていただきました．  
例えば以下の例において，

[code]

    #include<stdio.h>
    #include<string.h>
    
    int main(){
      char text1[] = "I like you.";
      char *text2 = "###";
    
      printf("number of elements of text1 == %lu\n", sizeof(text1)/sizeof(text1[0]));
      strcat(text1, text2);
      printf("concatenated text1 == %s\n", text1);
      return 0;
    }
    
[/code]

で，結果が

[code]

    number of elements of text1 == 12
    concatenated text1 == I like you.###
    
[/code]

となりました．  
text1の要素数は，初期値の要素数+'\0'の分で，12個のchar型が確保されることが，上の結果の1行目からも分かります．しかし，strcat()で，text1に11個より大きい文字を格納しているにもかかわらず，エラーが出ずに，きちんと結果が出力されてしまいました．

これはどうしてなのでしょうか？定義された要素数を超えた配列の部分に格納しようとすると，

[code]

    *** stack smashing detected ***:
    
[/code]

のようなエラーが出るのかと思ったのですが...

もし何か分かる方がいらっしゃいましたら，教えていただきたいです．


CC BY-SA 3.0
2018-02-02T10:42:24.507
41411
2019-07-06T18:49:24.900
24477
question
c
c言語における，要素数を定義しない配列の扱いについて


環境が分かりませんが、古くからのC ならば、stackサイズなんてチェックしないのが仕様。  
運が良ければ、そのまま、動きます。  
運が悪ければ、隣りの領域を壊します。 stack over flow になるとどうかは知りませんが、不正な動作となります。(環境依存)  
昔、運よく動いていたのは、隣りに大きな未使用配列があった時。


CC BY-SA 3.0
2018-02-02T11:51:37.527
41412
2018-02-02T11:51:37.527
41411
answer


MicrosoftのVisual C++であれば[/RTC (ランタイム エラー チェック)](https://docs.microsoft.com/ja-
jp/cpp/build/reference/rtc-run-time-error-checks)が用意されています。この機能が有効化されている場合、

> Run-Time Check Failure #2 - Stack around the variable 'text1' was corrupted.

という実行時エラーが検出されます。  
また[`strcat`](https://docs.microsoft.com/ja-jp/cpp/c-runtime-
library/reference/strcat-wcscat-
mbscat)は質問のようなセキュリティ上の欠陥があるため[`strcat_s`](https://docs.microsoft.com/ja-
jp/cpp/c-runtime-library/reference/strcat-s-wcscat-s-
mbscat-s)が推奨されています。`strcat_s`は事前に書き込み先のバッファサイズを受け取るため、書き込み前にバッファオーバーフローを検出できます。


CC BY-SA 3.0
2018-02-02T12:04:57.940
41413
2018-02-02T12:04:57.940
4236
41411
answer


`strcat`が`text1`の隣接するメモリ領域をブルドーザーのように破壊して進んだ結果です。  
以下のコードで`text1`の隣接するメモリ領域がわかります。

[code]

    #include <stdio.h>
    #include <string.h>
    
    int main(){
        char text1[] = "I like you.";
        char *text2 = "###";
    
        printf("before text1\n");
        for (const char *p = text1; *p; ++p) {
            printf("[%c] = %p\n", *p, p);
        }
        printf("[\\0] = %p\n", text1 + strlen(text1));
        printf("[?] = %p\n", text1 + strlen(text1) + 1);
        printf("[?] = %p\n", text1 + strlen(text1) + 2);
        puts("");
    
        strcat(text1, text2);
    
        printf("after text1\n");
        for (const char *p = text1; *p; ++p) {
            printf("[%c] = %p\n", *p, p);
        }
    
        return 0;
    }
    
    
[/code]

実行結果。

[code]

    before text1
    [I] = 0x7fffc49fe470
    [ ] = 0x7fffc49fe471
    [l] = 0x7fffc49fe472
    [i] = 0x7fffc49fe473
    [k] = 0x7fffc49fe474
    [e] = 0x7fffc49fe475
    [ ] = 0x7fffc49fe476
    [y] = 0x7fffc49fe477
    [o] = 0x7fffc49fe478
    [u] = 0x7fffc49fe479
    [.] = 0x7fffc49fe47a
    [\0] = 0x7fffc49fe47b
    [?] = 0x7fffc49fe47c
    [?] = 0x7fffc49fe47d
    
    after text1
    [I] = 0x7fffc49fe470
    [ ] = 0x7fffc49fe471
    [l] = 0x7fffc49fe472
    [i] = 0x7fffc49fe473
    [k] = 0x7fffc49fe474
    [e] = 0x7fffc49fe475
    [ ] = 0x7fffc49fe476
    [y] = 0x7fffc49fe477
    [o] = 0x7fffc49fe478
    [u] = 0x7fffc49fe479
    [.] = 0x7fffc49fe47a
    [#] = 0x7fffc49fe47b
    [#] = 0x7fffc49fe47c
    [#] = 0x7fffc49fe47d
    
[/code]

`[?]`の部分が`text1`に隣接するメモリ領域です。`strcat`は配列のサイズを指定できないので、このようにメモリを破壊して進みます。  
Cの標準ライブラリの関数にはこういった仕様の関数が多いので注意が必要です。


CC BY-SA 4.0
2019-07-06T18:42:50.303
56461
2019-07-06T18:49:24.900
2019-07-06T18:49:24.900
25852
25852
41411
answer