• ベストアンサー

C言語の型と配列

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

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

  • ベストアンサー
回答No.9

#8 さんへ >strは左辺値ですが、配列型ですので代入できません。 代入できないのは「左辺値ではない」からです。 宣言のときの char* str[10]={"a","b"}; str は、配列型ですが、式として参照されるとき、str はもはや配列型ではありません。 str は要素型へのポインタ、すなわち、 char のポインタへのポインタ(アドレス)です。 式として記述された str が配列型を持つのは、 ・&演算子のオペランド ・sizeof演算子のオペランド のときだけです。 int a[10];b=1; a = &b; a の型は int へのポインタ型であって配列型ではありません。代入できないのは、a が「左辺値ではない」からです。早い話、b のアドレスを格納するメモリがない(左辺値ではない)からです。 >str2=str;であれば、エラーになりません。 そうだったかもしれません。 何か、回答者の答えが、質問者をよけい混乱させているかもしれません(私も含め)。

ainobakuda
質問者

お礼

何度も回答していただきありがとうございます。 他の回答者のミスを指摘してもらいどっちが正しいのか考えるとより理解が深まりました。 ポインタと先頭アドレスがごっちゃになってたみたいです。 &でっていうのがとてもわかりやすかったです。 本当にありがとうございました!

その他の回答 (9)

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.10

現在の C の規格に従えば配列は左辺値です. ただし左辺値にも「変更できる左 辺値」と「変更できない左辺値」の 2種類があり, 配列は後者の「変更できない 左辺値」であるとなっています. また, 式中に表れた配列名が表すのはあくまで「配列」です. ・アドレスをとる単項演算子の & ・sizeof のオペランドになっていないときには, 自動的に「当該配列の先頭をさすポイン タ」に変換される, というだけです. 関数名も同様で, 単項 & と sizeof のオペランドになっていないときには自動 的に「当該関数の先頭アドレス」に変換されます. あくまで「現在の C の規格にてらして正しい解釈」はこうなっている, という だけですが.

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.8

strの型はchar*[10]、str2の型はchar*です。 strは左辺値ですが、配列型ですので代入できません。 これは、 int a[10]; a = 123; とできないのと同じです。 str2=str;であれば、エラーになりません。 Cでは、オブジェクト型へのポインタどうしは暗黙的に型変換されます。 strは配列型ですが、代入演算子の右オペランドに指定した場合には先頭要素へのポインタ型に暗黙的に変換されます。 すなわち、strの型は、 char*[10] → char** → char* の順に暗黙的に型変換されます。 ただし、C++ではエラーになります。 この辺の区別がついていない人が多いので要注意です。

ainobakuda
質問者

お礼

回答ありがとうございます。 確認したところ確かにC++ではエラーになり、 Cでは警告が出るだけでコンパイルできました。 Cで出来てC++で出来ないこともあるんですね。 暗黙的に変換されるというのはまったく知りませんでした・・。 というより今までちゃんと理解できてなかったみたいです。 ありがとうございました。

回答No.7

#6 さんへ >str = "ABC"; ○ 本当?str は左辺値(l-value)ではないので代入できないと思うのだが? *str = "ABC"; ○ ならばわかります。 >str これは配列の先頭アドレスです。 実際には同じアドレスですが、意味は微妙に違います str => 配列の0番目の要素のアドレスです。 &str => 配列の先頭アドレスです。 >str=str2;とはまさに定数に変数を代入しようとする行為です。 そもそも、str には代入不可。というのが私の見解です。

  • nda23
  • ベストアンサー率54% (777/1415)
回答No.6

変数に定数は代入できます。 str = "ABC"; ○ 右辺は文字列へのアドレスで、リンク時に 確定する数値です。 定数に変数や他の定数を代入できません。 123 = str; × こんなことは理由の説明いらないですよね。 char *str[10]; これはポインタが10個あるということです。 >strは char * を10個持つ配列をさすポインタ  違います。str これは配列の先頭アドレスです。 つまり、定数です。str=str2;とはまさに定数に 変数を代入しようとする行為です。できなくて 当然ですよね。 char2がどんな型の変数であろうと定数であろうと コンパイラがどんなに進化しようと代入できません。 >str2はchar *をさすポインタ これをするなら以下のような定義です。 char **str2; または char *str2[]; そこで str2 = str; これならOKです。

ainobakuda
質問者

お礼

とてもわかりやすく説明していただきありがとうございます。 ポインタと先頭アドレスがごっちゃになってたみたいですね・・。 すっきりしました。 本当にありがとうございました。

  • morchin
  • ベストアンサー率16% (212/1281)
回答No.5

char *str[10]は、char *型10個の配列型 char *str2は、char *型 なので、違う型同士、代入はできませんよ。 str[0] = str2; は、同じ型なので、できますが。

