Skip to content

Instantly share code, notes, and snippets.

@msymt
Last active March 3, 2020 12:32
Show Gist options
  • Save msymt/396a3787f30ba0d9329fae9335bd1160 to your computer and use it in GitHub Desktop.
Save msymt/396a3787f30ba0d9329fae9335bd1160 to your computer and use it in GitHub Desktop.
C:ポインタ再入門

C ポインタ再入門

環境

$ uname -a
Linux 5.0.0-37-generic #40~18.04.1-Ubuntu SMP Thu Nov 14 12:06:39 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

ポインタ型には、ポインタ型の変数ポインタ型の値が存在.

  • ポインタ型の値: メモリアドレス
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

int main(void){
  int hoge = 1;
  int huga = 2;
  int *hogep;   //[intへのポインタ]型の変数hogep

  printf("&hoge  %p\n", (void *)&hoge);
  printf("&huga  %p\n", (void *)&huga);
  printf("&hogep %p\n", (void *)&hogep);

  // ポインタ変数hogepにhogeのアドレスを代入
  hogep = &hoge;
  printf("&hogep %p\n", (void *)hogep);

  // hogepを経由してhogeの値を表示
  printf("hogep %d\n", *hogep); 

  // hogepを経由してhogeの値を変更
  *hogep = 10;
  printf("hoge  %d\n", hoge);
  return 0;
}
&hoge  0x7ffcdd36e778
&huga  0x7ffcdd36e77c
&hogep 0x7ffcdd36e780
&hogep 0x7ffcdd36e778
hogep 1
hoge  10

hogep = &hoge;にてポインタ変数hogephogeのアドレスを代入. よって

アドレス 変数
0x7ffcdd36e780 hogep 0x7ffcdd36e778

となる. ポインタ変数hogepが別の変数hogeのアドレスを保持している. この状態をhogepはhogeを指しているという。

printf("hogep %d\n", *hogep);
関節演算子を用いて、hogepからポインタを一つ手繰り寄せ、その値を表示. ポインタにをつけることで、その指している先のものを表す. ここでは、hogepはhogeを指しているため, *hogephogeと同じものを表す. よって, 5を表す.

また, hogepの指す先の値を書き換えることで、元々の代入元の値も書き換えることが可能
*hogep = 10;とすることで、結果的にhogeの値も変更される. 出力結果には10と書かれている.


ヌルポインタ

ヌルポインタ:何も指していないことが保証されているポインタ. 通常はマクロNULLを使用する. ヌルポインタは, 有効などんなポインタと比較しても等しくならないことが保証されているため, ポインタを返す関数の異常時の戻り値として使える. 例: 連結リストの末尾データ

NULL, 0, '\0'

文字列の終端は ナル文字('\0') である. ヌルポインタ(NULL)で終端するわけではない.

ナル文字は, 全てのbitが0であるバイトであり, 即ち値がゼロのchar型である.

ヌルポインタは、有効などんなポインタと比較しても等しくならないことが保証されているため、ポインタを返す関数の異常時の戻り値として使用できる。

添字演算子[]と配列

int *p;
int i;

//以下の2式同じ
*(p + i); 
p[i];

関数の仮引数の宣言

以下は全て同じ。 要素数が入っていたとしても無視される。

int func(int *a);
int func(int a[]);
int func(int a[10);

関数へのポインタ

int func(double d);
//関数funcへのポインタを格納するポインタ変数
int (*func_p)(double);

文字列リテラル

"" で囲まれた文字列を文字列リテラルと呼ぶ。 文字列リテラルの方は「charの配列」。 よって、式の中では「charへのポインタ」に読み替えられる。

char *str;
str = "abc;

charの配列を初期化する場合は例外

// OK
char str[] = "abc";
char str[] = {'a', 'b', 'c', '\0'};
// NG
char str[4];
str = "abc";

次の例は、charの配列ではなくポインタを初期化しているので、前述の例外には当てはまらない

char *str = "abc";

この場合、"abc"は通常通り「charの配列」であり、式の中なので「charへのポインタ」に読み替えられて、それがstrに代入される。

char *color_name[] = {
  "red",
  "green",
  "blue",
};

この例だと、識別子color_nameの型は「charへのポインタの配列」であり、 型分類である「配列」が、初期化子の最外周の中括弧に対応。 よって、"red"や"blue"は「charへのポインタ」に対応。

char color_name[][6] = {
  "red",
  "green",
  "blue",
}

この例では、 この例では、識別子color_nameは「charの配列(要素数6)の配列」となる。 "red"や"blue"は「charの配列(要素数6)」に対応。

その他

  • scanf()でintの値を入力してもらう場合には、変数名に&をつけて渡すが、何故文字列を入力して貰う場合、&をつけなくても良いのか
    • scanf()で文字列を入力して貰う場合、charの配列を渡すが、配列は式の中では先頭要素へのポインタに読み替えられるという規則により、ポインタになっており、そのポインタが値渡しされているので、scanf()側で呼び出し元の配列に結果を詰めることができる

参考文献

C言語ポインタ完全制覇

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment