• ベストアンサー

文字列の宣言(ポインタと配列)

Cでソケット通信などはできるくらい(もちろんできると言っても私のレベルで) になりましたが、文字列を宣言する際に char *str = "ahaha'; char str[] = "ahaha"; の2種類の違いが今イチしっくりきません。 いろんな参考書でこれの説明はありましたが、でも結局なんなんだ、という感じです。 ポインタで宣言するべき時、配列で宣言するべきときが判断できません。 またポインタで宣言するとエラーが出るけど試し配列で宣言してみたら なんか知らんが動いた、ということも多々ありましたが理由がよくわからなかったです。 よろしくお願いいたします。

  • R-gray
  • お礼率41% (1005/2413)

質問者が選んだベストアンサー

  • ベストアンサー
  • rabbit_cat
  • ベストアンサー率40% (829/2062)
回答No.7

#1、#2の方のとおり、 char *str = "ahaha'; は書き換え不可能な文字列 char str[] = "ahaaha"; は書き換え可能な文字列 っていう違いです。 パソコンのユーザーアプリなんかでは、普通書き換え可能なRAMにしかアクセスできないんで、あんまり違いはないんですが。 小さなメモリ+ROMみたいな組み込みのシステムでは、 char *str = "ahaha'; は、固定文字列(書き換え不可能)で、多くの場合ROMに置かれます。 char str[] = "ahaha'; は、書き換え可能ですから、ROMには置けず、RAMに置かないといけません。(必ずしもスタックに置く必要はないですが)

その他の回答 (8)

  • aris-wiz
  • ベストアンサー率38% (96/252)
回答No.9

>char *strもあればchar str[]もありますが >べつにどちらか片方あればいいんじゃないでしょうか。 これは私の思い込みですが、 この2つはC言語で実装する方法が同じだけであって、 用途としては使い分けるのが正しいと思います。 つまりC言語がこれらの変換を暗黙で行っていたとしても ポインタ変数は、変数の「アドレスを代入できる変数」であり、 配列名は複数の変数の先頭を示すアドレス「複数の変数」です。 違いと言うものは存在していると思います。

  • php504
  • ベストアンサー率42% (926/2160)
回答No.8

プログラム実行時にコードが置かれるメモリは書き換えられるとプログラムが動かなくなるので読み込み専用として保護されます。 この領域に書き込みしようとするとaccess violationのエラーが出て異常終了になります。 char *str = "ahaha'; の場合のポインタはこの書き込み禁止領域のアドレスになるということです。 char str[] = "ahaha"; の場合は読み書き自由なエリアのアドレスになります。

  • koko_u_
  • ベストアンサー率18% (459/2509)
回答No.6

>ただわざわざ2種類用意されている必然性って何なのでしょうか。 >いろんなソースを見ていると >char *strもあればchar str[]もありますが >べつにどちらか片方あればいいんじゃないでしょうか。 char str[] = "ahaaha"; が許されないと、文字配列 str の初期化をいちいちループをまわして(あるいは strncpy で)書かねばならず、面倒なので却下。 char *str = "ahaaha"; が許されないと。。。思い付かん。 多分 char str[] で代替可能だけど、ただのポインタの初期化だから、これだけ禁止にする理由もなかろう。 一つのソースの中で使い方が統一されていれば、特に問題ないと思われますが。 私は大抵前者を変更され得る文字配列の初期化に、後者をマジックナンバーを回避するための定数変数の宣言に使っています。

回答No.5

あと、そういえば Ano.3 の図にもありますが * だと str見て -> アドレス書いてあった。 アドレス見て -> "ahaha" って書いてあった。 の2回メモリアクセス [] だと "ahaha" って書いてあった の1回アクセス ですね。 最適化されるとそうなるか分からないですが。 なんかださっきの結論と逆ですが、違いがでる場所が違うので気にしないで下さい。(無責任) 結論としては、出来たプログラムを逆アセンブルしてみると面白いかもしれません。 多分、どの本にも書いてない事が分かりますよ。(見るときの気持ち次第ですが)

回答No.4

多分どっちでも、目的のプログラムは書けるんじゃないかと思います。 その他思う事としては、 str が どこかの関数のローカル変数なら [] の方は、関数に入る度に スタックに 0x61 0x68 0x61 0x68 0x61 0x00 => "ahaha" と書かないといけないので、ちょっと遅いかもしれません。 (x86のcpuなら 0x68616861 と 0x0061 の2回のアクセスかなぁ?) ただ同様に、 * の場合は、スタックに "ahaha" のアドレスを書かないとダメなの これにも時間がかかります。 (こっちはアドレスなので 32bits 1回) まとめると もっと 文字数が多いと差が出るんじゃないかと思います。 あと > いろんな参考書でこれの説明はありましたが、でも結局なんなんだ、という感じです。 こういう気持はすごく良く分かります。 結局、本って他人が過去に思った疑問に答えてるような部分があるので 自分の疑問には答えてくれませんよね。

  • noocyte
  • ベストアンサー率58% (171/291)
