
1 #include <stdio.h>
2
3 void setstr(char *str){
4 str = "abc";
5 return;
6 }
7
8 int main(){
9
10 char *str;
11
12 setstr(str);
13
14 str[1] = 'E';
15
16 printf("str = %s\n", str);
17
18 return 0;
19 }
20
上記のプログラムの動きがいまいち理解できません。
(メモリの状態など)
16行目でprintfすると、結果は「str = 、E・」となります。
---まず、4行目でabcに対してメモリが確保されて、その先頭アドレスが
strに設定されます。
しかし、setstr関数を抜けた時点で、先ほど確保されたメモリは開放されて
しまう。(? ここは想像です。確証がありません)
main関数に戻ってきて、14行目で変更しているメモリは、abcがかつてあった
場所の"b"の部分。(str自体は何も変更されていないから)
16行目でprintfしているのだけど、なぜこの結果になるのかが分かりません。。
分かる方いましたら教えて下さい。上の文章では何を言っているのか分かりづらいとは
思いますが。。
説明には適宜行番号を使って頂いて構いません。
よろしくお願いします。
No.4ベストアンサー
- 回答日時:
デバッガgdbを使って、実際にどのアドレスを指すかを実験(後述)してみると、main()で定義している変数str($2で指示されるアドレス)と、setstr()の仮引数str($4で指示されるアドレス)は別物であることがわかります。
これは、cパラメータ受け渡し方がcall by valueである事によるものです。
なので、setstr()で文字列のアドレスを設定したと思っていても、main()のstrには影響してません(setstr()から戻った直後の$7の値をみると明らか)。
こんな風にデバッガで追っかけたり、アセンブラソース出力(gcc -Sとかcl.exe /FAとかで生成)を眺めてみると、感じがつかめると思います。
(gdb) list
1 void setstr( char *str ){
2 str = "abc";
3 return;
4 }
5 int main(){
6 char *str;
7 setstr( str );
8 str[1] = 'E';
9 return 0;
10 }
(gdb) b 7
Breakpoint 1 at 0x401ada: file a.c, line 7.
(gdb) run
Starting program: a.out
Breakpoint 1, main () at a.c:7
7 setstr( str );
(gdb) p str
$1 = 0x0
(gdb) p &str
$2 = (char **) 0x81ff88
(gdb) step
setstr (str=0x0) at a.c:2
2 str = "abc";
(gdb) p str
$3 = 0x0
(gdb) p &str
$4 = (char **) 0x81ff70
(gdb) n
4 }
(gdb) p str
$5 = 0x405048 "abc"
(gdb) p &str
$6 = (char **) 0x81ff70
(gdb) n
main () at a.c:8
8 str[1] = 'E';
(gdb) p str
$7 = 0x0
(gdb) p &str
$8 = (char **) 0x81ff88
(gdb) p &str[1]
$9 = 0x1 <Address 0x1 out of bounds>
(gdb) n
Program received signal SIGSEGV, Segmentation fault.
0x00401aec in main () at a.c:8
8 str[1] = 'E';
(gdb) quit
%
ご回答ありがとうございます。
回答者さんの環境では、セグメンテーションフォールトが
おきるのですね。私の環境(BCC)では、おきませんでした。
これは置いておいて・・・
指摘された内容を見て、値渡しがダメなので参照渡しにして
みたら期待した動作になりました。
void setstr(char *str){
↓
void setstr(char *&str){
よく考えてみたら、4行目で、str自身を書き換えてますね。。
動作するようにはなりましたが、setstr内で登場した"abc"の部分の
メモリの扱いはどのようなものなるのかが謎です。
mainに制御が戻った後で、自然にこの部分が上書きされて
しまわないのかとか。。mallocで領域を確保したわけじゃないしなあ、とか。。
そもそもこのプログラムの書き方はおかしいのかな。。
No.13
- 回答日時:
>>ANo12
プロセスが終了した時に確実にメモリが解放されるならメモリリークなんて起きないでしょ。
実際AmigaOSはプロセスが終了しても確保したメモリは自動では解放されません。
No.10
- 回答日時:
>同一スコープで確保・開放されることが
>正しいということでしょうか。
正しいというか、その位に抑えておかないと私はメンテナンスできない。
>メモリの確保・開放は関数をまたいで、
>どこでもできるので、このような方針が
>あることは知りませんでした。
どこでもできる、ということは裏を返せばどこでやっているのか
すぐにわからなくなるということです。
No.8
- 回答日時:
想像ですが・・・
10行目で宣言されたstrはポインタですので、アドレスを入れる箱は準備されますが、その中身は「どこだかわからないアドレス」です。
普通はここで新しく領域を確保してちゃんとしたアドレスを入れます。
12行目でstrstr関数を呼ぶと、まずstrの値(どこだかわからないアドレス)をスタックにコピーしてstrstrの本体を実行します。
3行目のstrstrは、スタックからstrの中身(どこだか分からないアドレス)を作業メモリにコピーします。(コピーしないかもしれませんが話は同じです)
4行目では、"abc"が格納されているアドレスが作業メモリに上書きされます。
5行目のreturnは、strstr関数の返値がvoidなので何もせずに呼ばれたプログラムに戻ります。
14行目ではstrの中身(どこだか分からないアドレス)の次のアドレスに'E'を格納します。(ここでOSが止まったりします)
参照渡しだとstrのアドレスを渡すので一応動くと思いますが、"abc"が格納されている領域が書き換えできないような場所だったりするとまずいです。
この回答への補足
ご回答ありがとうございます。
> 14行目ではstrの中身(どこだか分からないアドレス)の次の~~
ここまではあれからいろいろ考えて私も同じことを考えました。
最後の一文についてですが、
> "abc"が格納されている領域が書き換えできないような場所
上記のような場所に領域が確保されることはあるのでしょうか?
現状では、書き換え可能な領域にメモリが確保されているようです。
このプログラムのように、setstr関数の中で、
メモリを確保しないプログラムでも、そもそもご法度な書き方
なのでしょうか。もしそうなると下記のプログラムも
不正ということになるんでしょうか?
(動作確認済みです。一応問題なく動作しました)
文字列・ポインタ辺りは、文法が難しいです^^;
#include <stdio.h>
int main(){
char *str;
str = "abc";
str[1] = 'B';
printf("str = %s", str);
return 0;
}
No.7
- 回答日時:
例によって他の人の回答は読んでませんが、
普通 setstr() のような関数を作って、文字列を格納する処理を作成する場合、
格納するための場所は関数を呼び出す側、この場合では main で確保するのが普通です。
setstr() のプロトタイプ宣言は質問文にあるままで結構です。
呼び出し側で適切に領域が確保されていることを前提として、その先頭アドレスを引き数にもらうのは普通の設計です。
setstr() の中で領域を確保して、呼び出し側でその領域を開放させるのは悪い設計です。
普通のプログラマは現在のスコープで確保した領域のことを覚えていたとしても、setstr() の中で確保される領域のことまでは気にしません。
ご回答ありがとうございます。
同一スコープで確保・開放されることが
正しいということでしょうか。
メモリの確保・開放は関数をまたいで、
どこでもできるので、このような方針が
あることは知りませんでした。
No.5
- 回答日時:
結果としては
str = aEc
を期待していたわけですよね?
それを実現するには以下のコードで可能です。
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 void setstr(char **str){
5 *str = malloc(4);
6 sprintf(*str, "abc");
7 return;
8 }
9
10 int main(){
11
12 char *str;
13
14 setstr(&str);
15
16 str[1] = 'E';
17
18 printf("str = %s\n", str);
19
20 return 0;
21 }
この回答への補足
ご回答ありがとうございます。
解放の話はそれはそうとして、、
3 void setstr(char *&str){
4 str = "abc";
5 return;
6 }
上記のような文字列の設定の仕方は認められていないのでしょうか?
一応期待した動作になります。
自分でもあんまりみたことありませんが。。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
コマンドプロンプトについて。
-
C++でデスクトップGUIアプリ開...
-
Windows Formアプリからコンソ...
-
バッチファイルで以下のような...
-
コンソールアプリを作成するの...
-
c言語
-
DLLファイルの逆コンパイラにつ...
-
C言語の関数のextern宣言
-
C言語 関数、変数の宣言について
-
C言語のことです。写真(見にく...
-
私は
-
C#でログファイルにファイルパ...
-
MACで動く実行ファイルをWindow...
-
VisualStudioでC++クラスを追加...
-
大量のデータを読み込んで表示...
-
visual studio 2022でのC#プロ...
-
プログラマー達は何故、プログ...
-
PIC12F1822でLED調光器を作りたい
-
最初に聞かれたこと
-
C言語について(初心者)
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
DLLファイルの逆コンパイラにつ...
-
大量のデータを読み込んで表示...
-
c言語
-
gccを行ってもexeファイルが生...
-
visual studio 2022でのC#プロ...
-
【C言語】全角文字の配列を、全...
-
Windows Formアプリからコンソ...
-
VisualStudioでC++クラスを追加...
-
VisualStudio2022でC言語プログ...
-
プログラマー達は何故、プログ...
-
C++でデスクトップGUIアプリ開...
-
逆コンパイルと逆アセンブルの...
-
C言語について。
-
C#でTreeViewのCheckBoxのサイ...
-
C言語の関数のextern宣言
-
int16_t の _t は何?
-
C#でログファイルにファイルパ...
-
ディスプレイの解像度とマウス...
-
c言語でイベントフラグを使った...
-
C言語のことです。写真(見にく...
おすすめ情報