47446


以下のコードは、char型の配列を(int*)のポインタを使用して記憶域を操作し文字列"AI"と表示されるように作りたかったのですが,'A'の文字しか表示されないのはなぜですか？  
#include

[code]

    int main(void)
    {
    
    char a[12];  
    char b[3] = "AI";
    
    /*int型のポインタpiで配列aの記憶域を操作して,AIと出力させる。*/
    
    int *pi = (int*) a;
    *pi = 65;
    *(pi + 1) = 73;
    *(pi + 2) = '\0';
    printf("%s\n", pi);
    
    /*bの配列を表示させる*/
    
    printf("%s", b);
    return 0;
    
    /*****実行結果******/
    //A
    //AI
    }
    
[/code]


CC BY-SA 4.0
2018-08-11T08:34:18.380
47444
2018-08-11T10:02:37.777
28645
question
c ポインタ
%sで配列の中身を表示さる時に、配列の先頭要素のアドレスを渡せば'\0'までの文字が表示される認識ですが、そうならない原因にがわからない。


`char`型配列`a`を操作するのになぜ`char *`ではなく、`int *`を使うのか意図がよくわからないんですが…。

ここでは`char a[12];`と宣言しているところからして、`int`型が4バイト(32ビット)のシステムで試されているものと仮定します。

[code]

        +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10+11
    a: |??|??|??|??|??|??|??|??|??|??|??|??|
    
[/code]

`int`型の定数`65`は16進表現すると、`0x00000041`となりますが、それを`int
*`型のポインタを通じて操作すると`int`型全体、つまり4バイトの領域に値がセットされ、リトルエンディアンのシステムなら、結果は以下のようになります。

[code]

    *pi = 65;
    
        +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10+11
    a: |41|00|00|00|??|??|??|??|??|??|??|??|
    
[/code]

また **ポインタ加算は要素のデータ型(のサイズ)でスケーリングされる** ので、その後のメモリ操作の結果は以下のようになります。

[code]

    *(pi + 1) = 73; //73は16進で0x00000049
    
        +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10+11
    a: |41|00|00|00|49|00|00|00|??|??|??|??|
                    ↑ (pi + 1) はここ
    
    *(pi + 2) = '\0'; //'\0'の値0x00は`int`型に引き伸ばされて、0x00000000と扱われる
    
        +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10+11
    a: |41|00|00|00|49|00|00|00|00|00|00|00|
                                ↑ (pi + 2) はここ
    
[/code]

あなたが、`printf("%s\n", pi);`を実行すると、 **ポインタ型`int *`は`printf`の内部の処理には伝わらず、常に`char
*`として1バイトずつ値を取り出して`'\0'`(=0x00)が出てくるまで出力します**から、最初の0x41(=`'A'`)だけが出力されるのは当然と言えるでしょう。

一部のCコンパイラではこのような使い方(`%s`に対応する引数が`char *`以外のポインタ)であると警告が出ると思います。`char
*`(または`unsigned char *`)以外の引数を`%s`に与えるとこのような動きになるというのはよく理解しておかれた方がいいでしょう。


CC BY-SA 4.0
2018-08-11T09:49:44.183
47446
2018-08-11T09:49:44.183
13972
47444
answer


> char型の配列を(int*)のポインタを使用して…

char ポインタ(char*)を使わないのは何故なんだろうとは思いますが…  
それはさておき、gdb で実行状況を調べてみると分かり易くなるかもしれません。

[code]

    $ gcc -Wall -g ai.c -o ai
    $ gdb ai
    
    (gdb) break 12
    Breakpoint 1 at 0x5b5: file ai.c, line 12.
    (gdb) run
    Breakpoint 1, main () at ai.c:12
    12        *pi = 65;
    (gdb) p/c a
    $3 = {1 '\001', 0 '\000', 0 '\000', 0 '\000', 100 'd', ...
    (gdb) p/c b
    $4 = {65 'A', 73 'I', 0 '\000'}
    (gdb) p/x &a
    $5 = 0xbffff3a0
    (gdb) p/x pi
    $6 = 0xbffff3a0
    (gdb) p sizeof(int)
    $7 = 4
    (gdb) p/x (pi + 1)
    $8 = 0xbffff3a4     ## +4 byte
    (gdb) p/x (pi + 2)
    $9 = 0xbffff3a8     ## +8 byte
    
[/code]

上記の様に、pi ポインタの位置は int のサイズ(この場合 4 byte)毎に移動して行きます。  
最終的に char 型配列 a の中身は以下の様になります。

[code]

    (gdb) p/c a
    $1 = {65 'A', 0 '\000', 0 '\000', 0 '\000',
          73 'I', 0 '\000', 0 '\000', 0 '\000',
          0 '\000', 0 '\000', 0 '\000', 0 '\000'}
    (gdb) p a
    $10 = "A\000\000\000I\000\000\000\000\000\000"
    (gdb) p puts(a)
    A
    
[/code]

ちなみに、char* ではどうなるのかと言うと…

[code]

    (gdb) set $cptr = malloc(sizeof(char *))
    (gdb) set $cptr = a
    (gdb) p/x $cptr
    $11 = 0xbffff3a0
    (gdb) p sizeof(char)
    $12 = 1
    (gdb) p/x ($cptr + 1)
    $13 = 0xbffff3a1
    (gdb) p/x ($cptr + 2)
    $14 = 0xbffff3a2
    (gdb) set *($cptr) = 65 
    (gdb) set *($cptr+1) = 73
    (gdb) set *($cptr+2) = '\0'
    (gdb) p puts(a)
    AI
    $15 = 3
    
[/code]


CC BY-SA 4.0
2018-08-11T10:02:37.777
47447
2018-08-11T10:02:37.777
47444
answer