回答No.3

●char *str = "ahaha";   ┌──┐    ┌─┬─┬─┬─┬─┬─┐ str│ ・─┼─→│_a_│_h_│_a_│_h_│_a_│_\0_│   └──┘   └─┴─┴─┴─┴─┴─┘ ●char str[] = "ahaha";   ┌─┬─┬─┬─┬─┬─┐ str│_a_│_h_│_a_│_h_│_a_│_\0_│   └─┴─┴─┴─┴─┴─┘

R-gray
質問者

お礼

みなさんありがとうございます。 ただみなさんのおっしゃっているあたりのことは私も大体理解できているのです。 (すみません、初めに言っておくべきでした。) ただわざわざ2種類用意されている必然性って何なのでしょうか。 いろんなソースを見ていると char *strもあればchar str[]もありますが べつにどちらか片方あればいいんじゃないでしょうか。 あるソースでchar *strと宣言した方は何故char str[]じゃ嫌だったんでしょう。 逆もまた然りです。

  • aris-wiz
  • ベストアンサー率38% (96/252)
回答No.2

int main(void) { char* str = "aaa"; char str2[] = "bbb"; // *str = 'c'; *str2 = 'd'; printf( "%s\n", str ); printf( "%s\n", str2 ); return 0; } 上記のプログラムで、 strは文字リテラルへのポインタになります。 つまり。 「リテラル」つまり「定数」です 何処かの領域[0] = 'a' 何処かの領域[1] = 'a' 何処かの領域[2] = 'a' 何処かの領域[3] = '\0' str = 何処かの領域のアドレス str2は、(文字列の)配列変数です。 str2[0] = 'b' str2[1] = 'b' str2[2] = 'b' str2[3] = '\0' 上記のプログラムで、 *str = 'c'; とできないのは、「定数」であるstrの 先の領域を書き換えようとしている為です。 C++などではこれをDefineの代わりに使用します。 const char* PATH = "c:\tmp\path";

  • koko_u_
  • ベストアンサー率18% (459/2509)
回答No.1

char* str = "ahaaha"; は「どこかの静的な領域に取られた」"ahaaha" にアクセスするために、そのアドレスを str に格納している。 当然、str の指す場所には書き込めない。本来 const char* と宣言するべき char str[] = "ahaaha"; は「自分で確保したスタック領域」を "ahaaha" で初期化。 もちろん、後で str を書換えることも可能。

関連するQ&A

  • 配列とポインタ

    配列とポインタの違いを教えて下さい。 後 char str[100]; と const char *strはどこが違いますか?

  • 文字列を表すための配列とポインタ

    文字列を表すための配列とポインタ  配列とポインタは同様に扱えるもの、と思って、次のプログラムを作りました。処理系は、Visual Studio 2010 コマンドプロンプトです。 #include <stdio.h> void main(void) { char a[256]; char *b; printf("文字列を入力してください。\n"); printf("例「abcde」\n\n"); printf("配列型文字列を使います。\n"); scanf("%s", a); printf("文字列は%sです。\n\n", a); printf("ポインタ型文字列を使います。\n"); scanf("%s", b); printf("文字列は%sです。\n", b); }  すると、まずコンパイル時に、 「warning C4700: 初期化されていないローカル変数'b'が使用されます」 と表示されました。そして、実行すると、「配列型文字列」の方は問題ないのですが、「ポインタ型文字列」の方の実行後に、 「x.exeは動作を停止しました。 問題が発生したため、プログラムが正しく動作しなくなりま した。プログラムは閉じられ、解決策がある場合は Windowsから通知されます。」 と表示され、エラーとして終了してしまいます。 「char *b;」 と宣言するところが問題のようですが、なぜなのかが分かりません。どなたか、解説をお願いします。

  • 配列で混乱しています

    c言語を勉強中のものです。文字列を配列で扱う場合とポインタで扱う場合の違いにこんらんしています。 たとえば char str[] = "hello"; str = "goodby"; のように文字列の再格納はダメですが、 char *str = "helllo"; str = "goodby" は大丈夫です。 また char[100]; scanf("%s",str) は大丈夫ですが char *str; scanf("%s",str) はいけません。この2つの違いは何によるのでしょうか? またポインタはそもそも int a; int *pa; pa = &a; のように、宣言したあとはアドレスを入れる専用のハコのように考えていたのですが、 char *str = "helllo"; str = "goodby" のようにいきなりアドレスでない文字列を代入するのも納得がいきません。誰か解説をお願いします。

  • C言語の型と配列

    char* str[10]={"a","b"}; char* str2="c"; としたときにstr=str2とすると 型が合わないといったエラーが出ます。 でもstrって結局はポインタの配列の先頭要素のアドレスですよね。 ポインタにポインタを入れているので通るのかなと思ったんですけど、 配列で宣言するとポインタにも型がつくのでしょうか? この例だと strは char * を10個持つ配列をさすポインタ  で、 str2はchar *をさすポインタ みたいなかんじです。 質問の意味がわかりにくいですが、ご指摘をいただければ補足しますので よろしくお願いします。

  • 配列とポインタと同時に使用する宣言について

    配列とポインタを同時に使用した場合、どのように確保されるのかイマイチ理解しきれていません。 例えば、 char *pointer[10]; と宣言した場合、 pointer[0]="ABC" pointer[1]="DEF" ・・・・ のようにポインタ型を配列で管理する、という風に認識しています。 一方で char (*pointer)[10] とした場合、どのように確保されるのでしょうか?

  • 文字列を配列化する方法を教えてください!

    1列の文字列を配列化する方法を教えてください。 メモリの確保らしいのですが、参考になるソースが拝見できれば嬉しく・・・main から参照したいです。 char Text[] = "AAAA\nBBBB\nCCCCCCCC\nDDD\n";  ↓ char str[0] = "AAAA";    str[1] = "BBBB";    str[2] = "CCCCCCCCC";    str[3] = "DDD"; どうやったらいいのか全くイメージがわかないので、 よろしく御願いします!

  • 二次元配列による文字列の配列の受渡しについての質問です。

    二次元配列による文字列の配列の受渡しについての質問です。 #include <stdio.h> void print_pname(char str[][5], int n) { int i, j; for (i = 0; i < n; i++) { printf("str[%d] = \"", i); for (j = 0; str[i][j] != '\0'; j++) putchar(str[i][j]); printf("\"\n"); } } int main(void) { char ary[][5] = {"Lisp", "C", "Ada"}; print_pname(ary, sizeof(ary) / sizeof(ary[0])); return 0; } 上のプログラム中の関数print_pnameの引数char str[][5]についてですが char (*str)[5](配列のポインタ)と変更した場合にwarningが多数発生します。 これはどうしてでしょうか? また、上のプログラムを配列のポインタを使って変更することは可能でしょうか? 以上、よろしくお願いします。

  • 配列とポインタの関係

    配列とポインタについてです。 char *p_str="HAPPY";と char p_str[10]={'H','A','P','P','Y'}; は同じことでしょうか?

  • ポインタの配列について

    はじめまして。 基本的な質問で恐縮ですが、質問があります。 例えば、char *name[] = {"Alice","Ben","Chris","Diana","Elmo"} のようなポインタの配列を宣言・初期化したとき、配列nameの各要素の 中身には、初期化子として与えた、文字列の先頭アドレスが 格納されているという解釈でよろしいのでしょうか?つまり、 *(name+1)やname[1]は、「文字列Benの先頭アドレスを指す」という意 味でよろしいのでしょうか?また、このとき、(name+1)というように *をつけないときは、「文字列Benの先頭アドレスを格納したメモリの 先頭アドレス」というような解釈でよろしいのでしょうか? どうかご教授お願いします。

  • プログラミング ポインタを使った文字列比較

    プログラミング ポインタを使った文字列比較 2つの文字列str1, str2を入力し,それらが等しければ0,等しくなければ1を返す関数str_compareを作り、返り値によって以下のように表示するプログラムを作れ。ただし,関数strcmpを使ってはならない。 文字列の入出力はmain関数で行い,関数str_compareの仮引数にはポインタ変数を宣言し,ポインタと間接演算子*を用いた処理を行うこと。 % ./a.out input str1 = Worldcup input str2 = Worldcup same strings % ./a.out input str1 = World input str2 = cup different strings この問題に私は次のようにプログラミングしました。 #include <stdio.h> #define MAX 100 int str_compare(char *, char *); main() { char str1[MAX], str2[MAX]; printf("input str1 = %s", str1); scanf("%s", str1); printf("input str2 = %s", str2); scanf("%s", str2); str_compare(str1, str2); if (str_compare(str1, str2) == 0) printf("same strings\n"); else if (str_compare(str1, str2) == 1) printf("different strings\n"); } int str_compare(char *s1, char *s2) { int i; for (i = 0; s1[i] != '\0'; i++) { if (s1[i] != s2[i]) { break; } } if (s1[i] == s2[i]) { return 0; } else { return 1; } } これで実行したところ、「input str1 =」の右のスペースが文字化け?してしまいます。(半角カタカナや記号が出る)ただ、その後に文字列を入力すると、正しく機能します。 これは何が悪いなのでしょうか、どなたか教えてください。

専門家に質問してみよう