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

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

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

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

Q&A

解決済

5回答

3049閲覧

&アドレス演算子

reotantan

総合スコア295

C

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

0グッド

0クリップ

投稿2015/09/02 09:31

編集2015/09/02 13:06

swapの受け取りは、nxnyなのに
sort2の中ではswap(n1,n2)で呼び出していますよね。
n1、n2として呼び出す必要があると思ったんですが、
どう考えればいいんでしょうか?
追記
int
n1 のint
はこのn1というポインタが示す値はイント型がでないといけない。(ポインタ型)
sort2の中の*n1はn1ポインタの値(実際は6、5)という理解であっていますか?
多分ポインタ型とポインタ型変数の理解が浅いのと、ポインタの前に*をつけて値を表わすために混乱しています。
何度も質問して申し訳ありませんが、どうかお力を貸して下さい。

void swap(int*nx,int*ny) { int temp=*nx; *nx=*ny; *ny=*nx; } void sort2(int*n1,int*n2) { if(*n1>*n2) swap(n1,n2); } int main(void) { int na,nb; puts("type two integers"); printf("A" ); scanf("%d",&na ); printf("B" ); scanf("%d",&nb ); sort2(&na,&nb); puts("sort value in asending order"); printf("A=%d\n",na ); printf("B=%d\n",nb ); return 0; } コード

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

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

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

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

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

guest

回答5

0

swapの仮引数部分を見ると、「前に*を付けるとint型になるもの」を渡す必要があると分かります。
sort2のswap(n1,n2)ですが、仮引数の部分を見ると、「n1がint型」ということが分かります。つまりn1は「前にを付けるとint型になるもの」です。
なので、n1 や n2 をそのまま渡します。*n1 だと int型の値になってしまうので、swapの仮引数に合いません。

なお、「前に*を付けるとint型になるもの」のことを「int型へのポインタ」と言います。

というのが、そのプログラムありきでの文法に沿っての説明です。
そうなると次に、「何故swapの仮引数は『前に*を付けるとint型になるもの』なのか?int型じゃ駄目なのか」という疑問がわくかもしれません。
アセンブラをやらずにC言語に入った人は、ポインタが難しいと感じる人が多いようで、世の中にポインタの説明文はいくらでもありますので、いろいろ調べてみてください。

投稿2015/09/02 09:49

otn

総合スコア84505

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

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

reotantan

2015/09/02 09:58

解説ありがとうございます。 アセンブリですか、otnさんはアセンブリはプログラミング言語をしっかり理解するために必須だと思いますか?
otn

2015/09/02 10:01

アセンブラを学ぶことは必須では無いと思います。ただ、ポインタの理解がどうしても出来ない場合には、立ち戻ってみるのもいいかと思います。
reotantan

2015/09/02 10:03

そうですか、丁寧にありがとうございます
guest

0

ポインタは宣言するときに「int *p」こんな風に書きますが、
アドレスを扱う変数といいながら、アドレスを確認するときは「p」のように宣言したのと異なる形で参照します。
感覚的に宣言時と参照時でイメージが逆転するので、混乱するのかなと思っています。
int a = 1;
ptr p = memoryAddress(a); //動作しません
printf("d%",ReferenceValue(p)); //動作しません
冗長で不便ではありますが、こういう書式なら混乱はなかったでしょうね。


main側はこのように書くとsort2内と同じような形になります
直ぐ近くにあると違和感も減るのではないでしょうか?

C

1int na=2, nb=1; 2int *p1,*p2; 3p1 = &na; 4p2 = &nb; 5sort2(p1,p2);

コメントを書いてみました。

C

