14760

[code]

    char* a  = "AAA";
    char b[] = "BBB";
    
[/code]

上のように宣言しても

[code]

    for( int i=0; a[i]; i++ ){ cout << a[i]; }
    for( int i=0; b[i]; i++ ){ cout << b[i]; }
    
[/code]

上のように同じように利用しているので違いがわからないのですが、`a`と`ｂ`には違いがありますか


CC BY-SA 3.0
2015-08-18T01:56:58.987
14553
2016-06-04T13:36:44.040
2015-09-08T23:32:57.053
3605
10945
question
c++ ポインタ array
char *a と char b[] にはどのような違いがありますか


`char* a`は`"AAA"`という領域の先頭アドレスを格納しているポインタ変数です。  
`char b[]`は`"BBB"`という領域を格納している配列です。

使う側はあまり気にしなくても使えますが、厳密には異なるものです。


CC BY-SA 3.0
2015-08-18T02:08:25.740
14554
2015-08-18T02:08:25.740
7214
14553
answer


`char* a` はポインタ `char b[]` は配列です。  
ポインタと配列はまったく違うものですが、一見同じようにプログラムが組めてしまうのが [c](/questions/tagged/c "'c'
のタグが付いた質問を表示") [c++](/questions/tagged/c%2b%2b "'c++' のタグが付いた質問を表示") の悪いところです。

配列 `b` には `'B', 'B', 'B', '\0'` という４つの要素が格納されています。  
ソースコード上に変数名 `b` を書くと (例外パターンを除き) `&b[0]` なる [ポインタ右辺値] と扱われます。

ポインタ変数 `a` にはポインタの値が格納されます。ポインタは端的にはメモリアドレスだから  
\- 32bit マシンなら 32bit の値  
\- 64bit マシンなら 64bit の値  
です (16bit や 20bit であるような機械もあります)

ここでは、そのポインタ変数 `a` だけでなくて、もうひとつ、無名な文字配列が同時に作られます。  
その無名な配列には `'A', 'A', 'A', '\0'` が格納されています。  
そして `a` はその無名な文字配列の先頭要素を指すように初期化されています。

提示例より違いがわかりやすいはずなので次のサンプルを試してみてください。

[code]

    char* a="ABCD";
    char b[]="EFGH";
    cout << sizeof a << endl;
    cout << sizeof b << endl; // sizeof は先の例外パターン
    
[/code]

まずは実行する前に、どんな値が表示されるかを推理してみて  
実行結果と比較してみてください。

あとついでに [c++](/questions/tagged/c%2b%2b "'c++' のタグが付いた質問を表示") ならこんな機能も使えます（
[c](/questions/tagged/c "'c' のタグが付いた質問を表示") には無い機能）。  
`#include <typeinfo>` を追加しておき

[code]

    cout << typeid(a).name() << endl;
    cout << typeid(b).name() << endl;
    
[/code]

GCC 4.8.2 では結果 `Pc` `A5_c` となりました。  
Visual C++ 2005 では結果 `char *` `char [5]` となりました。


CC BY-SA 3.0
2015-08-18T02:30:13.967
14557
2015-08-18T04:20:21.167
2015-08-18T04:20:21.167
8589
8589
14553
answer


ポインタと配列の違いであることは他の方の回答のとおり。

最も大きな違いはその変数や要素を書き換えようとしたときに現れると思います。

[code]

    char* a  = "AAA";
    char b[] = "BBB";
    
    // 変数への代入
    a = "CCC"; // OK。文字列"AAA"と変数aは切り離され、新たに"CCC"へのポインタがaに代入される。
    b = "DDD"; // NG。コンパイルエラー。配列変数に何かを代入することはできない。
    
    // 要素の書き換え
    a[1] = 'X'; // NG。文字列定数の書き換えであり、
                //     コンパイルエラーにはならないが実行時の挙動は未定義。
    b[1] = 'Y'; // OK。配列bの中身は 'B', 'Y', 'B', '\0' となる。
    
[/code]

なお、厳密には

[code]

    char* a  = "AAA";
    
[/code]

ではなく

[code]

    const char* a  = "AAA";
    
[/code]

とすべきです。  
文字列リテラル(ソースコードに直接記述する`""`で囲まれた文字列)の型はconst char[]なので、  
char*への変換はすべきではありません。(多くのコンパイラは警告を出してくれると思います。)  
またそうすれば、上記NG例の

[code]

    a[1] = 'X';
    
[/code]

はコンパイルエラーになってくれるので、未定義動作をコンパイル時点で回避できます。


CC BY-SA 3.0
2015-08-18T03:46:13.487
14560
2015-08-18T03:46:13.487
4765
14553
answer


他の回答で説明は十分だと思われますが、混乱してしまう原因の一つに関数の引数の場合があります。これだけはポインタで書こうが配列で書こうがどちらもポインタの型として認識されます。ただし配列の方は明示的に要素数を指定することも可能です。

[code]

    void foo(int *a)
    
[/code]

と

[code]

    void foo(int a[])
    
[/code]

はどちらも同じ意味です。  
さらに補足というか蛇足ですが、これが二次元以上の配列を関数に渡すときは少し事情が違ってきます。  
詳しくは次のリンクを参考にしてください。  
<http://www.nurs.or.jp/~sug/soft/tora/tora10.htm>

あと、関数とは別の話で、ローカル変数として配列で宣言時に文字列を代入できるのに、一旦何も代入せずに配列で宣言した場合はstrcpy()などを使わないと配列に入れられない等も、最初は理解に苦しむポイントです。一方ポインタの場合は宣言時以降も文字列をそのまま代入することが可能です。


CC BY-SA 3.0
2015-08-18T03:51:21.687
14561
2015-08-21T17:26:59.170
2015-08-21T17:26:59.170
7744
7744
14553
answer


「文字列リテラル」の説明を補足しておきます。  
C++ ソースコード中で文字列リテラルを使うと、実行時にはこの文字列は書き換えが禁止された静的な領域に配置されます。  
ポインタに文字列リテラルを代入した場合は、この静的領域にある文字列の先頭アドレスが直接代入されるので、この文字列を変更することは出来ません。  
配列に文字列リテラルを代入した場合はその文字列のコピーが配列に格納されるため変更が可能です。  
つまり、文字列リテラルで受けた文字列をあとで変更する必要があれば配列で受けなければなりません。


CC BY-SA 3.0
2015-08-19T02:46:58.233
14607
2015-08-19T02:46:58.233
2748
14553
answer


文字列がどのようなメモリに配置されるかが異なってきます。

  * `char* a = "AAA";`は、コンパイラが静的記憶期間の（プログラムの実行開始から終了までずっと存在する）メモリ領域に文字列を配置し、それへのポインタを`a`に代入するというものです。
  * 一方、`char b[] = "BBB";`は`char b[] = { 'B', 'B', 'B', '\0' };`の別表記です。配列の初期化においてのみ`""`の文字列リテラルはこのようにほかと違う取り扱いになります。

そのため、以下のように、変数の寿命を越えると差が出ます。

まず、`const char*`の変数の場合、`return`しても文字列本体は有効です。

[code]

    #include <cstdio>
    
    const char* f()
    {
        const char* a = "ABC";
        return a;
    }
    
    int main()
    {
        puts(f());
    }
    
[/code]

一方、配列を使った以下のプログラムでは、未定義の動作になります。関数から抜けると共にオブジェクト`b`が破棄されるためです。実例としては、デタラメな文字列が出力されたり、クラッシュしたりすることがあります。場合によっては、abcが出力されるものもありますが、偶然だと考えてください。

[code]

    #include <cstdio>
    
    const char* f()
    {
        char b[] = "abc";
        return b;
    }
    
    int main()
    {
        puts(f());
    }
    
[/code]

参考

  * [C FAQ 6章 配列とポインター 6.2: でもchar a[]はchar *aと同じと聞いたことがあるが。](http://www.kouno.jp/home/c_faq/c6.html#2)
  * [DCL30-C. 適切な記憶域期間を持つオブジェクトを宣言する](https://www.jpcert.or.jp/sc-rules/c-dcl30-c.html)


CC BY-SA 3.0
2015-08-23T09:10:44.717
14760
2016-06-04T13:36:44.040
2016-06-04T13:36:44.040
8616
8616
14553
answer