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

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

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

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

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

2回答

3567閲覧

【C言語】&構造体ポインタ変数名->要素名 と 構造体ポインタ変数名.要素名 の違い

yuki1013

総合スコア7

C

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

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2016/03/30 18:54

###困っていること(1)
使っている参考書は『苦しんで覚えるC言語』です。16章まで勉強を進めていて3週間経過しています。
(C言語が初めて扱う言語のため、本当にプログラム初心者です。)
C言語の書式について疑問が生じました。

ポインタ変数にはアドレスを入力出来るモードと
数値をメモリに記憶出来るモードがあることは理解できたのですが
構造体の勉強中に混乱し始めてしまって…。
困っていること(2)が本題です。

しかし簡単なところから確認していきたいので、お付き合い願います。
まず、以下の15行目の書き方には問題がありますか?
ポインタ変数なので、まずは[*data]として通常の変数を記憶できるようにしました。
scanf関数を用いているので[&*data]としています。

###ソースコード

C

1#include <stdio.h> 2 3void test(int *data); /*プロトタイプ宣言*/ 4 5int main(void) 6{ 7 int hoge; 8 test(&hoge); /*hogeのアドレスをtest関数に与える*/ 9 printf("hoge = %d\n",hoge); 10 return 0; 11} 12 13void test(int *data) /*hogeのアドレスを受け取る*/ 14{ 15 scanf("%d",&*data); /* 15行目 */ 16 return 0; 17}

###困っていること(2)
今度は構造体で、構造体ポインタ変数を用いるときに疑問が生じてしまいました。

(*構造体ポインタ変数名).要素名

構造体ポインタ変数名->要素名
と書き改めることまでは把握できました。

以下の32行目の書き方で混乱してしまいました。
&data->release

&(*data).release
↓ (?)
data.release
では「struct or union」と表示されるのですが何が問題なのでしょうか…。
上記の一段目と二段目は同じですが、三段目とこれらの違いがはっきりしていなくて悩んでいます。

###ソースコード

C

1#include <stdio.h> 2 3/*新規に game型 を宣言*/ 4typedef struct { 5 char name[256]; /*作品名*/ 6 int release; /*発売年*/ 7 char genre[256]; /*ジャンル*/ 8} game; 9 10void Input_game(game *data); 11void Output_game(game data); 12 13int main(void) 14{ 15 int i; 16 game data[5]; 17 18 for (i = 0;i < 5;i ++) { 19 Input_game(&data[i]); 20 } 21 22 for (i = 0;i < 5;i ++) { 23 Output_game(data[i]); 24 } 25 26 return 0; 27} 28 29void Input_game(game *data) 30{ 31 scanf("%s",data->name); /* nameが配列名なので、&は必要ない 。*/ 32 scanf("%d",&data->release); /* releaseが変数名なので&をつける…?*/ /*32行目*/ 33 scanf("%s",data->genre); /* nameと同様 */ 34 return 0; 35} 36 37void Output_game(game data) 38{ 39 printf("作品名:%s 発売年:%d ジャンル:%s\n",data.name,data.release,data.genre); 40 return 0; 41}

###追加の質問
「苦しんで覚えるC言語」の次に読んでみると、参考になるかもしれない書籍がありましたら
教えていただきたいです。
(こちらは本当におまけですので、無理に答えていただなくても大丈夫です。)

よろしくお願いします…!

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

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

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

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

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

guest

回答2

0

ベストアンサー

(1)はすでにアドレスですので、dataのままいいです。&*dataはdataが示す値を取得して、その値が存在する場所のアドレス(結局はdataそのもの)になるため、意味が無い演算です。よくscanf&を付けるのはポインタを指定しなければならないからで、既にポインタの場合はそのままでかみません。

(2)で注意すべきなのは演算子の優先順位とその優先順位が高いところから値を求めていく点です。演算子の優先順位は下記を参考にしてください。
Wikipedia:CとC++演算子#演算子の優先順位
上からわかるように&->->の方が結合が強いため、先に計算されます。わかりやすく()をつけると

&data->release

&(data->release)

&((*data).release)

となります。つまり、上記の動作は()の内側から計算されるため、

  1. dataが示す先の構造体を得る *data
  2. 1.の構造体の中のreleseを得る (*data).relese
  3. 2.で得た値が存在する場所のアドレスを得る &((*data).relese)

という動作になります。

(おまけ)古典ですが聖典「K&R」は一度は読んでおくと良いかもしれません。内容は古いですが、C言語を作った開発者によるC言語のエッセンスが詰まっています。ただ、全く初心者向けではないため、一度読んだだけで全て理解するのは難しいです。ある程度やってから再度読み直すと、その素晴らしさに気付くことでしょう。

投稿2016/03/30 20:41

編集2016/03/30 20:54
raccy

総合スコア21733

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

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

yuki1013

2016/03/31 04:40

(1)の内容は、説明していただけるとそうだった! と頷けるのですが、自力ではなかなか気づけませんでした… 「`&*data`ではdataが示す値を取得して、その値が存在する場所のアドレス(結局はdataそのもの)になるため…」 この部分が順を追って解説していたため、非常に参考になりました! (2)の内容は、今まで読んできた範囲では、ほとんど優先順位について触れていなかったのでためになりました。 さらに優先順位を意識して考え直してみると、* よりも . が優先されてしまうので `(*data).release`…そのあとにアドレスを得るための&… ようやく分かりました! 疑問が解消したので次の章に進むことが出来そうです、ありがとうございます!
guest

0

ポインターってわかりにくいですよねー
今一度おさらいしてみましょうよ
なお,長いですが,直接的な回答になっているわけじゃないのであしからず,です.

C

1int a; 2a = 1;

これで 変数a に 1が入るのはわかると思いますが,このとき,aというのはメモリにaという名前のついたある大きさの領域が確保されています.

ポインターを勉強すると * やら & を使ってアドレスを演算,それに向かって代入する事ができる,という話に出逢います.なるほど,じゃあ

C

1int *a; 2*a = 1;

なんてやっても出来そうですね.

ダメでした…

これは何故かと言うと int *a が aという名前のアドレスを記憶する型 の変数だからです.
ですのでその変数に数値の1をしまうということは直接的には出来ません(これがメモリのアドレスを示す値とわかっている(明示する)なら別ですが,上のコードの互換と考えて読んで下さい).

C

1int *a; // aというポインタ変数 2int b; // bというint型変数 3a = &b; // aはbというint型変数の場所(アドレス)を記憶 4*a = 1; // bに1を代入 5printf("aでアクセスするなら -> %d\n", *a); 6printf("bでアクセスするなら -> %d\n", b); 7

ああ,もうなんでしょう! * やら & やらごちゃごちゃしますねー.
でも,これで変数bに1が入ったのがわかりますかね?
それが変数aでもアクセス出来ているのが分かりますか?

このあたりを今一度しっかり(もしくはしっくりくるまで)理解しますと,その先がとても楽になります.

投稿2016/03/31 00:06

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

yuki1013

2016/03/31 02:58

自作関数の(仮ポインタ引数)に、引数のアドレスを渡したときは *をつけて通常の変数のように数値を代入出来ることに慣れてしまっていたために 一瞬、上二つの違いを整理できませんでした。 アドレスを記憶していないから、この二段目の場合はポインタ変数に数値を代入できないのでしたね。 完全に理解できたと言い張れるほどではないですが、この二つを見比べてつっかえていたものが解消したような気がしましたっ。 ありがとうございますっ!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.51%

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

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

質問する

関連した質問