ainobakuda
質問者

お礼

回答ありがとうございました。

回答No.4

CコンパイラーがPCにインストールされていないので確かめられませんが、 str2 = str; もエラーとなるのでは? str2=(char *)str; とするとコンパイルエラーは消えそうが、意図しないことが起こりそうです。 意味のある代入としては、 str2 = str[0]; str2 = str[9]; str2 = *str; str2 = *(str+9); など。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.3

あそっか, str を代入の左辺に書いたらまずいわな. str2=str; と勘違いしてた. さんきゅーです>#1. ちなみに 「この例だと strは char * を10個持つ配列をさすポインタ」 というのは間違い. str は「10個の char * からなる配列」とするのが正しい.

ainobakuda
質問者

お礼

回答ありがとうございました。 確かに配列の言い方が正確じゃありませんでした・・。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.2

C ならできていいはずだけど.... とりあえず, 使っているコンパイラと「正確な」メッセージを書いてもらえませんか? もっとも, 「大丈夫」ではあってもあまりほめられたスタイルじゃない.

ainobakuda
質問者

補足

遅くなり申し訳ありません。 一応 コンパイラはcygwin上でgcc 3.4.4を使っています。 error: incompatible types in assignment です。

回答No.1

str : char へのポインタのポインタ str[0] : charへのポインタ str2 : charへのポインタ str[0] = str2; ならエラーは出ない。 それに、str は、左辺値(l-value)ではないので、代入不可。 遠い記憶なので違っていたらすいません。

