ポインタ (Pointer)

配列型とメモリ領域

mem_addr.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>

int main(void){
  char str[] = "ABCDEFG";
  printf("str[-3] = '%2c' [%p]\n" ,str[-3], str - 3);
  printf("str[-2] = '%2c' [%p]\n" ,str[-2], str - 2);
  printf("str[-1] = '%2c' [%p]\n" ,str[-1], str - 1);
  
  printf("\n");
  
  printf("str[ 0] = '%c' [%p]\n" ,str[0], str + 0);
  printf("str[ 1] = '%c' [%p]\n" ,str[1], str + 1);
  printf("str[ 2] = '%c' [%p]\n" ,str[2], str + 2);
  printf("str[ 3] = '%c' [%p]\n" ,str[3], str + 3);
  printf("str[ 4] = '%c' [%p]\n" ,str[4], str + 4);
  printf("str[ 5] = '%c' [%p]\n" ,str[5], str + 5);
  printf("str[ 6] = '%c' [%p]\n" ,str[6], str + 6);
  printf("str[ 7] = '%2c' [%p]\n" ,str[7], str + 7);

  printf("\n");
  
  printf("str[ 8] = '%c' [%p]\n" ,str[8], str + 8);
  printf("str[ 9] = '%c' [%p]\n" ,str[9], str + 9);
  printf("str[10] = '%c' [%p]\n" ,str[10], str + 10);

  return 0;
}

mem_addr.c の実行結果は:

[cactus:~/code_c/c_tuts]% ./mem_addr
str[-3] = ' ' [0x7fff5fbff3cd]
str[-2] = ' ' [0x7fff5fbff3ce]
str[-1] = ' ' [0x7fff5fbff3cf]

str[ 0] = 'A' [0x7fff5fbff3d0]
str[ 1] = 'B' [0x7fff5fbff3d1]
str[ 2] = 'C' [0x7fff5fbff3d2]
str[ 3] = 'D' [0x7fff5fbff3d3]
str[ 4] = 'E' [0x7fff5fbff3d4]
str[ 5] = 'F' [0x7fff5fbff3d5]
str[ 6] = 'G' [0x7fff5fbff3d6]
str[ 7] = ' ' [0x7fff5fbff3d7]

str[ 8] = '?' [0x7fff5fbff3d8]
str[ 9] = 'a' [0x7fff5fbff3d9]
str[10] = '?' [0x7fff5fbff3da]

mem_addr2.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#include <stdio.h>

int main(void){
  unsigned int m[5] = {3, 7, 10};
  
  printf("m[0] = %2d [%p]\n", m[0], m + 0);
  printf("m[1] = %2d [%p]\n", m[1], m + 1);
  printf("m[2] = %2d [%p]\n", m[2], m + 2);
  printf("m[3] = %2d [%p]\n", m[3], m + 3);
  printf("m[4] = %2d [%p]\n", m[4], m + 4);
  
  return 0;
}

mem_addr2.c の実行結果は:

[cactus:~/code_c/c_tuts]% ./mem_addr2
m[0] =  3 [0x7fff5fbff3b0]
m[1] =  7 [0x7fff5fbff3b4]
m[2] = 10 [0x7fff5fbff3b8]
m[3] =  0 [0x7fff5fbff3bc]
m[4] =  0 [0x7fff5fbff3c0]

間接演算子 *

アドレスからそのメモリ箇所に記憶される値を参照することを [間接参照] (indirection) と言う. 間接参照するための演算子が [間接演算子]である

間接演算子 (Indirection Operator):

*オペランド

indirect.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#include <stdio.h>

int main(void){
  char str[] = "WTOPIA.";

  printf("str[0] = '%c' [%p]\n", *(str), str+0);
  printf("str[1] = '%c' [%p]\n", *(str+1), str+1);
  printf("str[2] = '%c' [%p]\n", *(str+2), str+2);
  printf("str[3] = '%c' [%p]\n", *(str+3), str+3);
  printf("str[4] = '%c' [%p]\n", *(str+4), str+4);
  printf("str[5] = '%c' [%p]\n", *(str+5), str+5);
  printf("str[6] = '%c' [%p]\n", *(str+6), str+6);
  printf("str[7] = '%2c' [%p]\n", *(str+7), str+7);

  return 0;
}

indirect.c の実行結果は:

[cactus:~/code_c/c_tuts]% ./indirect
str[0] = 'W' [0x7fff5fbff3d0]
str[1] = 'T' [0x7fff5fbff3d1]
str[2] = 'O' [0x7fff5fbff3d2]
str[3] = 'P' [0x7fff5fbff3d3]
str[4] = 'I' [0x7fff5fbff3d4]
str[5] = 'A' [0x7fff5fbff3d5]
str[6] = '.' [0x7fff5fbff3d6]
str[7] = ' ' [0x7fff5fbff3d7]

indirect2.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#include <stdio.h>

