dポイントプレゼントキャンペーン実施中!

C言語文法のうち、gcc 拡張機能を含まない C89 規格で、
文字列の「= での代入」は可能なんでしょうか?
strcpy が必要だと思っていました。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
char *p;
p=malloc(30);
p="Hello";
printf("%s\n",p)
return 0;
}

gcc で
gcc -Wall -std=c89 -pedantic-errors としてコンパイルしても
なんのエラー表示もなく正常に動作しています。
これまでC89では「=での文字列代入」は(宣言時を除いて)できない
と思ってたので不思議です。
ちなみに、free(p) を return 0; の前に入れると、エラーになります。
情報処理技術者試験(試験で要求される規格はC89)を受ける知り合い
から質問されたのがきっかけです。

A 回答 (8件)

C89でもC99でも、あるいはC++でも事情は変わりません。


提示されたプログラムが何をやっているかを表すと、おおむね次のようになります。

#include <stdio.h>

void *malloc(size_t size)
{
 static char __memory[30];
 return __memory;
}

static char __hello[] = "Hello";

int main(void)
{
 char *p;
 p=malloc(30);
 p=__hello;
 printf("%s\n",p)
 return 0;
}

こんなイメージです。
mallocが返したのは__memory[0]へのポインタですが、その後の代入では__hello[0]へのポインタをpに格納しています。
決して文字列がコピーされたわけではありません。
    • good
    • 0
この回答へのお礼

ありがとうございます。
提示してくださったプログラムからメモリ使用の状況を、
はっきりと理解できました。
私が混乱し、また、誤解していたのは、

static char __hello[] = "Hello";

ここでした。メイン関数の外で宣言(静的なメモリ確保)されている
のに、メイン関数の中で宣言されていると勘違いしてました。
そのためメイン関数内のブロック途中で(p="Hello";)
静的なメモリ確保を行っていると判断し奇妙に感じていました。
提示していただいたようにメイン関数の外で宣言されているのなら、
ブロック冒頭に宣言(通常は静的なメモリ確保はここのみ)を
区分けしなければならないとするC89の考え方と矛盾しません。
理屈が通らない(規格の方針と異なる例外が多い)のが
C言語かと思い始めていましたので精神的にも助かりました。

お礼日時:2009/10/06 05:42

え? #5 の


char *a = "Hello";
a = "World"; // ポインタ変数にポインタを代入しているので問題なし。
で「処理系によって動作が違う」ことはあり得ないんだけど. そのページでも「動作が違う」ようには読めなかったんだけど, どのへんにそんなことが書いてありますか?
最初に戻っちゃうんだけど, たぶん「文字列」とか「文字列代入」とかを何も考えずに使ってるんじゃないかな. だから混乱する.
C には「文字列型」というデータ型は存在しないし, したがって「文字列代入」という操作も本質的には存在しません.
    • good
    • 0
この回答へのお礼

ありがとうございます。
文字列型がないことは知っていましたし、だからこそ
メモリ確保を文字列リテラルが行っていることが、
malloc 以外の方法での静的なメモリ確保と同等と理解していました。
しかし、皆さんのご指摘でわかったことは、
char a[]="Hello";
での左辺の char a[6] に相当する静的なメモリ確保と、
右辺の "Hello" が文字列リテラルとして別途確保する
(静的な)メモリ確保は種類が異なる。
また、C89 で規定されるブロック先頭でまとめて行うべき
静的なメモリ確保に右辺の文字列リテラルでの「どこか」に
確保するメモリ確保動作は該当しない、
ということなんですね。
勉強になりました。

お礼日時:2009/10/05 15:22

char *a = "Hello";


a = "World"; // ポインタ変数にポインタを代入しているので問題なし。
こっちの件は、単なるアドレスの代入しなおし。
int a=1;
a = 2;
と一緒。

http://www9.plala.or.jp/sgwr-t/c/sec10-3.html
の件はstrcpyなので話がまったく別。
メモリのどこかにある"ABC"と書かれたメモリを"DEF"に書き換えるって事。処理系によってはメモリ書き換え禁止領域にあるので、書き換えるとメモリ保護例外で異常終了する。
まぁ、書き換えられる処理系でもバグの原因なので、禁断の技。

常のポインタの指す先のメモリが何処にあるか意識しないとC言語のプログラムはバグだらけになるので、ちゃんと理解してください。
    • good
    • 0
この回答へのお礼

了解しました。
文字列リテラルの文字列分のメモリ確保(「どこかに確保される」)
と、
char a[]="Hello";

が同じ種類だと思ってました。
a[0],a[1]に、'H','e'がそれぞれ入る事の他に、
この例では使われませんが、
"Hello"の文字列が「どこか」に、別途メモリ確保されるんですね。
で、このことは「宣言」とは呼ばない、と。

なので、char a[]="Hello";
よりは char *p="Hello";の方が「どこか」に確保された
メモリの先頭をpが指すので、メモリ節約にもなる、と。

お礼日時:2009/10/05 15:14