関連するQ&A

  • C言語の型キャスト

    C言語入門者です、よろしくお願いします C言語の型キャストについて教えてください 参考サイトで以下のようになってました char str[8] = "ABC"; char *ptr; ptr = (char*)str; printf("char型ポインタのアドレス先の値は: %s\n", ptr); この型キャストって意味ありますか? キャストしようがしなかろうがptrに収まるのはstrの先頭アドレスですよね? ただ型キャストを説明するためだけにこのような形にしているのか 意味がある型キャストなのかが解らず迷ってます

  • 2次元配列とポインタ

    char str1[][6]={"one","two","three","four","five"}; char *pt1; pt1 = str1; //(1) 上記の記述では(1)で型エラーが発生します。 ここを pt1 = *str1; //(2) と変更するとOKになるのですが、 (1)にすれば配列名だけでstr1先頭のアドレスがポインタpt1にセット されると思うのですが、なぜダメなのでしょうか?

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

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

  • C言語

    以下のC言語のプログラムを教えてください。 お願いします。 (1)標準入力から文字列(2 文字以上)を入力し,文字数を計上すると共に,入力された文字列の逆順に入れ替える処理を実現してください.なお,以下の要件を満たしたプログラムを作成してください. ・ 入力された文字列は,char 型の配列(要素数50)で受け取ること ・ 文字数を計上するcount 関数(引数:配列のアドレス,戻り値:文字数)を定義 し,main 関数より呼び出すこと ・ 文字列を逆順に入れ替えるreverse 関数(引数:配列のアドレス,戻り値:無し) を定義し,main 関数より呼び出すこと ・ 標準出力の処理は,main 関数で記述すること 【プロトタイプ宣言】 int count(char *str); void reverse(char *str); 【実行結果】 文字列を入力してください(2 文字以上) apple 文字数 = 5 入れ換え前 apple 入れ換え後 elppa (2)char 型の配列(要素数50)を2 つ宣言し,標準入力から2 つの文字列を入力してください.そして,格納した字列を入れ替える関数(swapstr 関数)を作成し,入れ替え前と入れ替え後の配列内の値(文字列)を配列名とともに標準出力するプログラムを作成してください. 【プロトタイプ宣言】 void swapstr(char *str1, char *str2); 【実行結果】 2 つの文字列を入力してください apple strawberry 入れ換え前 配列str1 = apple 配列str2 = strawberry 入れ換え後 配列str1 = strawberry 配列str2 = apple

  • C言語の基礎的な質問---文字配列の初期化

    C言語の配列の初期化に関する質問です。 もし規格によって回答が異なる場合は、ANSIのCということにしてください。 関数の中に、 char str[ ]="ABC"; (イ) という宣言があるとします。(staticは付きません。) これは、 char str[ ]={'A', 'B', 'C', '\0'}; (ロ) と全く同じ意味でしょうか。  似て非なるものに char *str="ABC"; (ハ) というものがあります。この場合は、 strとは違うところに"ABC"('C'の次には'\0'があります。)という領域が確保されていて、 その先頭アドレスでstrが初期化されるのですよね。 (イ)(ロ)(ハ)のいずれの場合も関数の中に書かれているとすれば、 いずれもstrは自動変数で、関数実行時にstrの領域が確保されますよね。 (イ)は配列strの領域が確保されるときに、 配列strとは別のところにある"ABC"という領域の内容を、コピーして設定する、 ということでしょうか。 (ロ)は、配列の領域確保時にstr[0]を'A'で、str[1]を'B'で、str[2]を'C'で、str[3]を'\0'で、初期化する、 ということで、 配列とは別のところには"ABC"という領域はない、 という考えでよろしいでしょうか。 もしそうだとしたら、配列とは別のところに"ABC"という領域があるかどうかという点で(イ)と(ロ)は異なることになりますが、そう考えてよろしいのでしょうか。 それとも、そういうことは処理系に依存することなんでしょうか。

  • 構造体配列のポインタについて(H17春基本情報試験C言語問題)

    平成17年春基本情報技術者試験の午後問題の問6について、質問があります。 設問dの答えがイなのですが、これがどうしても理解できません。 関数「int word_width(char *str, CHARPROF *char_list)」の「char_list」は構造体の配列のポインタとして宣言されています。 そしてこの関数内の処理を問う穴埋めdの解答は「*str != char_list[idx].char_p」となっています。 char_listは構造体配列のポインタなので、「char_list[idx]->char_p」あるいは「(*char_list[idx]).char_p」となるのでは?と思ったのですが、選択肢にもそれはありません。 char_list配列の先頭アドレスの中身を見るときは「char_list->char_p」となっても、要素番号がつくと、ポインタとはみなさなくなって「char_list[0].char_p」となるのでしょうか? だとするとポインタ宣言したchar_listとポインタ宣言しなかった場合(「CHARPROF char_list」のように普通に変数宣言?した場合)の「char_list[0].char_p」の違いがよくわからないのですが・・・。 念のため、この関数word_widthと構造体CHARPROFを転記します。 アドバイス、よろしくお願いします。 typedef struct {  char char_p; /* 文字 */  int char_w; /* 文字幅(ポイント数) */ } CHARPROF; int word_width(char *str, CHARPROf *char_list) {  int print_w = 0, idx;  while (*str != '\0') {   for (idx = 0; (ここに穴埋め「d」が入る); idx++);   print_w += char_list[idx].char_w;   str++;   }   return print_w; }

  • 配列で混乱しています

    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言語 型変換について

    c言語 型変換について 下記のように文字コードは、unsigned int型('B')をunsigned char 型(str[1] ) 格下げする型変換する規則を教えてください。 *質問ソースプログラム: int main(void) { char str[4]; /* 文字列を格納する配列 */ str[0] = 'A'; /* 代入 */ str[1] = 'B'; /* 代入 */ ・・・・・・ printf("size B %u\n",(unsigned)sizeof('B')); printf("size str[1] %u\n",(unsigned)sizeof(str[1])); * 実行結果 size B 4 size str[1] 1

  • C言語のint型の配列が分かりません

    #include<stdio.h> int main(void) { int str[ ]={0,1,2} printf("%s\n", str); return 0; } というプログラムをC言語でつくってみましたが動きません.(012と表示されて欲しかったのですが) int str[ ]={1,2,3}の部分をchar str[ ]={'0','1','2'}とすれば動きます. そこで質問なのですが, printf("~%s~", (配列名));  はchar型の配列にしか適応できないのですか? ※追記 puts関数の定義は int puts (const char *str); であるそうなので char型の仮引数にはchar型のアドレスを渡さなければいけません. ではprintf関数の定義は一体どんなものなのですか?

  • C言語のポインタと配列について

    下のコードについて2つ質問があります。 (1) char *p; p = str; ならわかるのですが、なぜ型が違う char *p = str; のような代入がOKなのでしょうか。文字列をコピーするときの   *p = *str; と比較して、とても違和感があります。  str は引数で受けた文字列の先頭アドレス。 (2)   char *str;   str = " 1 23 4 5"; と   char str[] = " 1 23 4 5"; との違いがよくわかりません。 #include <stdio.h> #include <ctype.h> void TrimSpace(char *str) {   char *p = str;        // (1)型が違うのになぜこんな代入をするのか?      while (*str != '\0') {     if ( !isspace(*str) ) {       *p = *str;      // この代入は自然       p++;     }     str++;   }   *p = '\0'; } int main(void) {      //char *str;   //str = " 1 23 4 5";     (2)これでは空白を詰めた後の文字列が表示されないのはなぜか?      char str[] = " 1 23 4 5"; //これで OK   printf("空白を詰める前:\"%s\"\n", str);   TrimSpace(str);   printf("空白を詰めた後:\"%s\"\n", str);   return 0; }

専門家に質問してみよう