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で質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# 宣言する関数の形が決まっている状態で、 str1とstr2の文字列をこの順に引っ付けてstrに保存し 2 2022/05/30 18:21
- C言語・C++・C# c言語 プログラムのエラー 1 2023/02/11 20:31
- C言語・C++・C# プログラミングの授業の課題です 1 2023/01/17 22:15
- C言語・C++・C# c言語配列の結合についてです。 なぜうまくいかないのでしょうか。 #include <stdio.h 4 2022/05/30 22:42
- C言語・C++・C# str[j++]の意味 2 2022/08/30 16:20
- C言語・C++・C# sprintf()の使い方について 1 2022/08/17 16:16
- C言語・C++・C# Cのdoubleの浮動小数点表示について 3 2023/04/17 13:14
- C言語・C++・C# C#テキストボックスの文字を配列にいれてその後表示する 4 2022/07/17 04:47
- C言語・C++・C# C# 浮動小数の数値文字列化 1 2022/04/18 15:15
- C言語・C++・C# C言語プログラム変更 2 2022/12/21 15:03
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
ArduinoのジャイロモジュールMP...
-
C言語をコンパイルするとコンピ...
-
あなたは、Excelはどうやって学...
-
C言語 列挙型(enum型)変数について
-
大量のデータを読み込んで表示...
-
C言語初心者 構造体 課題について
-
gccを行ってもexeファイルが生...
-
VisualStudio2022でC言語プログ...
-
C言語初心者 構造体 課題について
-
mallocについて
-
プログラミングについての質問...
-
scipy っていうのをいれようと...
-
Int('1234') で、strをかんたん...
-
License='MIT' ってなんでmitな...
-
どなたかこのプログラミングを...
-
卒業研究でよく分からないとこ...
-
c言語
-
終端記号、非終端記号とは
-
C言語について。
-
chatgptでつくってもらったコー...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
gccを行ってもexeファイルが生...
-
あなたは、Excelはどうやって学...
-
WindowsのCapsLock(キャップス...
-
質問失礼します。 プログラム言...
-
double型が正常に認識されてい...
-
Notepad++の関数リスト表示でC...
-
どちのほうがすきですか?
-
Stuck
-
Notepad++の関数リスト表示の変...
-
ArduinoでMouse関数を使用して...
-
C言語の関数と配列に関する質問
-
C言語って古いですか?
-
Linuxでの開発環境構築や設定の...
-
Bitcoin、BTCはブロックチェー...
-
C++6.0でのresource.hについて
-
MACで動く実行ファイルをWindow...
-
C言語 列挙型(enum型)変数について
-
c言語
-
大量のデータを読み込んで表示...
-
こんなことてしますか??
おすすめ情報