配列とポインタの違いがわかってないのではないでしょうか


char a[30] = "Hello"; //配列の初期化の場合のみ文字列リテラルの扱いが違う
a = "World"; //初期化以外で文字列の代入は出来ない
はもちろんエラーですが
char *a = "Hello";
a = "World"; // ポインタ変数にポインタを代入しているので問題なし。

この回答への補足

ありがとうございます。
ただ、最後の例は混乱しております。
http://www9.plala.or.jp/sgwr-t/c/sec10-3.html
にあるように、処理系によって動作が違うという記述もあるようです。

補足日時:2009/10/05 11:24
    • good
    • 0
この回答へのお礼

どうもありがとうございます。
文字列リテラルの特殊性(メモリを文字列分確保していても
a[]="Hello"; のような malloc を使わない静的なメモリ確保とは
異なる)を私ががわかっていませんでした。

お礼日時:2009/10/05 15:30

文字列リテラルの型はC89より前からchar *型だったと思いますが。

ですから、char *型のp変数に代入することは不思議でも何でもないです。
char *str = "hello";
p=str;
とか
char str[10] = "hello";
p=str;
と書くのは良いんでしょうか?
p="hello";
が良くないのは何が違うからでしょうか?

printf( "%d", 1 );
とか気にせずに書いてますよね。
printfの第一引数の型を調べてみてください。

>char a[30]="Hello";
これは初期化ですから意味がぜんぜん違います。
どちらかと言うとCの文法の中でも、こちらの書き方の方が特殊なんです。初期化の=と代入の=を同じに扱ってはダメです。

この回答への補足

ありがとうございます。
char *str ="hello";
char str[10]="hello";
がブロック冒頭の宣言部分に書かれている場合は、
私もおかしくないと思います。

ご説明で、何がわかっていないかがはっきりしてきました。
char *str="hello";
と書いた場合と、
char *str;
この間に他の命令文
str="hello";
とわけた場合は、
「=」の意味が同じものなのか?ということです。
他の方のご説明で、これらは全く異なるということで
理解できました。

補足日時:2009/10/05 11:34
    • good
    • 0
この回答へのお礼

>char *str="hello";
>と書いた場合と、
>char *str;
>この間に他の命令文
>str="hello";
>とわけた場合は、
>「=」の意味が同じものなのか?
は、結局、「同じものだ」、と理解できました。
ありがとうございました。

お礼日時:2009/10/05 15:25

文字列リテラルの型はC89より前からchar *型だったと思いますが。

ですから、char *型のp変数に代入することは不思議でも何でもないです。
char *str = "hello";
p=str;
と書くのは許せても
p="hello";
が許せないのは意味不明です。

printf( "%d", 1 );
とか気にせずに書いてますよね。
printfの第一引数の型を調べてみてください。

>char a[30]="Hello";
これは初期化ですから意味がぜんぜん違います。
どちらかと言うとCの文法の中でも、こちらの書き方の方が特殊なんです。初期化の=と代入の=を同じに扱ってはダメです。
    • good
    • 0

結論から言うと質問者さんの勘違いです


文字列の代入は出来ていません。
p=malloc(30);
では、30バイトのメモリを確保して先頭アドレスをpに代入しています。
p="Hello";
では、mallocしたアドレスは捨てて"Hello"の文字列リテラルの先頭アドレスが代入されているだけです。
つまり文字列が転送されるのではなくて、文字列の先頭アドレスが変わっているだけなんです。

なのでアドレスを表示してみると分かります。
p=malloc(30);
printf("p=%p(malloc)\n", p);
p="Hello";
printf("p=%p(hello)\n", p);

>ちなみに、free(p) を return 0; の前に入れると、エラーになります。
mallocして無いメモリをfreeしたらエラーになるのは当たり前です。

この回答への補足

詳しい説明ありがとうございます。

「"Hello"の文字列リテラルの先頭アドレスが代入されているだけ」

は、C89 で可能なんでしょうか?
char a[30]="Hello";
がプログラム冒頭にある場合は「初期化」として可能だと記憶しています。けれど、今回のような実行文途中、

char *p;
色々な実行文
p="Hello";
が可能なんでしょうか?

C99 や gcc 拡張機能では可能なような気がしますが、
C89 では不可能だと思ってたんです。
C89 に厳格に従わない場合はエラーとする
-pedantic-erros が機能しない例なんでしょうか?

補足日時:2009/10/05 09:35
    • good
    • 0

変数とは別の領域に作られた


【Hello】
のアドレスを代入してるのでは?
(mallocで確保した領域とは別)

書き換えできない領域だから、freeでエラーが出るのだと思う
    • good
    • 0
この回答へのお礼

ありがとうございます。
確かに、そうだと思います。

他の方のご説明もあわせて理解できたのは、
malloc 等は全く関係なく、

p="Hello";
のような実行文がC89では冒頭の宣言部分ではなく、
実行文の途中で可能なのか?

が、知りたい質問だとわかりました。

お礼日時:2009/10/05 09:34

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!