#include <stdio.h>
#include <stdlib.h>
void func_A(int x)
{
	x = 10;
}
void func_B(int y[])
{
	y[1] = 20;
}
void func_C(int z)
{
	z = 30;
}
/*
  scanf は引数に & を付けますが, そのような変数を受けとるときは,
  *w のように * を付けます. 呼出側, 受け側ともにアドレスの
  受け渡しを指定しています.
 */
void func_D(int *w)
{
	*w = 40;
}
int main(void)
{
	int a;
	int b[10];
	a = 1;
	printf("a=%d\n", a);
	func_A(a); // 変数の値を渡す.
	printf("a=%d\n\n", a);
	b[1] = 2;
	printf("b[1]=%d\n", b[1]);
	func_B(b); // 配列の名前 (=アドレス)を渡す.
	printf("b[1]=%d\n\n", b[1]);
	b[1] = 2;
	printf("b[1]=%d\n", b[1]);
	func_C(b[1]); // 変数 b[1] の値を渡す.
	printf("b[1]=%d\n\n", b[1]);
	// func_A と比較すると, 
	// func_D は, 引数に & を付けアドレスに変換している点が異なる.
	a = 1;
	printf("a=%d\n", a);
	func_D( &a ); // 変数のアドレスを渡す.
	printf("a=%d\n\n", a); // 関数呼び出しを行った後は, 値が変わる.
	
	return EXIT_SUCCESS;
}
#include <stdio.h>
/*
  2つの3次元ベクトル a, b の和を計算し, ベクトル c に代入する関数.
  仮引数 c, a, b はアドレスを受けとるだけで,
  値を保持するための記憶域の確保は行わない.
*/
// 配列の場合は, アドレスを受け取る. 各要素の値は受け取らない.
void sum(double c[3], double a[3], double b[3])
{
	 int i;
	 for(i=0; i<3; i++)
		  c[i] = a[i] + b[i];
}
int main(void)
{
	/*
	  宣言を行った時点で, その変数や配列の値を記憶する領域が確保される.
	  そのブロックが終了するまでは, 他の変数がその領域を使用することはない.
	*/
	double x1[3] = {1.0, 2.0, 3.0};
	double x2[3] = {4.0, 5.0, 6.0};
	double x3[3]; // 呼び出し側で記憶域の確保が必要.
	sum(x3, x1, x2); // 配列の先頭のアドレスを渡している.
	printf("x1 + x2 = (%g, %g, %g)\n", x3[0], x3[1], x3[2]);
	 
	return 0;
}
関数の実引数として, 通常の変数や定数の値を用いることもできるし, それらのアドレスを用いることもできる.
詳細は第10章「ポインタ」ので学習することとして, アドレスやそれを操作するためのポインター変数を理解しておくとよい. 教科書の「配列」の章を, 天下り的でなく, 見通しよく理解できる。
変数を用いる場合には, メモリー(記憶域)上に必要な領域を確保し, そこに変数の値を保存する. 「値」は,そこに保存されている内容であり, 「アドレス」は, 保存されている場所である. 「ポインタ変数」はアドレスを格納するための変数である. 表1に通常の変数とポインター変数を並べて示す.
さらに, 配列まで含めたときの変数とアドレスの関係を2に示す.
以下に, アドレスを仮引数とする関数呼び出しの例を示す.
// ポインタ変数を引数とする関数
#include <stdio.h>
void func(double *x)
{
	 printf("(2) &x = %p\n", &x); // x のアドレス.
	 printf("(2) sizeof(x) = %u\n", sizeof(x)); // x のアドレス.
	 printf("(2)  x = %p\n",  x); // xの値 (ここでは a のアドレス)
	 printf("(2) *x = %f\n", *x); // *x の値.
	 *x = 20.0;
	 printf("(2) *x = %f\n", *x); // *x の値.
}
int main(void)
{
	 double a = 3.0;
	 printf("(1) a = %f\n",  a); // a の値
	 printf("(1) &a = %p\n", &a); // a のアドレス
	 printf("(1) sizeof(a) = %u\n", sizeof(a)); // a のサイズ
	 func(&a); // funcの引数のxに aのアドレス&a が代入される
	 printf("(3) a = %f\n",  a);
	 return 0;
}
次に, 配列を引数とする関数を示す. 配列名を引数とする場合, 配列のアドレスを関数に渡すことになる.
#include <stdio.h>
#define NUMBER 5
/* 配列の各要素に100を加える関数 */
void func(double a[NUMBER])
{
	 int i;
	 for(i=0; i<NUMBER; i++)
		  a[i] += 100.0;
}
int main(void)
{
	 int i;
	 double x[NUMBER] = {0.0}; // 全ての要素を 0.0 に初期化.
	 for(i=0; i<NUMBER; i++)
		  printf("x[%d] = %g\n", i, x[i]);
	 func(x);
	 for(i=0; i<NUMBER; i++)
		  printf("x[%d] = %g\n", i, x[i]);
	 
	 return 0;
}
List6-17のconstの使い方は, C言語の規格上は誤りである.
List6-17はgcc でコンパイルすると, 以下のメッセージが表示される.
List6-17org.c: In function ‘main’: List6-17org.c:19: warning: passing argument 1 of ‘mat_add’ from incompatible pointer type List6-17org.c:19: warning: passing argument 2 of ‘mat_add’ from incompatible pointer typeこれは, 19行目でmat_add 関数を呼び出す際の実引数maおよび mbと, 3行目の関数定義で使われている仮引数ma およびmbの型と異なることが原因である.
#include <stdio.h>
void mat_add(const int ma[2][3], const int mb[2][3], int mc[2][3])
{
	 int i, j;
	 for(i=0; i<2; i++)
		  for(j=0; j<3; j++)
			   mc[i][j] = ma[i][j] + mb[i][j];
}
int main(void)
{
	 int i, j;
	 int ma[2][3] = {{1, 2, 3}, {4, 5, 6}};
	 int mb[2][3] = {{7, 8, 9}, {10, 11, 12}};
	 int mc[2][3] = { 0 };
	 mat_add(ma, mb, mc);
	 for(i=0; i<2; i++) {
		  for(j=0; j<3; j++)
			   printf("%3d", mc[i][j]);
		  putchar('\n');
	 }
	 return 0;
}
mat_add( (const int (*)[3])ma, (const int (*)[3])mb, mc);
キャストを用いた修正版
#include <stdio.h>
/*
  関数呼出しの際に,
  【『xxx』へのポインター】を【『constのxxx』へのポインター】に渡すことはできる.
  しかし,  一般に
  【『xxx』へのポインター】を【『yyy』へのポインター】に渡すことはできない.
  【『「int」の配列』へのポインター】を【『「constのint」の配列』へのポインター】に
  渡すこともできない.
*/
/*
  C言語では, 関数に配列名を渡すときは, ポインタに変換される.
  この場合は,
  void mat_add(const int ma[2][3], const int mb[2][3], int mc[2][3]) や
  void mat_add(const int ma[][3], const int mb[][3], int mc[][3]) は
  void mat_add(const int (*ma)[3], const int (*mb)[3], int (*mc)[3]) と
  解釈されている. (int ma[] は int (*ma) であることに注意.)
  すわなちma, mbは,【『「const int」の配列 』へのポインタ 】である.
*/
void mat_add(const int (*ma)[3], const int (*mb)[3], int (*mc)[3])
{
	 int i, j;
	 for(i=0; i<2; i++)
		  for(j=0; j<3; j++)
			   mc[i][j] = ma[i][j] + mb[i][j];
}
int main(void)
{
	 int i, j;
	 int ma[2][3] = {{1, 2, 3}, {4, 5, 6}};
	 int mb[2][3] = {{7, 8, 9}, {10, 11, 12}};
	 int mc[2][3] = { {0}, {0} };
	 /*
	   main で宣言されている ma, mb は, 【『 「int」 の配列 』 へのポインタ】である.
	   一方, mat_add は 【『 「const int」 の配列 』 へのポインタ】を受け取ることに
	   なっているので, 以下の関数呼出しでは, 型が合わない (incompatible pointer type).
	 */
	 /*  mat_add(ma, mb, mc); */
	 /* 型を合わせるために, 以下のようにキャストする. */
	 mat_add( (const int (*)[3])ma, (const int (*)[3])mb, mc);
	 for(i=0; i<2; i++) {
		  for(j=0; j<3; j++)
			   printf("%3d", mc[i][j]);
		  putchar('\n');
	 }
	 return 0;
}
#include <stdio.h>
typedef const int (*CONST_MAT_PTR)[3];
typedef const int (CONST_MAT)[3];
void mat_add(const int (*ma)[3], const int (*mb)[3], int (*mc)[3])
{
	 int i, j;
	 for(i=0; i<2; i++)
		  for(j=0; j<3; j++)
			   mc[i][j] = ma[i][j] + mb[i][j];
}
int main(void)
{
	 int i, j;
	 int ma[2][3] = {{1, 2, 3}, {4, 5, 6}};
	 int mb[2][3] = {{7, 8, 9}, {10, 11, 12}};
	 int mc[2][3] = { {0}, {0} };
	 /* typedef により行列型を宣言しておくと, 以下のように簡潔に表現できる. */
	 mat_add((CONST_MAT_PTR)ma, (CONST_MAT_PTR)mb, mc);
	 /* または, */
	 mat_add((CONST_MAT *)ma, (CONST_MAT *)mb, mc);
	 for(i=0; i<2; i++) {
		  for(j=0; j<3; j++)
			   printf("%3d", mc[i][j]);
		  putchar('\n');
	 }
	 return 0;
}
#include <stdio.h>
/* 構造体の中に配列を宣言し, その中に配列を用意する. */
struct MAT {
	 int m[2][3];
};
void mat_add(const struct MAT *ma, const struct MAT *mb, int mc[2][3])
{
	 int i, j;
	 // const で宣言されていても, 参照は可能.
	 for(i=0; i<2; i++)
		  for(j=0; j<3; j++)
			   mc[i][j] = ma->m[i][j] + mb->m[i][j];
	 // ma->m[1][1] = 50; // エラー: const struct なので 代入不可.
}
int main(void)
{
	 int i, j;
	 struct MAT ma = {{{1, 2, 3}, {4, 5, 6}}};
	 struct MAT mb = {{{7, 8, 9}, {10, 11, 12}}};
	 int mc[2][3] = { {0}, {0} };
	 mat_add(&ma, &mb, mc);
	 
	 for(i=0; i<2; i++) {
		  for(j=0; j<3; j++)
			   printf("%3d", mc[i][j]);
		  putchar('\n');
	 }
	 return 0;
}
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
	 const int a = 1;
	 const int b = 2;
	 int c = 3;
    
	 const int *x;
	 int *y;         // y:  pointer to int
	 // [A] const int *x  <- const int a は型が一致するので可.
	 x = &a;         // xの値 すなわち xの指す先を変更することはできる.
	 /* ...ここで xを用いた処理. a の値を参照することができる. */
	 x = &b;         // xの値 すなわち xの指す先を変更することはできる.
                     // xが指す先の内容(const int)を変更することはできない.
	 // *x = 10;     // この行はエラー
	 // [B] const int *x  <- int c は型が異なるが型変換される.
	 x = &c;
	 // *x = 10;     // ただし, x は const int を指すので代入は不可.
	 // [C] int *y  <- const int a は型が異なるが型変換される.
	 // y = &a;      // 不可. この代入が許されると,
	 // *y = 10;     // aの値を変更できてしまう. const で宣言した意味がなくなる.
	 
	 // [D]	 int (*y)  <- int c[2] は型が一致するので可.
	 y = &c;         // これは y, c ともに const int でなく int 型を指すので可.
	 *y = 10;        // 当然代入もできる.
	 
	 printf("%d\n", *x);
	 
	 return EXIT_SUCCESS;
}
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
	 int i;
	 const int a[2] = {1,2};
	 const int b[2] = {1,2};
	 int c[2] = {1,2};
    
	 const int (*x);
	 int (*y);       // y:  pointer to int
	 // [A] const int (*x)  <- const int a[2] は型が一致するので可.
	 x = a;            // xの値 すなわち xの指す先を変更することはできる.
	 /* ...ここで xを用いた処理. a の値を参照することができる. */
	 x = b;            // xの値 すなわち xの指す先を変更することはできる.
                       // xが指す先の内容(const int)を変更することはできない.
	 // x[1] = 10;     // この行はエラー
	 // [B] const int (*x)  <- int c[2] は型が異なるが型変換される.
	 x = c;
	 // x[1] = 10;     // ただし, x は const int を指すので代入は不可.
	 // [C] int (*y)  <- const int a[2] は型が異なるが型変換される.
	 // y = a;         // この代入が許されると,
	 // y[1] = 10;     // aの値を変更できてしまう. const で宣言した意味がなくなる.
	 
	 // [D]	 int (*y)  <- int c[2] は型が一致するので可.
	 y = c;            // これは y, c ともに const int でなく int 型を指すので可.
	 y[1] = 10;        // 当然代入もできる.
	 
	 for (i=0; i<2; i++)
		  printf("%d\n", x[i]);
	 
	 return EXIT_SUCCESS;
}
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
	 int i, j;
	 const int a[3][2] = { {1,2}, {3,4}, {5,6}};
	 const int b[3][2] = { {1,2}, {3,4}, {5,6}};
	 int c[3][2] = { {1,2}, {3,4}, {5,6} };
    
	 const int (*x)[2];
	 int (*y)[2];       // y:  pointer to array[2] of int
	 // [A] const int (*x)[2]  <- const int a[3][2] は型が一致するので可.
	 x = a;             // xの値 すなわち xの指す先を変更することはできる.
	 /* ...ここで xを用いた処理. a の値を参照することができる. */
	 x = b;            // xの値 すなわち xの指す先を変更することはできる.
                       // xが指す先の内容(const int)を変更することはできない.
	 // x[1][1] = 10;  // この行はエラー
	 // [B] const int (*x)[2]  <- int c[3][2] は型が異なるので不可.
	 // x = c;         // C言語の規格上はエラー. エラーにならないコンパイラもある.
	 // [C] int (*y)[2]  <- const int a[3][2] は型が異なるので不可.
	 // y = a;         // 不可. これが許されると,
	 // y[1][1] = 10;  // aの値を変更でしまい, a をconst で宣言した意味がなくなる.
	 
     // [D]	 int (*y)[2]  <- int c[3][2] は型が一致するので可.
	 y = c;            // これは y, c ともに const int でなく int 型なので可.
	 y[1][1] = 10;     // 当然代入もできる.
	 
	 for (i=0; i<3; i++)
		  for (j=0; j<2; j++)
			   printf("%d\n", x[i][j]);
	 
	 return EXIT_SUCCESS;
}
(c)1999-2013 Tetsuya Makimura