TIL

Today I Learned. 知ったこと、学んだことを書いていく

アドレスとは - C言語

わかりやすいC 入門編の「17.1 アドレスとは」を読んでのメモみたいなもの

アドレスとは、メモリ内の位置のこと。

メモリは1バイトごとに区切られている。また、それぞれの区切りごとに0から順に番号が振られている。この番号をアドレスという。アドレスは16進数で表現される。

0x7ffee36fa9a8みたいな感じ

変数のアドレスは使用しているアドレスの最初のアドレスとなる。

例:int型の変数aのアドレス

以下のように 変数a が複数のアドレスを使っていた場合、最初の0x7ffee36fa9a8が変数aのアドレスとなる

0x7ffee36fa9a8  <- これが変数aのアドレス
0x7ffee36fa9a9
0x7ffee36fa9aa
0x7ffee36fa9ab

また、プログラムの中で&aと書くことで、変数aのアドレス(0x7ffee36fa9a8)表す。

&はアドレス演算子といい、アドレスを求めることができる

配列のアドレスは少し特徴的

例えば、int型の配列nがあった場合、n

  • 配列名
  • 配列nのアドレスの定数

という2つの意味を持つ

配列の場合、&をつけなくてもアドレスを表現できてしまう!

※それぞれの要素は変数と同じ扱いであるため、&をつける

例:以下のような配列nの場合

0x7ffeecbb89b0     <- &n[0]のアドレス (nも同じアドレスを指す)
0x7ffeecbb89b4     <- &n[1]のアドレス
0x7ffeecbb89b8     <- &n[2]のアドレス

n == &n[0]となっている!!

また、配列のアドレスはメモリ上の連続したアドレスになる。

#include <stdio.h>

int main(void) {
    int a = 13;
    int n[] = {5, 12, 8, 21, 9};

    printf("%p --  &a\n", &a);
    printf("%p --  n\n", n);
    printf("%p --  &n[0]\n", &n[0]);
    printf("%p --  &n[1]\n", &n[1]);
    printf("%p --  &n[2]\n", &n[2]);

    return 0;
}

実行結果

0x7ffeecbb89a8 --  &a
0x7ffeecbb89b0 --  n
0x7ffeecbb89b0 --  &n[0]
0x7ffeecbb89b4 --  &n[1]
0x7ffeecbb89b8 --  &n[2]

n と n[0]のアドレスが全くおなじになっていることに注目

また、配列の各要素のアドレスが4バイトずつになっている。これは、この環境のコンパイラではintが4バイトだから。

「配列のアドレス」と「型」が分かれば、それぞれの要素のアドレスが分かるということ!!

これは、ここに書いたベースレジスタとインデックスレジスタに関係している

本当にn == &n[0]なのか試してみた!

(2018/01/29 追記)

以下のプログラムを動かしてみた

#include <stdio.h>

int main(int argc, char *argv[]){

    int n[] = {3, 4, 7};

    if (n == &n[0]) {
        printf("同じ!\n");
    } else {
        printf("違う!\n");
    }
    
    if (n == &n[1]) {
        printf("同じ!\n");
    } else {
        printf("違う!\n");
    }
}

実行結果

同じ!
違う!

n == &n[0]が一致していることがわかった!!

(追記ここまで)

&演算子と参照渡し

(2018/1/30 追記)

C言語では基本的にすべて値渡しになっている。値渡しでは、変数の値のコピーが渡される。

でも、ある関数で引数で渡された値ではなく、渡された変数の値を変えたい場合、値渡しでは実現できない。

そこで、参照渡しというものを使う。

&演算子を使うことで、変数のアドレスを求めることができる。参照渡しができる!

値渡しと参照渡しの違いを図で書いてみた

f:id:tmg1998:20180131003556p:plain:w350

値渡しと参照渡しの違いをコードで表してみた

#include <stdio.h>

int main(int argc, char *argv[]){
    int a, b, *c;

    a = 25;

    // = 値渡しの場合 =
    b = a;
    b = 30;     // bの値を変更
    printf("a: %d\n", a);

    // = 参照渡しの場合 =
    c = &a;
    *c = 30;    // c(aのアドレス)の値を変更
    printf("a: %d\n", a);
}

実行結果

a: 25
a: 30

参照渡しの場合の流れ

  1. c = &aで変数aのアドレスをポインタcへ格納
  2. *c = 30でcに格納されているアドレスの変数(a)に30を設定する

f:id:tmg1998:20180131003810p:plain:w350

(追記ここまで)

参考文献

わかりやすいC入門編

わかりやすいC入門編