C言語の文字列のメモリ上の取り扱いについて

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
kawakami
記事: 4
登録日時: 12年前

C言語の文字列のメモリ上の取り扱いについて

#1

投稿記事 by kawakami » 12年前

Cの勉強をしている中で
#include <stdio.h>

int main(){
char a[] = "this is char a[]\n";
printf("%s",a);
printf("char a[] pointer size is %d\n",sizeof(a));
a[0] = 'z';
printf("%s",a);
char *b = "this is char *b\n";
printf("%s",b);
printf("char *b pointer size is %d\n",sizeof(b));
*b = 'z';
printf("%s",b);
char *c = a;
printf("%s",c);
printf("char *c pointer size is %d\n",sizeof(c));
return 0;
}
というプログラムはchar a[]でa が配列の大きさを持っていますが、a にはアドレスが入るのでコンパイル時にaの大きさを記録した変数が裏で生成されているのでしょうか?

一般的にローカル変数はスタックに入るというのですがCASL2を今のところ勉強した限りスタックとは別の関数に飛ぶ時の元の関数の変数の避難場所であってスタックに自動変数を入れるとLIFOみたいになるので大変になるのではないでしょうか?

char *bでは文字列を変更できないとなっていますがなぜでしょうか?書き換え不可メモリ部分って何でしょうか?ROMですか?

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: C言語の文字列のメモリ上の取り扱いについて

#2

投稿記事 by みけCAT » 12年前

コードはcodeタグで囲み、きちんとインデントをしていただくと、見やすくてありがたいです。

実際に出力されるコードを見てみましょう。コメントは私が補いました。

コード:

	.file	"c_str_memory.c"
	.def	___main;	.scl	2;	.type	32;	.endef
	.section .rdata,"dr"
LC1:
	.ascii "%s\0"
LC2:
	.ascii "char a[] pointer size is %d\12\0"
LC3:
	.ascii "this is char *b\12\0"
LC4:
	.ascii "char *b pointer size is %d\12\0"
LC5:
	.ascii "char *c pointer size is %d\12\0"
LC0:
	.ascii "this is char a[]\12\0"
	.text
	.globl	_main
	.def	_main;	.scl	2;	.type	32;	.endef
_main: # int main() {
LFB6:
	.cfi_startproc
	pushl	%ebp
	.cfi_def_cfa_offset 8
	.cfi_offset 5, -8
	movl	%esp, %ebp
	.cfi_def_cfa_register 5
	pushl	%edi
	pushl	%esi
	pushl	%ebx
	andl	$-16, %esp
	subl	$48, %esp
	.cfi_offset 7, -12
	.cfi_offset 6, -16
	.cfi_offset 3, -20
	call	___main
	leal	22(%esp), %edx # char a[] = "this is char a[]\n";
	movl	$LC0, %ebx
	movl	$18, %eax
	movl	%edx, %edi
	movl	%ebx, %esi
	movl	%eax, %ecx
	rep movsb
	leal	22(%esp), %eax # printf("%s",a);
	movl	%eax, 4(%esp)
	movl	$LC1, (%esp)
	call	_printf
	movl	$18, 4(%esp) # printf("char a[] pointer size is %d\n",sizeof(a));
	movl	$LC2, (%esp)
	call	_printf
	movb	$122, 22(%esp) # a[0] = 'z';
	leal	22(%esp), %eax # printf("%s",a);
	movl	%eax, 4(%esp)
	movl	$LC1, (%esp)
	call	_printf
	movl	$LC3, 44(%esp) # char *b = "this is char *b\n";
	movl	44(%esp), %eax # printf("%s",b);
	movl	%eax, 4(%esp)
	movl	$LC1, (%esp)
	call	_printf
	movl	$4, 4(%esp) # printf("char *b pointer size is %d\n",sizeof(b));
	movl	$LC4, (%esp)
	call	_printf
	movl	44(%esp), %eax # *b = 'z';
	movb	$122, (%eax)
	movl	44(%esp), %eax # printf("%s",b);
	movl	%eax, 4(%esp)
	movl	$LC1, (%esp)
	call	_printf
	leal	22(%esp), %eax # char *c = a;
	movl	%eax, 40(%esp)
	movl	40(%esp), %eax # printf("%s",c);
	movl	%eax, 4(%esp)
	movl	$LC1, (%esp)
	call	_printf
	movl	$4, 4(%esp) # printf("char *c pointer size is %d\n",sizeof(c));
	movl	$LC5, (%esp)
	call	_printf
	movl	$0, %eax # return 0;
	leal	-12(%ebp), %esp
	popl	%ebx
	.cfi_restore 3
	popl	%esi
	.cfi_restore 6
	popl	%edi
	.cfi_restore 7
	popl	%ebp
	.cfi_restore 5
	.cfi_def_cfa 4, 4
	ret
	.cfi_endproc
LFE6:
	.def	_printf;	.scl	2;	.type	32;	.endef
