質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.44%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

Q&A

解決済

2回答

1029閲覧

構造体のポインタについて

muzuwaku

総合スコア1

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

0グッド

0クリップ

投稿2023/03/06 06:06

編集2023/03/06 10:16

ブレイクポイントで値を見ながらやっていたのですが

18行目ではvar2にちゃんと数値が代入されるのに
21行目のprintf("%d %.1f\n",var2->i, var2->d);

で、var2->iとvar2->dにデタラメな数値が代入されるのです。
何故なのでしょうか?
(20行目のprintf文を消せばちゃんと代入されるのです…)

該当のソースコード

c

1#include <stdio.h> 2 3struct s_type { 4 int i; 5 double d; 6}; 7 8struct s_type s(void); 9struct s_type* p_s(struct s_type s); 10 11int main(void) 12{ 13 struct s_type var = { 0 }; 14 struct s_type var1 = { 0 }; 15 struct s_type* var2 = NULL; 16 17 var1 = s(); 18 var2 = p_s(var); 19 20 printf("%d %.1f\n", var1.i, var1.d); 21 printf("%d %.1f\n", var2->i, var2->d); 22 23 return 0; 24} 25 26struct s_type s(void) 27{ 28 struct s_type temp; 29 30 temp.i = 100; 31 temp.d = 123.23; 32 33 return temp; 34} 35 36struct s_type* p_s(struct s_type s) 37{ 38 s.i = 10; 39 s.d = 20.0; 40 41 return &s; 42}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

setoppu

2023/03/06 06:16

ローカル変数へのポインタを返して、ソレがいつまで有効だと思いますか?
dameo

2023/03/06 06:22

structキーワードがいくつか抜けていて、gccでは大量のエラーが出ています。 以下の警告も出てますね。 warning: function returns address of local variable
muzuwaku

2023/03/06 06:44

>>setoppu すみません、どのように書いたら有効になりますでしょうか? >>dameo visual studioで実行していたからかもしれません、気が付きませんでした。
int32_t

2023/03/06 06:47

p_s() が何をする関数なのか、日本語で説明したほうがよいと思います。渡された var を書き換える? 渡された var から新たに var2 を作る?
muzuwaku

2023/03/06 07:15

>>int32_t 自分で構造体ポインタはどのような処理になるかなと何も見らず適当に書いてしまったもので本当に申し訳ありません。 >>dameo よく見たら出力のビルド項目にC4172が出ておりました。失礼しました。 >>episteme 自分では解決出来ない気がしてきました。値を返すだけでしたらどのように書いたらよろしいのでしょうか?
dameo

2023/03/06 07:26 編集

Cなら警告に気付かなかったとしてもコンパイルエラーが出ていてデバッグできないはずなんですけどね C++でコンパイルしたらエラーが出ない代わりに警告も出ないのでC4172も出ないしとても不思議ですね 一体どんなバージョンのvisual studioなんでしょうか?
dameo

2023/03/06 07:32

visual studioのC++の場合、x64(64bit)でビルドするとC4172が出ず、x86(32bit)でビルドするとC4172が出るみたいですね。 https://godbolt.org/z/Pj39ahTh9
dameo

2023/03/06 07:35

いずれにしてもC言語タグなので、ファイル名の拡張子を.cにしてコンパイルしてください。 C++なら書き方を全般的に見直した方がいいです。
muzuwaku

2023/03/06 07:40

>>demeo vs2019です。拡張子がcppだったのでC++だったのかもしれません。
dameo

2023/03/06 07:42

> いずれにしてもC言語タグなので、ファイル名の拡張子を.cにしてコンパイルしてください。 上記実施後、質問文の修正をお願いします。
muzuwaku

2023/03/06 08:23

>>dameo x64ビルドのやり方など理解しておらず失礼しました。 試してみたところx64では値もデタラメな値ではなくちゃんと通りました。 何故x64では通ったのかはわかりませんがいずれ自分が理解出来る日を祈ります。
muzuwaku

2023/03/06 08:25

皆さま最後まで長々とありがとうございました。 もしまた機会がありましたらその時は面倒かもしれませんがその際ご指南の程よろしくお願いいたします。
dameo

2023/03/06 08:37

> 試してみたところx64では値もデタラメな値ではなくちゃんと通りました。 偶然です。 C++でもvar2は見てはいけないメモリを見てしまっていますが、たまたまprintfで書き換えられないだけです。 なぜC++の64bitで警告が出なかったのかは知りません。どうしても知りたいならマイクロソフトの人にでも聞けばいいのでは? 基本的にC言語やC++言語では自己責任で何でも出来るので、個人的に処理系の警告は親切で付けてくれてるだけのものと考え、そこまで期待してません。 最後に、なんかやたらCとC++を混同する人が多いので、同じ人だと思ってます。
dameo