1int main(void) 2{ 3 sort2(&na,&nb); 4 //na,nbではint型の値、&na,&nbではアドレスが参照できる 5return 0; 6} 7 8void sort2(int*n1,int*n2) //アドレスを受け取る変数の宣言 9{ 10 if(*n1>*n2)swap(n1,n2); 11 //*n1,*n2でint型の値、n1,n2でアドレスが参照できる 12} 13 14void swap(int*nx,int*ny) //アドレスを受け取る変数の宣言 15{ 16 int temp=*nx; 17 *nx=*ny; 18 *ny=*nx; 19 //*ny,*nxでint型の値が参照できる 20}

投稿2015/09/02 13:59

編集2015/09/02 14:15
hirohiro

総合スコア2068

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

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

hirohiro

2015/09/02 14:02

所でswapの最終行は 「*ny=*temp;」ではないでしょうか?
reotantan

2015/09/02 15:05

本には*はついていなかったんですが、*をつけるのとつけないのと意味は変わってしまいますか?
hirohiro

2015/09/02 16:09 編集

あれ?すみません。「*がない」というのは何処のことでしょう? コメント欄で書いたのはここのことです。 void swap(int*nx,int*ny) { int temp=*nx; *nx=*ny; *ny=*nx; //*ny=temp; では? } ※tempに*はいらないですね。
guest

0

ポインタは最初は色々混乱しますよね。

考えた方としては、int *nx の部分を別の型として見てみるとすっきりするかもしれません。

typedef int* intp;

として、新しい型として考えてみます。すると以下のようになります。

#include <stdio.h> typedef int* intp; void swap(intp nx, intp ny) { int temp = *nx; *nx = *ny; *ny = temp; } void sort2(intp n1, intp n2) { if (*n1 > *n2) { swap(n1, n2); } } int main(void){ int a = 6; int b = 5; sort2(&a, &b); printf("a: %d, b: %d", a, b); return 0; }

こうすると、sort2 から swap へ同じ型の変数を渡しているのがなんとなく分かるんじゃないかと思います。

ちなみに実際に動作が確認できるものをアップしてみました↓
https://paiza.io/projects/1jlMQXBYnFEtXflEjft9PA

投稿2015/09/02 11:39

edo_m18

総合スコア2283

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

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

reotantan

2015/09/02 15:07

ありがとうございました、とても分かりやすい解説で助かりました。
guest

0

ベストアンサー

Cのアドレス演算子は至極単純です。

変数に値を代入する。ということは、基本的にはメモリに値をセットしている訳です。
アドレス演算子を変数に用いるというのは、セットした値ではなく、値をセットしたメモリの番地を問い合わせていることになります。

つまり、変数の値を収めた「メモリアドレス」を表すのが、アドレス演算子です。
別の見方をすると、ポインタ変数が保持するタイプの値を取得できる。ということです。

下記のように書いた場合、変数:cNumにはchar型の配列が割り当てられます。

C

1char cNum[] = "01234";

(仮に、変数:cNumが0x100番地から割り当てられた場合)以下のコードは 0x100〜0x104 を表示します。

C

1int i; 2for(i=0;i<5;i++) 3 printf("0x%x\n", &cNum[i]);

関数に参照渡し(ポインタ渡し)をするというのは、値の入っているメモリの番地を渡すことです。
アドレス演算子の利用目的は、変数のポインタを得るのに使われます。

配列じゃなく普通の変数の場合は、添え字が付かないだけです。

C

1int num = 0; 2printf("0x%x\n", &num);

話が本題とはそれますが…

先ほどの変数:cNumは配列です。
配列の場合、添え字を使わずに変数名だけ指定した場合は配列先頭のポインタと等価なので、以下のように書くこともできます。

C

1int i; 2for(i=0;i<5;i++) 3 printf("0x%x\n", cNum+i);

配列に入っているデータを順次関数に渡す場合などでは、こういった表現方法も使えます。

[追加]
関数定義の int* も 変数定義の int* もそうですが、これらは「int型のポインタ」を収める変数定義です。

ポインタというのは「"値を収めたメモリ領域(=変数に割り当てられたメモリ領域)" の番地」を表すものです。
そのポインタ変数を * で修飾すると、ポインタの示すメモリに格納された値を表します。

この逆のパターンが、アドレス修飾子 & です。
例えば int型の変数:num のポインタが欲しい場合は &num と書きます。得られるのは変数に割り当てられたメモリアドレスです。

これらをコードで示すと、以下のような感じです。
※ポインタ経由で、 num1 = num2; と同じ結果を得る処理を行います。

C

1int num1, num2; 2int* pNum; 3 4num1 = 999; // num1 に 999 を代入 5pNum = &num; // num1のポインタを pNum に代入 6num2 = *pNum; // pNum(num1のメモリアドレス) に格納された値 (= num1の値)を num2に代入 7 8printf("%d %d %d", num1, *pNum, num2);

関数へのポインタ渡しについては、例えば以下のような感じです。

test1関数は引数がポインタ渡しなので、test1関数を呼び出す際は 変数:numのポインタを取得して渡しています。
test2関数も引数がポインタ渡しです。test2関数を呼び出す際もポインタを渡すことになります。test1関数では既にポインタで値を受け取っていて変数:pNumの中身はnumのポインタです。test2関数を呼び出す時は、変数:pNumに収められたnumのポインタをそのまま渡します。

もし、test1関数の中で pNum のように、 で修飾するとポインタの中身ということで、intの値(変数:numに代入した値)を表すことになります。しかし、test2関数はポインタを必要とするので修飾の必要はありません。
※これは & で修飾した場合の逆パターンです。

実行結果としては、test2関数内の printf関数によって変数:numに代入した値が出力されます。

C

1void test2(int* pNum){ 2 printf("%d", *pNum); 3}; 4 5void test1(int* pNum){ 6 test2(pNum); 7}; 8 9int num; 10 11num = 123; 12test1(&num);

質問の関数でも同様のポインタ渡しが行われます。
https://teratail.com/questions/15327 でも書いたのですが、デバッガで動きを追いかけることをした方がいいですよ。

質問の実装も、sort2関数に以下のように渡すことも合わせて考えれば、多少理解の助けになるでしょうか。

C

1int *pNA, *pNB; 2 3pNA = &na; 4pNB = &nb; 5 6sort2(pNA,pNB);

投稿2015/09/02 10:15

編集2015/09/02 14:10
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

reotantan

2015/09/02 15:03

丁寧な解説ありがとうございました。 やっと理解できました;
guest

0

void swap(intnx,intny)

この宣言ではnx,nyをintのポインタで受け取ると宣言しています。

ここで呼び出し側ですが

swap(n1,n2);

となっていてn1,n2の型は何かとみると

void sort2(intn1,intn2)

n1,n2もintのポインタです。

ですので、swapを呼び出すときはそのままn1,n2を使えばintのポインタが渡っていきます。

ここでn1,n2のポインタがさす内容は何かを確認するときに

if(*n1>*n2)

のように*n1,*n2とします。

intを例にすると
データ型が値の場合
宣言 int a
値参照 a
アドレス参照 &a
データ型がポインタの場合
宣言 int* a;
値参照 *a
アドレス参照 a
(多重ポインタはいったん話の外に置きます)

おまけ

int temp=*nx; *nx=*ny; *ny=*nx;

ここのny=nx;はny=temp;にしないとnx==*nyになってしまいますよ。

投稿2015/09/02 09:58

kutsulog

総合スコア985

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

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

reotantan

2015/09/02 10:26

気になったんですが、データ型をポインタにする利点で何ですか? いままでデータ型に値しか出てこなかったのですが、 データ型にポインタを使ってしかできない事、また利点などはあるのでしょうか? (データ型に値を使い、アドレス参照できれば十分では?と思ってしまいました。)
reotantan

2015/09/02 15:02

ありがとうございました!
kutsulog

2015/09/02 16:22

C++ならともかくCにアドレス参照による引数渡しはできませんので、 関数で値を操作して関数の呼び元に返すにはポインタが必須です。 ざっくり言うとCは機械が高速に動くために 機械がそのまま解釈できるコードを書くことが求められる言語です。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問