int main(void){
  unsigned int m[5] = {3, 7, 10};
  printf("m[0] = %2d [%p]\n", *(m+0), m+0);
  printf("m[1] = %2d [%p]\n", *(m+1), m+1);
  printf("m[2] = %2d [%p]\n", *(m+2), m+2);
  printf("m[3] = %2d [%p]\n", *(m+3), m+3);
  printf("m[4] = %2d [%p]\n", *(m+4), m+4);

  return 0;
}

indirect2.c の実行結果は:

[cactus:~/code_c/c_tuts]% ./indirect2
m[0] =  3 [0x7fff5fbff3b0]
m[1] =  7 [0x7fff5fbff3b4]
m[2] = 10 [0x7fff5fbff3b8]
m[3] =  0 [0x7fff5fbff3bc]
m[4] =  0 [0x7fff5fbff3c0]

ポインタ宣言子 char *p

pointer.c

1
2
3
4
5
6
7
8
9
#include <stdio.h>

int main(void){
  char str[] = "WTOPIA.";
  char *p = str;
  printf("str[0] = '%c' [%p]\n", *p, p);

  return 0;
}

pointer.c の実行結果は:

[cactus:~/code_c/c_tuts]% ./pointer
str[0] = 'W' [0x7fff5fbff3d0]

ポインタを含む和算

ptr_arith.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#include <stdio.h>

int main(void){
  char str[] = "WTOPIA.";
  char *p;

  for(p = str; *p != '\0'; p++)
    // '\0' にしなくてはいけない
    // '0' だったら知らない場所に指すかもしれない
    printf("'%c' %x [%p]\n", *p, *p, p);

  return 0;
}

ptr_arith.c の実行結果は:

[cactus:~/code_c/c_tuts]% ./ptr_arith
'W' 57 [0x7fff5fbff3c0]
'T' 54 [0x7fff5fbff3c1]
'O' 4f [0x7fff5fbff3c2]
'P' 50 [0x7fff5fbff3c3]
'I' 49 [0x7fff5fbff3c4]
'A' 41 [0x7fff5fbff3c5]
'.' 2e [0x7fff5fbff3c6]

ポインタを含む減算演算と ptrdiff_t 型変数

ptr_arith2.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#include <stdio.h>

int main(void){
  char str[] = "WTOPIA.";
  char *p;

  for(p = str; *p != '\0'; p++)
    printf("str[%td] '%c' %x [%p]\n", p - str, *p, *p, p);

  return 0;
}

ptr_arith2.c の実行結果は:

[cactus:~/code_c/c_tuts]% ./ptr_arith2
str[0] 'W' 57 [0x7fff5fbff3c0]
str[1] 'T' 54 [0x7fff5fbff3c1]
str[2] 'O' 4f [0x7fff5fbff3c2]
str[3] 'P' 50 [0x7fff5fbff3c3]
str[4] 'I' 49 [0x7fff5fbff3c4]
str[5] 'A' 41 [0x7fff5fbff3c5]
str[6] '.' 2e [0x7fff5fbff3c6]

添字演算子 (Subscript Operator)

subscript.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#include <stdio.h>

int main(void){
  char str[] = "WTOPIA.";
  int i;
  for(i = 0; str[i] != '\0'; i++)
    printf("str[%d] '%c' %x [%p]\n", i, *(str+i), *(str+i), str+i);

  return 0;
}

subscript.c の実行結果は:

[cactus:~/code_c/c_tuts]% ./subscript
str[0] 'W' 57 [0x7fff5fbff3c0]
str[1] 'T' 54 [0x7fff5fbff3c1]
str[2] 'O' 4f [0x7fff5fbff3c2]
str[3] 'P' 50 [0x7fff5fbff3c3]
str[4] 'I' 49 [0x7fff5fbff3c4]
str[5] 'A' 41 [0x7fff5fbff3c5]
str[6] '.' 2e [0x7fff5fbff3c6]

アドレス演算子 &

addr_opera.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>

int main(void){
  int x = 2, y = 3;
  int *p; // int *型変数 (int へのポインタ) の宣言

  printf("x = %d [%p]\n", x ,&x);
  printf("y = %d [%p]\n", y ,&y);

  p = &x;
  printf("p = %p\n", p);
  *p = 10; // * は間接演算子

  p = &y;
  printf("P = %p\n", p);
  *p = 10; // * は間接演算子

  printf("x = %d\n", x);
  printf("y = %d\n", y);

  return 0;
}

addr_opera.c の実行結果は:

[cactus:~/code_c/c_tuts]% ./addr_opera
x = 2 [0x7fff5fbff3cc]
y = 3 [0x7fff5fbff3c8]
p = 0x7fff5fbff3cc
P = 0x7fff5fbff3c8
x = 10
y = 10

NULL ポインタ (空ポインタ)

null_ptr.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>

int main(void){
  char *p;

  p = 0;
  if(p == NULL)
    printf("Yes! [%p]\n", p);

  p = (void *)0;
  if(p == NULL)
    printf("Yes! [%p]\n", p);
  if(!p)
    printf("Yes! [%p]\n", p);
  if(NULL == 0x00)
    printf("Yes! [%p]\n", p);

  return 0;
}