2023/03/06 10:22

visual studioのタグを付けるなら環境に2019である旨を書くべきで、ただ言語は明確にCなので、タグのCは必要です。実際に生成されるコードも違いますし、解決後に勝手に消すのもおかしく、CとC++を混同したがる人すぎです。 履歴と違って実際にはついてますが・・・
int32_t

2023/03/06 21:23

x64 だと引数は4つまでレジスタ渡しになるので、x86 よりスタックの値が壊れにくいです。
dameo

2023/03/06 23:17

確かに呼出規約は違いますが、、、 ; x86 sub esp, 16 ; 00000010H mov ecx, esp mov edx, DWORD PTR _var$[ebp] mov DWORD PTR [ecx], edx mov eax, DWORD PTR _var$[ebp+4] mov DWORD PTR [ecx+4], eax mov edx, DWORD PTR _var$[ebp+8] mov DWORD PTR [ecx+8], edx mov eax, DWORD PTR _var$[ebp+12] mov DWORD PTR [ecx+12], eax call s_type * p_s(s_type) ; x64 lea rax, QWORD PTR $T1[rsp] lea rcx, QWORD PTR var$[rsp] mov rdi, rax mov rsi, rcx mov ecx, 16 rep movsb lea rcx, QWORD PTR $T1[rsp] call s_type * p_s(s_type) ; p_s この例でいうと$T1が呼び出し前に確保されてる点が違ってて助かってるということかもしれません。 上の生成コードは先のURL(Compiler Explorer)で確かめたのでオプション正確じゃないですけど。。。
dameo

2023/03/06 23:18

なお、C++でコンパイルしてる時点で全然意味ないですけどね
guest

回答2

0

ベストアンサー

他の方もおっしゃっているように、関数の引数は関数の実行が終わると破棄されるので、引数のアドレスを返してはいけません。

コピーのコストが小さければ、構造体そのものを返すのが一番安全です。

c

1 ... 2 struct s_type var2 = ps_s(); 3 ... 4 5struct s_type p_s() 6{ 7 struct s_type s = {.i=10, .d=20.0}; 8 return s; 9}

ポインタで返すなら、メモリの動的確保などします。

c

1 ... 2 struct s_type* var2 = ps_s(); 3 ... 4 free(var2); 5 ... 6 7struct s_type* p_s() 8{ 9 struct s_type* s = (struct s_type*)malloc(sizeof(struct s_type)); 10 s.i = 10; 11 s.d = 20.0; 12 return s; 13}

投稿2023/03/06 07:23

編集2023/03/06 07:45
int32_t

総合スコア21207

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

muzuwaku

2023/03/06 07:41

構造体ポインタを使って返してみたいと思っておりましたが今のかなり自分には難しそうです。解答ありがとうございました。
muzuwaku

2023/03/06 08:08

malloc関数を使うことで通るようになりました。このような場合に動的メモリの確保を使うのですね。ありがとうございます。あと失礼ながらs.i、s.dの方はアロー演算子に変更させてもらいました。
episteme

2023/03/06 08:11 編集

mallocするのは正解。ただし使い終わったらちゃんとfree()しましょうね、と。 # 借りたものは返しましょう
muzuwaku

2023/03/06 08:28

mainの最後の方でfree(var2);を付け足しておきました。
int32_t

2023/03/06 21:21

そうですね、-> でないとコンパイルできませんね。 (たいていコンパイル確認せずに回答を書いてます)
guest

0

C

1struct s_type *p_s(s_type s) 2{ 3 s.i = 10; 4 s.d = 20.0; 5 6 return &s; 7}

引数であるsはその関数から抜けた途端捨てられます。
捨てられるってことは他の用途に使って構わない、となるとアドレス&sに置かれた値が保持される保証はありません。

(20行目のprintf文を消せばちゃんと代入されるのです…)

原因は正にソレ。 20行目のprintfのために使われ(ナカミが書き換えられ)たってことでしょね。

投稿2023/03/06 06:40

編集2023/03/06 07:20
episteme

総合スコア16614

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

muzuwaku

2023/03/06 07:39

中身が何故書き換えられてしまったのかよくわからないです。ポインタを使ったからでしょうか?
episteme

2023/03/06 08:15 編集

抜けた途端に捨てられる(使い回されても文句の言えない)領域のナカミがずっと維持されると思っていたから、です。 運よく使われずに維持されるかもしれないけど、それは保証されない。 > 中身が何故書き換えられてしまったのかよくわからないです。 わかんなくてもいいのでは? 捨てたからには使い回されても文句言えないんだから。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.44%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問