C : 配列の添字について


以下に示すコードが何故実行できるのかわかりません.  
`(2)` において、添字は`-1` になっていると思いますが、何故エラーが発生しないのですか?

[code]

        #include <stdio.h>
        #include <string.h>
    
        int main()
        {
          char s[] = "hello";
    
          int len = strlen(s);
          printf("len: %d\n", len);               // (1)
    
          printf("%c\n", s[strlen(s) - 1 - len]); // (2)
          // printf("%c\n", s[-1]);               -> compile error
          // printf("%c\n", s[strlen(s) - 6]);    -> (2) と同じ
    
          return 0;
        }
    
[/code]

実行結果 `(2)` の `printf` は改行のみの印字です.

[code]

        len: 5
    
[/code]

コメントの通り、`s[-1]` では警告が表示されるだけで、コンパイルエラーにはなりませんでした.




提示例は [c](/questions/tagged/c "'c' のタグが付いた質問を表示") や
[c++](/questions/tagged/c%2b%2b "'c++' のタグが付いた質問を表示")
でいうところの「未定義動作」を引き起こしています。  
言語仕様書による専門用語「未定義動作 (undefined behavior) 」とは  
\- 誤ったプログラム、または誤ったデータに対する動作であって  
\- 言語仕様書はその動作に対して何も規定しない  
です。

起こりうる動作としては  
\- 処理系が独自の正しい動作を規定する (Visual C の `fflush(stdin);` 等)  
\- コンパイラが翻訳時点で診断し、指摘する (提示例 `s[-1]` )  
\- 診断の結果、コンパイルエラーになるかもしれない  
\- 診断の結果、警告になるかもしれない  
\- リンカがリンク時点で診断し、指摘する (結果は同上)  
\- 実行時にライブラリ関数や OS 等が診断し、実行時エラーで強制終了となる  
\- 診断等が一切なされずにプログラムとしては動作を続けるが  
\- あたかも「プログラマが勝手に期待している通りに」動作する  
\- プログラマの期待に背いて変な動作をする (提示例 (2))  
\- いわゆる「暴走」をする  
\- その他の動作

ということで

> 何故エラーが発生しないのですか?

そもそも「エラーとして検出される」ことは期待してはいけないのです。

