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ですか?
C言語の文字列のメモリ上の取り扱いについて
Re: C言語の文字列のメモリ上の取り扱いについて
コードはcodeタグで囲み、きちんとインデントをしていただくと、見やすくてありがたいです。
実際に出力されるコードを見てみましょう。コメントは私が補いました。
アドレスを指定してスタック領域の任意の場所にアクセスできます。
文字列リテラルが書き換えられない理由は、申し訳ないですが私には良くわかりません。
少なくともプログラムやグローバル変数、static変数のデータと同じRAMに格納されているはずです。
おそらく、OSによって書き換え禁止がセットされているのでしょう。
実際に出力されるコードを見てみましょう。コメントは私が補いました。
.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
コンパイラがaの大きさを記憶し、定数で出力しているようです。kawakami さんが書きました:char a[]でa が配列の大きさを持っていますが、a にはアドレスが入るのでコンパイル時にaの大きさを記録した変数が裏で生成されているのでしょうか?
確かにスタック領域を使用しますが、この場合のスタックは単にメモリ領域の使い方の話なので、kawakami さんが書きました:一般的にローカル変数はスタックに入るというのですがCASL2を今のところ勉強した限りスタックとは別の関数に飛ぶ時の元の関数の変数の避難場所であってスタックに自動変数を入れるとLIFOみたいになるので大変になるのではないでしょうか?
アドレスを指定してスタック領域の任意の場所にアクセスできます。
これは正確ではありません。例えば、このコードでb=a;とすれば、bが指している文字列を変更できるはずです。kawakami さんが書きました:char *bでは文字列を変更できないとなっていますがなぜでしょうか?書き換え不可メモリ部分って何でしょうか?ROMですか?
文字列リテラルが書き換えられない理由は、申し訳ないですが私には良くわかりません。
少なくともプログラムやグローバル変数、static変数のデータと同じRAMに格納されているはずです。
おそらく、OSによって書き換え禁止がセットされているのでしょう。
オフトピック
また怒られそう…
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)
Re: C言語の文字列のメモリ上の取り扱いについて
御返事有り難うございます。
質問の件についてはおおよそ理解できたのではないかと思います。
アセンブラからの説明で、アセンブラを自分も学習する必要があるのではないかと思えました。
情報工学の学生でも何でもないのですが、このままではこの他にも質問ばっかり増えていきそうですし、本格的に本格的にアセンブラの勉強をする覚悟をしました。
ありがとうございます。
質問の件についてはおおよそ理解できたのではないかと思います。
アセンブラからの説明で、アセンブラを自分も学習する必要があるのではないかと思えました。
情報工学の学生でも何でもないのですが、このままではこの他にも質問ばっかり増えていきそうですし、本格的に本格的にアセンブラの勉強をする覚悟をしました。
ありがとうございます。
- softya(ソフト屋)
- 副管理人
- 記事: 11677
- 登録日時: 14年前
- 住所: 東海地方
- 連絡を取る:
Re: C言語の文字列のメモリ上の取り扱いについて
それは本末転倒の様な。本来は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
みけ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(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。
Re: C言語の文字列のメモリ上の取り扱いについて
a にアドレスが入るというのも誤りです。
配列名は基本的に先頭要素へのポインタです。
&a[0]と書くのと同じことです。
ただしアドレス演算子やsizeof演算子のオペランドのときは、配列全体を表します。
そしてsizeof演算子で得られる配列全体のサイズはスコープ内で宣言されたサイズです。
異なるファイルスコープでextern付きで実体と異なる要素数で宣言すると、実体とは異なる宣言どおりのサイズを返します。
配列名は基本的に先頭要素へのポインタです。
&a[0]と書くのと同じことです。
ただしアドレス演算子やsizeof演算子のオペランドのときは、配列全体を表します。
そしてsizeof演算子で得られる配列全体のサイズはスコープ内で宣言されたサイズです。
異なるファイルスコープでextern付きで実体と異なる要素数で宣言すると、実体とは異なる宣言どおりのサイズを返します。
Re: C言語の文字列のメモリ上の取り扱いについて
[quote="softya(ソフト屋)"]それは本末転倒の様な。本来はC言語の規格書を確認すべきです。
みけCAT さんの方法はご本人も怒られる書いてますが環境依存な仕様がどうなっているか確認できません。
つまり、特定の環境・条件での特定のコンパイラの動作が確認出来るだけです。
そうですよね、未定義だから。は結論として正しいのではないかと思います。
教えていただいたURLの話もかなり参考になりました。
しかしスタックに自動変数が入る部分はどうもアセンブラを読めるようにならないとと理解できそうにないかなと思っています。
CASLで少し勉強をしたし、正直C言語に関しては本末転倒と言っていいほどの時間を奪われた気がするので(最初にPythonを勉強したり、CASLから入ったらもうちょっと違ったかもしれません)
やけっぱち気味にもう少し深入りしてもいいかなと思っています。
みけCAT さんの方法はご本人も怒られる書いてますが環境依存な仕様がどうなっているか確認できません。
つまり、特定の環境・条件での特定のコンパイラの動作が確認出来るだけです。
そうですよね、未定義だから。は結論として正しいのではないかと思います。
教えていただいたURLの話もかなり参考になりました。
しかしスタックに自動変数が入る部分はどうもアセンブラを読めるようにならないとと理解できそうにないかなと思っています。
CASLで少し勉強をしたし、正直C言語に関しては本末転倒と言っていいほどの時間を奪われた気がするので(最初にPythonを勉強したり、CASLから入ったらもうちょっと違ったかもしれません)
やけっぱち気味にもう少し深入りしてもいいかなと思っています。
Re: C言語の文字列のメモリ上の取り扱いについて
>そしてsizeof演算子で得られる配列全体のサイズはスコープ内で宣言されたサイズです。
異なるファイルスコープでextern付きで実体と異なる要素数で宣言すると、実体とは異なる宣言どおりのサイズを返します。
初耳ですが重要そうな部分でないかと思います。規格書を頑張って紐解いてみます。
異なるファイルスコープでextern付きで実体と異なる要素数で宣言すると、実体とは異なる宣言どおりのサイズを返します。
初耳ですが重要そうな部分でないかと思います。規格書を頑張って紐解いてみます。