94189


C言語のプログラムについての質問です。  
以下のプログラムがあります。

[code]

    #include <stdio.h>
    #include <limits.h>
    
    int main(void)
    {
        int n = UINT_MAX;
        if (n + 1) {
            puts("true");
        }
    }
    
[/code]

このプログラムを実行すると、何も出力されないことになっています。  
7行目でなぜ0になるか理解できません。  
msys2を使用していて、コンパイラはgccで、デバッガはgdbです。  
VSCodeのバージョンは、1.76.1です。

試したこと

  1. ブレークポイントを6行目に置いて、デバッグをしました。しかし、n = 0と表示されます。
  2. このプログラムは、本に書いてあり、解説を読みました。`unsigned int型のUINT_MAXは、int型の-1となるため、条件式は-1+1で0となる。`とのことです。

どうかよろしくお願いします。


CC BY-SA 4.0
2023-03-15T07:34:19.120
94188
2023-05-03T21:53:39.067
2023-05-03T21:53:39.067
3060
57333
question
windows c msys2
C言語のUINT_MAXについて


`UINT_MAX` は `unsigned int` で表現できる値の最大値です。 `int` で表せる範囲を超えています。

`unsigned int` から `int` への型変換が起こるとき、元の値を変換後の型で表せなければその結果は処理系定義ということになっています。
処理系定義というのはコンパイラごとに違う結果になってもよい (ただしどういう結果になるのかはコンパイラの資料として文書化されるべき) という意味です。

GCC
の文書によれば[符号付き整数は二の補数のみをサポート](https://gcc.gnu.org/onlinedocs/gcc-12.2.0/gcc/Integers-
implementation.html)しているので `UINT_MAX` のビットパターンを `int` として読み替えると `-1` となります。

[二の補数](https://ja.wikipedia.org/wiki/2%E3%81%AE%E8%A3%9C%E6%95%B0)を理解していれば自然な動作であることがわかると思います。


CC BY-SA 4.0
2023-03-15T08:20:06.100
94189
2023-03-15T08:20:06.100
3364
94188
answer


今どきの CPU は例外なく「単純２進数」で符号なし整数を、「２の補数表記」で符号付き整数を表現します。

単純２進数とは最上位ビットを符号と解釈しない表記  
２の補数表記とは最上位ビットを符号と解釈する代わりに表記できる絶対値が約半分になる仕様

説明を短くするために `int` あるいは `unsigned int` の大きさが 16bit であるとします。すると

  * `int` (符号付き整数) で表現できる値は `[-32768...32767]` の範囲  
`-1` は内部表現 `0xFFFF`

  * `unsigned int` (符号なし整数) で表現できる値は `[0U...65535U]` の範囲  
`UINT_MAX` は `65535U` であり、その内部表現は `0xFFFF`

  * `UINT_MAX` は `unsigned int` では表現できるが `int` では表現できない値

となっています。

`int n = UINT_MAX;` ということは `int` (は符号付き数) で表現できない `unsigned int` の値を無理くり `int`
に格納しろという意味です。このときオーバーフローという誤りが発生しています。つまりこの行は文法的には正しいのですが、意味論的には誤っているわけです。でも２の補数表記をする
CPU ではこの誤りを無視して内部表現をそのまま取り扱う（この例では変数 `n` の初期値として採用している）ので `UINT_MAX` の内部表現
`0xFFFF` をそのまま `int` にぶっこみます。なのでオーバーフローをエラーにしない代わりに `n` の値は `-1` となります（本来目的とした
`65535U` とならない）

さて例題の `if (n+1)` においては比較（大小比較、同値比較）がありません。このときは式の値が `0` か `非０` かの比較と解釈されます。
`if ((n+1)!=0)` ってことですね。

誤ったプログラムである結果 `n` は `-1` になっているので `n+1` は `0` だから `if` の比較は偽であり `puts()`
は実行されません。


CC BY-SA 4.0
2023-03-15T08:29:04.170
94190
2023-03-15T08:29:04.170
8589
94188
answer


整数オーバーフローの分かりやすい (かもしれない) 図があります

  * [整数オーバーフロー攻撃対策](https://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/c907.html)

[![整数オーバーフローの図](https://i.stack.imgur.com/fKQf2.png)](https://i.stack.imgur.com/fKQf2.png)

図では 2バイトの整数で示されているけど, 4バイトだとしても扱いは同じ

[code]

        int n = INT_MAX;
        printf("int %d \n", n);
        printf("int %d (+1)\n", n +1);
    //
    // int 2147483647
    // int -2147483648 (+1)
    
        unsigned int un = UINT_MAX;
        printf("uint %u \n", un);
        printf("uint %u (+1)\n", un +1);
    //
    // uint 4294967295
    // uint 0 (+1)
    
[/code]

4バイトでは, ビットパターンとして以下の内容を保持でき,  
最上位ビットを符号ビットとして扱うかどうかが `int`, `unsigned int` の異なるところ  
(なので 4バイト整数の `11111111_11111111_11111111_11111111` の次の値は `0`)

[code]

    00000000_00000000_00000000_00000000
        〜
    11111111_11111111_11111111_11111111
    
[/code]


CC BY-SA 4.0
2023-03-15T08:42:33.143
94191
2023-03-15T13:55:32.277
2023-03-15T13:55:32.277
43025
43025
94188
answer


本を読み直しまして、参考になるところを発見しました。

>
> ここで重要な点は、Cのプログラムは値そのものよりも、型の指定に影響されて動作を変えることです。Cプログラミングでは、変数や関数の返り値、引数などの型を処理の必要に応じて正しく宣言し、その宣言に合わせてコードを記述する必要があります。なぜならば、ある変数が格納するビット構成や、関数が返したビット構成の意味が型によって変わるからです。


CC BY-SA 4.0
2023-03-17T12:04:32.083
94224
2023-03-17T13:03:30.283
2023-03-17T13:03:30.283
3060
57333
94188
answer