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

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

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

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

Q&A

解決済

3回答

1161閲覧

C言語の共用体について

tails

総合スコア22

C

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

0グッド

0クリップ

投稿2020/06/26 15:09

共用体について

C言語の共用体について、どんな操作が未規定、もしくは未定義になるのか、教えて下さい。

例えば、次のコードは、未定義の動作となりますか?
union U_tag に先に代入されているのは、v1 なので、v2.a を操作するには、一度 v2 の方の構造体を代入する必要があるのかな?と思いました。

C

1#include <stdio.h> 2 3union U_tag { 4 struct { 5 int a; 6 int b; 7 } v1; 8 struct { 9 double a; 10 double b; 11 } v2; 12}; 13 14int main(void) { 15 union U_tag u = {{10, 20}}; 16 printf("%d %d\n", u.v1.a, u.v1.b); 17 18 u.v2.a = 3.5; 19 u.v2.b = 5.5; 20 printf("%f %f\n", u.v2.a, u.v2.b); 21 22 return 0; 23}

Wikipedia

https://ja.wikipedia.org/wiki/%E5%85%B1%E7%94%A8%E4%BD%93
Wikipedia のC言語の欄では、/* これはダメ: 今u2に入っているのはあくまでFoo型 */ との文がありますが。
未定義の動作、ですか?

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

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

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

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

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

guest

回答3

0

ベストアンサー

仕様で完全に保証されていると言えるのは入れたときと同じ型で取りだす場合です。 質問文中の例は問題ないです。

共用体の仕様から直接的には読み取れないのですがポインタの型変換の説明の中にオブジェクトをバイト列として読むことは出来ることが示されているのでどんなオブジェクトであっても char (または signed charunsigned char やそれらを const 修飾したものなど) の配列として読むことは許されると考えられます。


オブジェクトの表現や境界調整 (アラインメント) は処理系 (アーキテクチャ) 依存であり、書き込んだときと別の型で読みだすのは処理系によって許されることは出来ますがそうでないときは未定義の挙動の可能性が出てきます。

まとめると

  • 入れたときと同じ型で取りだす場合は許される。
  • バイト列として読む場合は許される。
  • それ以外は処理系の裁量による。 言語仕様によって保証はされないが各環境の事情に合わせて使うことがただちに駄目というわけではない。

投稿2020/06/27 00:01

SaitoAtsushi

総合スコア5684

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

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

yumetodo

2020/06/27 03:22

unionってのはメモリーを節約するために使うものであって、別の型として読み出すためにあるわけじゃないですね。C++だとバイト列としてすら読めないですし。そういう用途にはmemcpyを使うべきでしょう。
pepperleaf

2020/06/27 07:04

> バイト列として読む場合は許される。 読み出すのは、問題無いですが、結果は無保証、、、ですね。 もっとも、unionでバイト列として読出し、それをシリアルで転送、受信側は、逆の操作で復元は組み込みで良くありました。 To: yumetodoさん、 一応、Cのタグがあるので、C言語の範囲では、問題無いですね。
tails

2020/06/28 11:14

この質問文中の例に問題がないのであれば、Wikipedia の例も問題ない、ということですよね。 では、Wikipedia の 'ダメ' というのは、何が言いたいのでしょうか… Foo として使っているのに、Bar みたいに使ったら意味分からなくなるだろ… ということでしょうか。。
anndonut

2020/07/04 03:06

tailsさん、Wikipediaのダメというのは書いた本人に聞いてみないと分からないと思います。C/C++はアセンブリ言語と併用することがあり、そのような用途では言語仕様をあまり気にしない記述をすることが多いです。structの1つ目の要素(a)についてうるさく言っていなかったのはそのためだと思います。そこらへんについては「楽しいバイナリの歩き方」などを読まれると理解が深まると思います。大規模プロジェクトだとプリプロセッサでOSやツールチェーンでスイッチングをかけます。これは言語仕様で保証しないコードを書かなければならない時に行う手法です。
guest

0

intのサイズは処理系に依存しますので、v1が何バイトが不明です。
intを4バイトとしたら、v1は8バイトになりますね。
doubleのサイズは8バイトなので、v2は16バイトになります。
U_tag内のv1とv2のサイズが異なります。概ねv2の方が大きくなるでしょう。
処理系によって、前詰め、後ろ詰めが異なるので、

v1 | a | b |
v2 | a | b |

となるかもしれませんし、

v1 | a | b |
v2 | a | b |
となるかもしれません。
v1.aに値を代入した場合に影響を受けるのはv2.aかもしれないし、v2.bかもしれない。

これでは、思惑通りの挙動を得られないでしょう。
質問の意図が不明なので、回答にならないかもしれませんが。

投稿2020/06/26 23:17

KohheyDokkoisyo

総合スコア63

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

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

tails

2020/06/28 11:11

なるほど… u.v2.a に代入した直後では、u.v1.a, u.v1.b, u.v2.b の読み取りは「未定義の動作を引き起こす」ということですね。 ありがとうございます。
guest

0

質問。どうしてそのような質問をしようと思ったのですか?unionのwikipediaのページには一言も未規定、未定義という言葉が出てきていないのですが。「悪魔の証明」という言葉をご存知ですか?無実を証明することは難しいんですよ。

例えば、次のコードは、未定義の動作となりますか?

なりません。

Wikipedia のC言語の欄では、/* これはダメ: 今u2に入っているのはあくまでFoo型 */ との文がありますが。

未定義の動作、ですか?

このwikiページを書いた人はサディストだなあと。
/* u2.bar.pbar = NULL; */ /* これはダメ: 今u2に入っているのはあくまでFoo型 */
というのは、この後、このコードがfooとして使われてるのかbarとして使われてるのかわからないのですよ。だから一概にダメとは言えなくてですね。

C

1char *str = "ABC"; 2u2.bar.pbar = (void*)str; 3printf("%f\n", u2.foo.dfoo);

こういうのがイカンのですよ。未定義うんぬん以前の話として、「お前いったい何がしたいん???」って全員から言われますね。または、単なるバグだと。まず、void*はアドレス値を保持しますがそれをdoubleの書式で出力してどないすねん、で、fooとbarの一個目はたまたま同じintだからstructのパディング同じになる「だろう」けど一個目がintとchar[10]だったらどうすんねん。データはぜんぜん違うところ入ってますよ、意味のないデータ拾ってますよ、ってことになります。

そもそもunionをunionだけで使っちゃいけないんですよ。その外側でどうやって使ってるのかがきちんと分かるように管理しなければならない。だからCのコードでunionはあまり見かけないですね。unionを使ったコーディングはよほど分かってる人じゃないと書けない。例えば私はこうやって書いてますね。手前味噌で申し訳ないですけど…。

C

1typedef enum tag_ObjectType { 2 OBJECT_TYPE_INTEGER, 3 OBJECT_TYPE_LIST 4} ObjectType; 5 6struct tag_Object; // オブジェクト型の前方宣言 7 8typedef struct tag_ListObjectComponent { 9 struct tag_Object *car; // carはリストの要素(伝統的な概念) 10 struct tag_Object *cdr; // cdrはリストの後続部(伝統的な概念) 11} ListObjectComponent; 12 13typedef struct tag_Object { 14 ObjectType type; 15 union { 16 int i; 17 ListObjectComponent list; 18 } d; 19} Object;

unionの中身を識別するためにenumを置いてるのですよ。unionを使うにはこれくらいはしないとダメです。

投稿2020/06/26 23:06

anndonut

総合スコア667

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

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

pepperleaf

2020/06/27 07:06

別のコメントに書きましたが、バイト列との変換用 unionだと、タグなんて付けずに利用。内部通信用に良く使われました。
tails

2020/06/28 11:10

質問の経緯は、Wikipedia に書いてあった「これはダメ」というのは、どういう意味でダメなのかな?と思ったからですね。 質問文中のコードの、 (1) u.v2.a = 3.5 と代入するのは、問題がないか (2) (1) が問題ない場合、u.v2.a = 3.5 とした直後は、u.v1.a, u.v1.b の読み取りは無効ですか? u.v2 に v2 の方の構造体を代入すれば、そちらが有効になるのは分かるのですが、構造体のメンバだけを代入すると、どういう挙動なのかな?と…
pepperleaf

2020/06/28 11:25

> u.v1.a, u.v1.b の読み取りは無効ですか? 無効と言うより、無保証。読めるが、意味のない値(処理系依存)となるだけです。(それを無効と言うかもしれませんが)
tails

2020/07/03 02:20

未定義の動作ですね。 分かりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問