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

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

ただいまの
回答率

90.61%

  • C

    3563questions

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

&アドレス演算子

解決済

回答 5

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 924

reotantan

score 245

swapの受け取りは、*nx*nyなのに
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;
}
コード
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 5

+1

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

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/09/02 18:58

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

    キャンセル

  • 2015/09/02 19:01

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

    キャンセル

  • 2015/09/02 19:03

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

    キャンセル

checkベストアンサー

0

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

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

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

下記のように書いた場合、変数:cNumにはchar型の配列が割り当てられます。
char cNum[] = "01234";
(仮に、変数:cNumが0x100番地から割り当てられた場合)以下のコードは 0x100〜0x104 を表示します。
int i;
for(i=0;i<5;i++)
  printf("0x%x\n", &cNum[i]);
関数に参照渡し(ポインタ渡し)をするというのは、値の入っているメモリの番地を渡すことです。
アドレス演算子の利用目的は、変数のポインタを得るのに使われます。

配列じゃなく普通の変数の場合は、添え字が付かないだけです。
int num = 0;
printf("0x%x\n", &num);


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

先ほどの変数:cNumは配列です。
配列の場合、添え字を使わずに変数名だけ指定した場合は配列先頭のポインタと等価なので、以下のように書くこともできます。
int i;
for(i=0;i<5;i++)
  printf("0x%x\n", cNum+i);
配列に入っているデータを順次関数に渡す場合などでは、こういった表現方法も使えます。

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

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

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

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

int num1, num2;
int* pNum;

num1 = 999;    // num1 に 999 を代入
pNum = &num;    // num1のポインタを pNum に代入
num2 = *pNum;    // pNum(num1のメモリアドレス) に格納された値 (= num1の値)を num2に代入

printf("%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に代入した値が出力されます。

void test2(int* pNum){
  printf("%d", *pNum);
};

void test1(int* pNum){
  test2(pNum);
};

int num;

num = 123;
test1(&num);

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

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

pNA = &na;
pNB = &nb;

sort2(pNA,pNB);

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/09/03 00:03

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

    キャンセル

0

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

ここで呼び出し側ですが
      swap(n1,n2);
となっていてn1,n2の型は何かとみると

void sort2(int*n1,int*n2)
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 19:26

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

    キャンセル

  • 2015/09/03 00:02

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

    キャンセル

  • 2015/09/03 01:22

    C++ならともかくCにアドレス参照による引数渡しはできませんので、
    関数で値を操作して関数の呼び元に返すにはポインタが必須です。

    ざっくり言うとCは機械が高速に動くために
    機械がそのまま解釈できるコードを書くことが求められる言語です。

    キャンセル

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/03 00:07

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

    キャンセル

0

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


main側はこのように書くとsort2内と同じような形になります直ぐ近くにあると違和感も減るのではないでしょうか?
int na=2, nb=1;
int *p1,*p2;
p1 = &na;
p2 = &nb;
sort2(p1,p2);


コメントを書いてみました。
int main(void)
{
    sort2(&na,&nb); 
    //na,nbではint型の値、&na,&nbではアドレスが参照できる
return 0;
}

void sort2(int*n1,int*n2) //アドレスを受け取る変数の宣言
{
    if(*n1>*n2)swap(n1,n2);
    //*n1,*n2でint型の値、n1,n2でアドレスが参照できる
}

void swap(int*nx,int*ny) //アドレスを受け取る変数の宣言
{
    int temp=*nx;
    *nx=*ny;
    *ny=*nx;
    //*ny,*nxでint型の値が参照できる
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/09/02 23:02

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

    キャンセル

  • 2015/09/03 00:05

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

    キャンセル

  • 2015/09/03 01:07 編集

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

    キャンセル

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

  • ただいまの回答率 90.61%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

  • 解決済

    C言語 式の構文エラー 内容について

    複数の単語からなる文を入力し,各単語の先頭文字を大文字に変えて表示させる というプログラムを作っているのですが、コンパイル時に ------ Borland C++ 5.5.1 f

  • 受付中

    点数別分布グラフ

    点数分布のグラフですが、少し長いように感じました。もう少し短くすることはできますか? またこのコードに対して何かご意見がありましたらお願いします。 #inc

  • 解決済

    コードを見てダメ出しや指摘などお願いします。

    #include <stdio.h> int main(void){     double a;     double b;        char o;  //演算子    

  • 解決済

    [C言語] 2次元配列に一文字ずつ格納する方法

    前提・実現したいこと お世話になります。またしても初歩的な内容で申し訳ないのですが、現在勉強のため、入力を平面として捉え、「0」「1」に変換して位置関係を保持したまま出力する、と

  • 解決済

    a.exeは動作を停止しました。の直し方を教えてください。

    プログラムを実行して途中までは処理されるのですが、ある所から「a.exeは動作を停止しました。」と出てしまいプログラムが強制終了されてしまいます。 前回このようなことがあり、質問

  • 解決済

    約数判定 変数に読み込めない

    include <stdio.h> int main(void){ int x,y; scanf("%d%d",x,y); if(x%y) puts("no");

  • 解決済

    c言語 リスト構造について...

    前提・実現したいこと 最近C言語でリスト構造を勉強したので自己流でリスト構造のプログラムを作成したのですが正常に作動しません。どなたか解決法を教えてください。 説明不足だったの

  • 受付中

    C言語のエラー対策について

    int a; scanf(“%d”,a); この文で、aに2000000000000と言った容量がオーバーする値をユーザーが入力した時に再入力を要求するプログラムはどのように書きま

同じタグがついた質問を見る

  • C

    3563questions

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