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

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

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

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

Q&A

解決済

3回答

4059閲覧

offsetofマクロの定義

strike1217

総合スコア651

C

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

0グッド

0クリップ

投稿2017/01/18 08:26

編集2017/01/18 15:01

C

1#define offsetof(s, mem) (size_t)&(((s *)0) -> mem)

offsetofマクロの定義を見てみますと。
やっていることは、なんとな~~くは分かるのですが・・・・

(s *)これはなんです???
アロー演算子が出てきているので、ポインタですよね??
シンタックスシュガーですか?

(((s *)0) -> mem)
この部分の式について何をやっているのか詳しく教えてください。

[追記]
実験してみました。

C

1struct d { 2 int j; 3 char g; 4 long r; 5 }a; 6 7 printf("%p, %p, ", &(a), &(a.g)); 8 printf("%d\n", &(a.g) - &(a)); 9 10 struct d *v = NULL; 11 // printf("%d", v ->j); segmentation fault 12 printf("&(v) = %p\n", &(v)); 13 printf("&(v -> j) = %p\n", &(v -> j)); 14 printf("&(v -> g) =%p\n", &(v -> g));

&(v) = 00C2FE5C
&(v -> j) = 00000000
&(v -> g) =00000004
こんな結果になりました。オフセットになっております。

C

1struct d v; 2 3printf("%p", v); 4printf("%p", v.g);

結果は、
&(v) = 002EFED4
&(v.g) =002EFED8

ちゃんとアドレスが出てきます。

分からないのは、「ヌルポインタにしたときに、なんで、アドレスがオフセットに変化しているか・・・」
ということです。

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

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

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

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

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

guest

回答3

0

このマクロを使用すれば、構造体「s」の中における要素「mem」のメモリ番地を知ることができます。

投稿2017/01/18 14:28

HogeAnimalLover

総合スコア4830

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

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

strike1217

2017/01/18 14:31 編集

ありがとうございます。 それは分かります。 どうして、このような記述の仕方で求めることができるか・・・ということです。
HogeAnimalLover

2017/01/18 14:49

NULLポインタを(s型構造体ポインタに)キャストし、0番地にs型構造体分の構造体変数があるとみなします。この上で、この構造体変数における要素memのメモリ番地を&演算子で取得します。この構造体変数は先頭が0であるので、この計算の結果、s型構造体における要素memのメモリ番地が手に入ります。
strike1217

2017/01/18 14:54

ああ~~~ 「0番地にs型構造体分の構造体変数があるとみなします。」 だから、アドレスがオフセットになるのですね!!
guest

0

(s *)は、s *型へのキャスト演算子です。

オフセットは、「メンバを指すアドレス」引く「構造体全体を指すアドレス」となりますが、0をポインタに読み替えることで、この後者を0にして、引き算を不要としています。

投稿2017/01/18 09:51

maisumakun

総合スコア145183

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

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

strike1217

2017/01/18 13:36

キャストしているのは分かるのですが・・・ (* s)こうではないのですか?? 0という数字をキャストしているのは初めて見かけたんですが・・・
guest

0

ベストアンサー

これは見た目以上に複雑な問題です。とりあえず「C言語の範囲で」説明します。
(C++言語の範囲ではさらに複雑になるので、追加で質問がなければC言語だけの説明にとどめます。)

まず、マクロという機能の意味は御存知ですね? 使用箇所で機械的に展開されます。

例えば、

C

1struct A {int a; int b;} 2... 3offsetof(struct A, b)

と書くと、最終行は

C

1(size_t)&(((struct A*)0) -> b)

として翻訳されます。括弧が多いですが1つづつ考えていきます。
まず、(struct A*)0の部分は、struct A型へのヌルポインタを作り出しています(0をポインタにキャストするとヌルポインタになる)。
次に、そのポインタを利用してメンバbにアクセスしています。構造体はヌルポインタなのに、そのメンバにアクセスするなんて物騒! そう思えるかもしれませんが、「実際にその値を利用しようとしない限り」そのようなアクセスも許容されています。今回はその外側の&記号でアドレスを求めているだけなので、bの値は利用しません。実際にアクセスできなくても、アドレスは計算できるのです。
最後に、そのアドレスに基づくポインタをsize_t型にキャストしています。ポインタから整数へのキャストは見慣れないかもしれませんが、そのポインタの値が「ヌルポインタから」どれだけ離れているかの値が、キャスト後の結果となります。結果、bのオフセットが計算されます。最初のヌルポインタ作成は、起点をヌルポインタにすることで、実体のある構造体を利用する場合に必要となる調整を省いているわけです。

以上、maisumakunさんの説明を小難しく解説しました。

投稿2017/01/18 13:35

majiponi

総合スコア1720

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

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

strike1217

2017/01/18 13:43

「(struct A*)0の部分は、struct A型へのヌルポインタを作り出しています(0をポインタにキャストするとヌルポインタになる)。」 なるほど!! (s *) -----> (struct A *)になるのですね! 分かりやすいです。 ありがとうございます
strike1217

2017/01/18 14:23

struct A *v = NULL; printf("%p\n", &(v -> g)); これでも同じ結果が得られました。 ヌルポインタはアドレスが0になるものではないですよね? なぜ、ヌルポインタと&でオフセットが出せるんですかね・・・・・
majiponi

2017/01/18 14:31

0になるとは限らないだけで、0になることもあります。補足のコードの場合、ヌルポインタの内部表現が0で、偶然アドレスの値がオフセットに一致しているのだと思います。
strike1217

2017/01/18 16:44

誰をベストアンサーにしようか迷いましたが、選ばせてもらいます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問