null_ptr.c の実行結果は:

[cactus:~/code_c/c_tuts]% ./null_ptr
Yes! [0x0]
Yes! [0x0]
Yes! [0x0]
Yes! [0x0]

ポインタへのポインタ: char **x

ptr_ptr.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

int main(void){

  char x[] = "testing";
  char *p;
  char **z;

  printf(" x = %p\n", x);

  p = x;
  printf(" p = %p, &p = %p\n", p, &p);

  z = &p;
  printf("*z = %p, z  = %p\n", *z, z);
  
  return 0;
}

ptr_ptr.c の実行結果は:

[cactus:~/code_c/c_tuts]% ./ptr_ptr
 x = 0x7fff5fbff3d0
 p = 0x7fff5fbff3d0, &p = 0x7fff5fbff3c8
*z = 0x7fff5fbff3d0, z  = 0x7fff5fbff3c8

ポインタの配列: char *x[]

ptr_array.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>

int main(void){
  char *x[10] = {
    "char",
    "short",
    "int",
    "long",
    "float",
    "double"
  };

  char *p;
  char **z;

  for(p = *x; *p != '\0'; p++)
    printf("%c\t(%d)\t[%p]\n", *p, *p, p);

  for(z = x; *z != NULL; z++)
    printf("%s\t[%p]\n", *z, *z);

  return 0;
}

ptr_array.c の実行結果は:

c	(99)	[0x100000efa]
h	(104)	[0x100000efb]
a	(97)	[0x100000efc]
r	(114)	[0x100000efd]
char	[0x100000efa]
short	[0x100000eff]
int	[0x100000f05]
long	[0x100000f09]
float	[0x100000f0e]
double	[0x100000f14]

ポインタの関数 f(char *x), f(int *x)

ptr_func.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>

void f(int *);

int main(void){
  int x = 2, y = 3;

  f(&x);
  f(&y);

  printf("x = %d\n", x);
  printf("y = %d\n", y);

  return 0;
}

void f(int *p){ // int *型変数 (int へのポインタ)
  *p = 10; // * は間接演算子
}

ptr_func.c の実行結果は:

[cactus:~/code_c/c_tuts]% ./ptr_func
x = 10
y = 10

func.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

void f(int);

int main(void){

  int x = 2;
  f(x);
  printf("(3) x = %d\n", x);
  
  return 0;
}

void f(int s){
  printf("(1) s = %d\n", s);
  s = 10;
  printf("(2) s = %d\n", s);
}

func.c の実行結果は:

[cactus:~/code_c/c_tuts]% ./func
(1) s = 2
(2) s = 10
(3) x = 2

ptr_func2.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#include <stdio.h>

void f(char *);

int main(void){

  char str[] = "WTOPIA.";
  f(str);

  return 0;
}

void f(char *str){
  char *p;
  for(p = str; *p != '\0'; p++)
    printf("[%p] '%c' %s\n", p, *p, p);
}

ptr_func2.c の実行結果は:

[cactus:~/code_c/c_tuts]% ./ptr_func2
[0x7fff5fbff3c0] 'W' WTOPIA.
[0x7fff5fbff3c1] 'T' TOPIA.
[0x7fff5fbff3c2] 'O' OPIA.
[0x7fff5fbff3c3] 'P' PIA.
[0x7fff5fbff3c4] 'I' IA.
[0x7fff5fbff3c5] 'A' A.
[0x7fff5fbff3c6] '.' .

ポインタを返す関数 char *f()

今度は逆に, アドレスを返す関数, [ポインタ関数] を作成してみよう.

func_ptr.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

char *f(void);

int main(void){
  char *y;
  y = f();

  printf("(2) %s [%p]\n", y, y);

  free(y);

  return 0;
}

char *f(void){
  char str[8] = "WTOPIA.";
  char *x;

  x = malloc( strlen(str) + 1 );
  /*
    malloc によって割り当てたメモリ領域は, 解放すれまでメモリは消費される.
  */
  if(x == NULL) exit(1);

  strncpy(x, str, strlen(str));
  printf("(1) %s [%p]\n", x, x);

  return x;
}

func_ptr.c の実行結果は:

[cactus:~/code_c/c_tuts]% ./func_ptr
(1) WTOPIA. [0x100100080]
(2) WTOPIA. [0x100100080]

メモリ・リーク (Memory Leak) とは,

プログラムが常に動作しているような場合, malloc で割り当てたメモリ領域を free で適切に解放しないと, メモリ消費が増えてゆく [メモリリーク] が起こる. メモリリークが起こり, メモリを消費尽くすと, システムがダウンする.

関数へのポインタ (*f)()

ptr_to_func.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include <stdio.h>

void f(void);

int main(void){
  void (*pf)(void) = f;
  pf();

  return 0;
}

void f(void){
  printf("Hello World!\n");
}

ptr_to_func.c の実行結果は:

[cactus:~/code_c/c_tuts]% ./ptr_to_func
Hello World!