ページ 11

C言語での文字列について(その2)

Posted: 2009年4月23日(木) 13:34
by march3
さきほどの質問に関することかもしれませんが、以下のプログラムがまともに動きません。
#include <stdio.h>

int main(void);

int main(void){
	char* string="hello";
	char* p;

	for(p=string; *p!='\0' ;p++){
		printf("p=%p : %c\n",p ,*p);
		if(*p=='l'){
			*p = 'L';//ここで強制終了してしまう
		}
		getchar();
	}
	return(0);
}
配列にするとうまくいくのですが、なぜなのでしょう?
#include <stdio.h>

int main(void);

int main(void){
	char string[/url]="hello";
	//char* string="hello";
	char* p;

	for(p=string; *p!='\0' ;p++){
		printf("p=%p : %c\n",p ,*p);
		if(*p=='l'){
			*p = 'L';
		}
		getchar();
	}
	return(0);
}
ご指導いただけると幸いです。

ちなみに環境はVineLinux gccです。

Re:C言語での文字列について(その2)

Posted: 2009年4月23日(木) 13:45
by Mist
上のプログラムは落ちるところで文字リテラルの内容を変更しようとしています。
文字リテラルは変数ではないので変更するようなことはしてはいけません。

下のプログラムは、書き換え可能な配列に文字リテラルの内容をコピーしたあと配列の内容を書き換えているだけなので問題ありません。

Re:C言語での文字列について(その2)

Posted: 2009年4月23日(木) 14:02
by 御津凪
Mist さんの説明をさらに深く説明すると、

文字リテラルが置かれている領域が読み取り専用領域となっている場合があります。

そのため、スタックに格納されない文字列(文字リテラル領域からの参照)の場合、
読み取り専用の文字列を書き換えようとしてエラーが発生しているものと思われます。
(VC++では書き換えられることができるように設定できます)

配列の場合については、 Mist さんの説明通りとなります。

Re:C言語での文字列について(その2)

Posted: 2009年4月23日(木) 14:22
by march3
Mistさん
>文字リテラルは変数ではないので変更するようなことはしてはいけません。
はぁ、そういうもんなんですか。
配列なら変更可能だけど文字リテラルは変更できないんですね...

御津凪さん
>VC++では書き換えられることができるように設定できます
VC++では設定変更で可能なんですか。(VC++も使ってきたけど初めて知った)

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

Re:C言語での文字列について(その2)

Posted: 2009年4月23日(木) 14:33
by 御津凪
補足です。

> VC++では設定変更で可能なんですか。(VC++も使ってきたけど初めて知った)

直接的ではないのですがそうなります、ということを付け加えておきます。

最適化オプションの中で、「文字列プール」というオプションがあるのですが、これが有効になると、
ソース中の重複した文字リテラルをひとつにまとめ、文字リテラルが読み取り専用になります。
(同じ文字リテラルを別々のコードから参照しても同じアドレスを指すようになります)

これが無効だと、それぞれの文字リテラルは別々に持ち、文字リテラルを変更することができます。
ただし文字列テーブルのように決められた領域で置かれているため、長さを変更することはできません。


基本はこの設定は無効ですが、デバッグ情報の形式および最適化設定によって有効になることがあります。

Re:C言語での文字列について(その2)

Posted: 2009年4月23日(木) 14:41
by hoge
> VC++では設定変更で可能なんですか。(VC++も使ってきたけど初めて知った)

GCCでも、古いバージョンなら、書きかえ可能にするオプションがあったはず。
-fwritable-strings だったかな。使ったことねぇけど。

Re:C言語での文字列について(その2)

Posted: 2009年4月23日(木) 15:11
by march3
試しに以下のプログラムを同じ環境で同じようにコンパイル、実行してみました。
/* strings.c */
#include <stdio.h>
int main(void);
int main(void){
    char* string = "hello";

    printf("string = %p\n", string);
    printf("hello = %p\n", "hello");
    return(0);
}

実行結果:
string = 0x8048480
hello = 0x8048480

御津凪さんのおっしゃるとおり、メモリ削減の最適化が行われていることが確認できました。

またhogeさんのおっしゃるとおり
----(プロンプトのコピペで失礼)----
% gcc -o strings strings.c -fwritable-strings
% ./strings
string = 0x8049474
hello = 0x8049487
----------------------------------
で、gccでも設定変更可能なようですね。