kawakami さんが書きました:char a[]でa が配列の大きさを持っていますが、a にはアドレスが入るのでコンパイル時にaの大きさを記録した変数が裏で生成されているのでしょうか?
コンパイラがaの大きさを記憶し、定数で出力しているようです。
kawakami さんが書きました:一般的にローカル変数はスタックに入るというのですがCASL2を今のところ勉強した限りスタックとは別の関数に飛ぶ時の元の関数の変数の避難場所であってスタックに自動変数を入れるとLIFOみたいになるので大変になるのではないでしょうか?
確かにスタック領域を使用しますが、この場合のスタックは単にメモリ領域の使い方の話なので、
アドレスを指定してスタック領域の任意の場所にアクセスできます。
kawakami さんが書きました:char *bでは文字列を変更できないとなっていますがなぜでしょうか?書き換え不可メモリ部分って何でしょうか?ROMですか?
これは正確ではありません。例えば、このコードでb=a;とすれば、bが指している文字列を変更できるはずです。
文字列リテラルが書き換えられない理由は、申し訳ないですが私には良くわかりません。
少なくともプログラムやグローバル変数、static変数のデータと同じRAMに格納されているはずです。
おそらく、OSによって書き換え禁止がセットされているのでしょう。
オフトピック
また怒られそう…
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

kawakami
記事: 4
登録日時: 12年前

Re: C言語の文字列のメモリ上の取り扱いについて

#3

投稿記事 by kawakami » 12年前

御返事有り難うございます。
質問の件についてはおおよそ理解できたのではないかと思います。
アセンブラからの説明で、アセンブラを自分も学習する必要があるのではないかと思えました。
情報工学の学生でも何でもないのですが、このままではこの他にも質問ばっかり増えていきそうですし、本格的に本格的にアセンブラの勉強をする覚悟をしました。
ありがとうございます。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: C言語の文字列のメモリ上の取り扱いについて

#4

投稿記事 by softya(ソフト屋) » 12年前

それは本末転倒の様な。本来はC言語の規格書を確認すべきです。
みけCAT さんの方法はご本人も怒られる書いてますが環境依存な仕様がどうなっているか確認できません。
つまり、特定の環境・条件での特定のコンパイラの動作が確認出来るだけです。

C言語の仕様。
「日本工業標準調査会:データベース-JIS詳細表示」
http://www.jisc.go.jp/app/pager?%23jps. ... ISNO=X3010

文字リテラルの書き換えに関しては次の議論をお読みください。
「激論中の文字列リテラル • C言語交流フォーラム ~ mixC++ ~」
http://dixq.net/forum/viewtopic.php?f=3&t=13395
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ISLe
記事: 2650
登録日時: 14年前
連絡を取る:

Re: C言語の文字列のメモリ上の取り扱いについて

#5

投稿記事 by ISLe » 12年前

a にアドレスが入るというのも誤りです。
配列名は基本的に先頭要素へのポインタです。
&a[0]と書くのと同じことです。

ただしアドレス演算子やsizeof演算子のオペランドのときは、配列全体を表します。

そしてsizeof演算子で得られる配列全体のサイズはスコープ内で宣言されたサイズです。
異なるファイルスコープでextern付きで実体と異なる要素数で宣言すると、実体とは異なる宣言どおりのサイズを返します。

kawakami
記事: 4
登録日時: 12年前

Re: C言語の文字列のメモリ上の取り扱いについて

#6

投稿記事 by kawakami » 12年前

[quote="softya(ソフト屋)"]それは本末転倒の様な。本来はC言語の規格書を確認すべきです。
みけCAT さんの方法はご本人も怒られる書いてますが環境依存な仕様がどうなっているか確認できません。
つまり、特定の環境・条件での特定のコンパイラの動作が確認出来るだけです。


そうですよね、未定義だから。は結論として正しいのではないかと思います。
教えていただいたURLの話もかなり参考になりました。
しかしスタックに自動変数が入る部分はどうもアセンブラを読めるようにならないとと理解できそうにないかなと思っています。
CASLで少し勉強をしたし、正直C言語に関しては本末転倒と言っていいほどの時間を奪われた気がするので(最初にPythonを勉強したり、CASLから入ったらもうちょっと違ったかもしれません)
やけっぱち気味にもう少し深入りしてもいいかなと思っています。

kawakami
記事: 4
登録日時: 12年前

Re: C言語の文字列のメモリ上の取り扱いについて

#7

投稿記事 by kawakami » 12年前

>そしてsizeof演算子で得られる配列全体のサイズはスコープ内で宣言されたサイズです。
異なるファイルスコープでextern付きで実体と異なる要素数で宣言すると、実体とは異なる宣言どおりのサイズを返します。

初耳ですが重要そうな部分でないかと思います。規格書を頑張って紐解いてみます。

閉鎖

“C言語何でも質問掲示板” へ戻る