C言語の問題①②解答

@kusano_k ksn
C言語の問題① ( x==y && x+1==y+2 ) が真(非0)となるxとyの型と値は何でしょう?
C言語の問題② ( x==y && x+1!=y+1 ) が真(非0)となるxとyの型と値は何でしょう?

https://twitter.com/#!/kusano_k/status/127368325490683904

の解答。

特定の環境でコンパイルできて非0の値になればOK。xとyは別の型でもOK。問題中の式はそのまま使う((bool)x==(bool)y のようにキャストするのはダメ)。C++ではない(適当なクラスを作って演算子オーバーロードはダメ)。というつもりだった。

私の考えていた解法

#include <stdio.h>

int main()
{
    {
        //  (1)
        double x = 1e100;
        double y = 1e100;
        printf( "%d\n", ( x==y && x+1==y+2 ) );
    }
    {
        //  (2)
        char *x = 0;
        int *y = 0;
        printf( "%d\n", ( x==y && x+1!=y+1 ) );
    }
}

doubleの仮数部の精度は50bit程度なので、大きな数と小さな数を足すと小さな数が無視されてしまう(情報落ち)。

ポインタに整数nを加えると、アドレスはn増えるのではなく、ポインタが指す変数のサイズのn倍増えることを利用する。C++ではエラーになるけど、Cなら異なる型のポインタが比較できる(警告はでるかも)。

見かけた間違い①

bool(_Bool)を使う

0が偽で0以外の値が真ということだろうけど、計算前にint型に変換されてしまうのでダメ。

$ cat test.c
#include <stdio.h>
int main() {
    _Bool x=0, y=0;
    printf( "%d\n", ( x==y && x+1==y+2 ) );
}
$ gcc test.c; ./a.out
0

見かけた間違い②

char/short/int/long

例えば、short x=0x7fff, int y=0x7fff ならば x+1=-0x8000, y+1=0x8000 になるということだろうけど、intより小さい型は計算前にintに変換されるというルールがあるのでダメ。

参考

少し詳しい型変換の説明

signed/unsigned

例えば、signed int x=0x7fffffff, unsigned int y=0x7fffffff。==での比較の前に、unsignedに変換されてしまうのでダメ。

別解

@ucq 勇士Q(β)
int*x=0;short*y=0;みたいな

https://twitter.com/#!/ucq/status/127374331327164416

@dempacat 天下一品だよね…… RT 坦々麺
double使って①できた。doubleとfloat両方で②やろうとしたら暗黙の型変換でできなかった。ポインタ使ったら、①②同時に満たせた。片方だけってことも可能。 QT @kusano_k: C言語の問題①②

https://twitter.com/#!/dempacat/status/127374461937786880

@take_songs たけ (TAKE)
①short*とint? ②違う方のポインタ同士? RT @kusano_k C言語の問題① ( x==y && x+1==y+2 ) が真(非0)となるxとyの型と値は何でしょう?   C言語の問題② ( x==y && x+1!=y+1 ) が真(非0)となるxとyの型と値は

https://twitter.com/#!/take_songs/status/127376160161136640

②のようにポインタを使っても解ける。しかも、上手く書くとそのまま②の答えにもなる。x+1==y+3のようにすれば、この解は排除できたのだろうか。

@kakkun61 岡本和樹
@kakkun61 long long x = INT_MAX; int y = INT_MAX; でうまくいった。

https://twitter.com/#!/kakkun61/status/127634644064673792

暗黙的にintに変換されてしまうのが問題なので、intよりも大きな型なら良いと。Skypeで別の方からも同じことを言われた。なるほど。