Subsections

16章 H24.2.29: データ, 配列, 関数の整理

データ, ポインタ, これらの配列とそれを操作するための関数への受け渡しの整理.

16.1 本日の課題

  1. fscanf関数を用いファイルに保存されている値を入力し, printf関数を用いて標準出力(画面)に出力するプログラムを作成せよ.
  2. 以下の4つのプログラムを作成せよ. 3次元のベクトルを配列および構造体で表現し, そのベクトルに演算を行うプログラムを作成せよ. (演算の例: 平行移動 $\overrightarrow{x'} = \overrightarrow{x} + (1, 2, 3)$, 外積, ベクトルへの行列の演算など)

    ただし, 演算を行う部分は関数で実現すること. また, 配列と構造体のそれぞれの場合について, 関数に値を渡す場合と, アドレスを渡す場合のそれぞれのプログラムを作成すること.

作成したプログラム(.cが付くファイル)をメールで提出する. さらに課題1で用いた入力ファイルを提出すること. 本文に, 学籍番号, 氏名を明記すること.

16.2 まとめ

16.2.1 変数, 構造体

main関数内で使う.

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	double x, y;

	x = 3.0;
	y = x;

	printf("y=%f\n", y);
	
	return EXIT_SUCCESS;
}
#include <stdio.h>
#include <stdlib.h>

typedef struct {
	double v1;
	double v2;
	double v3;
} VECTOR;

int main(void)
{
	VECTOR x = {1.0, 2.0, 3.0}; // 初期化
	VECTOR y;

	y = x; // 代入

	printf("y.v1=%f\n", y.v1);
	printf("y.v2=%f\n", y.v2);
	printf("y.v3=%f\n", y.v3);
	
	 return EXIT_SUCCESS;
}

関数に渡す.

#include <stdio.h>
#include <stdlib.h>

void func(double y)	// 	y = x; のように代入される.
{
	printf("y=%f\n", y);
	y=10.0;
}

int main(void)
{
	double x = 3.0;

	func(x);
	printf("x=%f\n", x); // x の値は変わらない.
	
	return EXIT_SUCCESS;
}
#include <stdio.h>
#include <stdlib.h>

typedef struct {
	double v1;
	double v2;
	double v3;
} VECTOR;



void func(VECTOR y) // y = x; のように代入される.
{
	printf("y.v1=%f\n", y.v1);
	printf("y.v2=%f\n", y.v2);
	printf("y.v3=%f\n", y.v3);

	y.v1 = 10.0;
}

int main(void)
{
	VECTOR x = {1.0, 2.0, 3.0};

	func(x);
	printf("x.v1=%f\n", x.v1); // x.v1の値は変わらない.
	
	 return EXIT_SUCCESS;
}

関数から返す.

#include <stdio.h>
#include <stdlib.h>

double func(void)
{
	double x;

	x = 3.0;

	return x;
}

int main(void)
{
	double y;

	y = func();

	printf("y=%f\n", y);
	
	return EXIT_SUCCESS;
}
#include <stdio.h>
#include <stdlib.h>

typedef struct {
	double v1;
	double v2;
	double v3;
} VECTOR;

VECTOR func(void)
{
	VECTOR x = {1.0, 2.0, 3.0};
	return x; // 関数は 構造体を返すことができる.
}

int main(void)
{
	VECTOR y;

	y = func();

	printf("y.v1=%f\n", y.v1);
	printf("y.v2=%f\n", y.v2);
	printf("y.v3=%f\n", y.v3);
	
	 return EXIT_SUCCESS;
}

16.2.2 変数, 構造体へのポインタ

main 関数内で用いる.

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	double x;
	double *y;

	x = 3.0;
	y = &x;

	printf("y=%f\n", *y);

	*y = 10.0;
	printf("x=%f\n", x); // の値は書き換えれていにことに注意.
	
	return EXIT_SUCCESS;
}
#include <stdio.h>
#include <stdlib.h>

typedef struct {
	double v1;
	double v2;
	double v3;
} VECTOR;

int main(void)
{
	VECTOR x = {1.0, 2.0, 3.0};
	VECTOR *y;

	y = &x;

	printf("y.v1=%f\n", y->v1); // y.v1 ではないことに注意.
	printf("y.v2=%f\n", y->v2);
	printf("y.v3=%f\n", y->v3);
	
	y->v1 = 10.0;
	printf("x.v1 = %f\n", x.v1); // x.v1 の値が書き換えられていることに注意.

	return EXIT_SUCCESS;
}

関数に渡す.

#include <stdio.h>
#include <stdlib.h>

// 	y = x; のように代入される.
// x のアドレスを受け取ることになる.
void func(double *y)
{
	printf("y=%f\n", *y); // ポインタ変数の復習をしておきましょう.
	*y=10.0;
}

int main(void)
{
	double x = 3.0;

	func(&x); // アドレスを渡す.
	printf("x=%f\n", x); // x の値は変わる.
	
	return EXIT_SUCCESS;
}
#include <stdio.h>
#include <stdlib.h>

typedef struct {
	double v1;
	double v2;
	double v3;
} VECTOR;

void func(VECTOR *y) // アドレスを受け取る. y = x; のように代入される.
{
	printf("y->v1=%f\n", y->v1); // y はポインタ変数であるので
	printf("y->v2=%f\n", y->v2); // y.v2 ではなく,
	printf("y->v3=%f\n", y->v3); // y->v2 または (*y).v2

	y->v1 = 10.0; // アドレスを受け取っているので x.v1 に代入していることになる.
}

int main(void)
{
	VECTOR x = {1.0, 2.0, 3.0};

	func(&x); // アドレスを渡す.
	printf("x.v1=%f\n", x.v1); // x.v1の値は変わる.
	
	 return EXIT_SUCCESS;
}

関数から返す.

#include <stdio.h>
#include <stdlib.h>

double a;

double *func(void)
{
	a = 10.0;
	return &a; // アドレスを返す.
}

// この関数は誤り.
double *func_wrong(void)
{
	double x; // このx はこの関数内でのみ使用できる.
	x = 10.0;
	return &x;
	// アドレスを返すことはできるが, そのアドレス上の領域は,
	// この関数から戻ったときに別の変数に使われる.
}

int main(void)
{
	double y;

	y = *func(); // 返されたアドレス上の値を得る.
	printf("y=%f\n", y);
        
	return EXIT_SUCCESS;
}
#include <stdio.h>
#include <stdlib.h>

typedef struct {
	double v1;
	double v2;
	double v3;
} VECTOR;

VECTOR a;

VECTOR *func(void)
{
	a.v1 = 1.0;
	a.v2 = 2.0;
	a.v3 = 3.0;

	return &a; // アドレスを返す
}

int main(void)
{
	VECTOR y;

	y = *func(); // 返されたアドレス上の値を得る.
	// もし func 内で変数を宣言し, その変数のアドレスを返されても
	// ここでは その変数の値は使用できない.
	printf("y.v1=%f\n", y.v1);
	
	 return EXIT_SUCCESS;
}

16.2.3 変数, 構造体の配列

main関数内で使う.

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	double x[3] = {1.0, 2.0, 3.0}; // 初期化
	double y[3];

	y[0] = x[0]; // 代入
	y[1] = x[1];
	y[2] = x[2];

	printf("y[0]=%f\n", y[0]);
	printf("y[1]=%f\n", y[1]);
	printf("y[2]=%f\n", y[2]);
	
	return EXIT_SUCCESS;
}
#include <stdio.h>
#include <stdlib.h>

typedef struct {
	double v1;
	double v2;
	double v3;
} VECTOR;

int main(void)
{
	VECTOR x[2] = {{1.0, 2.0, 3.0}, {10.0, 20.0, 30.0}};

	printf("x[0].v1=%f\n", x[0].v1);
	printf("x[1].v1=%f\n", x[1].v1);

	return EXIT_SUCCESS;
}

関数に渡す. 配列のコピーを渡しているのではなく, アドレスを渡していることに注意.

#include <stdio.h>
#include <stdlib.h>

// 配列の先頭要素のアドレスを受け取る.
// ポインタ変数と同じ.
void func(double y[])
{
	printf("y[0]=%f\n", y[0]);
	y[0] = 10.0;
}

int main(void)
{
	double x[3] = {1.0, 2.0, 3.0};

	func(x);
	printf("x[0] = %f\n", x[0]); // 値が書き換わることに注意.
	
	 return EXIT_SUCCESS;
}
#include <stdio.h>
#include <stdlib.h>

typedef struct {
	double v1;
	double v2;
	double v3;
} VECTOR;

void func(VECTOR y[]) // アドレスを受け取る. y = x; のように代入される.
{
	printf("y[0].v1=%f\n", y[0].v1);

	y[0].v1 = 10.0;
}

int main(void)
{
	VECTOR x[2] = {{1.0, 2.0, 3.0}, {10.0, 20.0, 30.0}};

	func(x); // アドレスを渡す.
	printf("x[0].v1=%f\n", x[0].v1); // x[0].v1の値は変わる.
	
	 return EXIT_SUCCESS;
}

関数から返すことはできない.

#include <stdio.h>
#include <stdlib.h>

double *func(void)
{
	double a[3] = {1.0, 2.0, 3.0};

	return a; // 関数は配列を返すことはできないので誤り.
}
	


int main(void)
{
	double *p;

	p = func();
	printf("p[0] = %f\n", p[0]);
	
	 return EXIT_SUCCESS;
}

16.2.4 ポインタと配列

main 関数内で用いる.

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	double x[3] = {1.0, 2.0, 3.0};
	double *y;

	y = x; // 配列名は配列の先頭の要素のアドレスを表す.
	
	printf("y[0]=%f\n", y[0]); // *(y+i) と y[i] は同じ意味.
	printf("y[1]=%f\n", y[1]);
	printf("y[2]=%f\n", y[2]);
	
	y[0] = 10.0;
	printf("x[0] = %f\n", x[0]); // x[0] の値は書き換えられていることに注意.
	
	return EXIT_SUCCESS;
}

関数に渡す.

#include <stdio.h>
#include <stdlib.h>

// 配列の先頭要素のアドレスを受け取る.
// 配列と同様に扱うことができる.
void func(double *y)
{
	printf("y[0]=%f\n", y[0]);
	printf("*y=%f\n", *y);
	y[0] = 10.0;
}

int main(void)
{
	double x[3] = {1.0, 2.0, 3.0};

	func(x);
	printf("x[0] = %f\n", x[0]); // 値が書き換わることに注意.
	
	 return EXIT_SUCCESS;
}
#include <stdio.h>
#include <stdlib.h>

typedef struct {
	double v1;
	double v2;
	double v3;
} VECTOR;

void func(VECTOR *y) // アドレスを受け取る. y = x; のように代入される.
{
	printf("y[1].v1=%f\n", (y+1)->v1);
	// printf("y[1].v1=%f\n", (*(y+1)).v1);
	// printf("y[1].v1=%f\n", y[1].v1); と同等.

	(y+1)->v1 = 10.0;
	// y[1].v1 = 10.0; と同等.
}

int main(void)
{
	VECTOR x[2] = {{1.0, 2.0, 3.0}, {10.0, 20.0, 30.0}};

	func(x); // アドレスを渡す.
	printf("x[0].v1=%f\n", x[0].v1); // x[0].v1の値は変わる.
	
	 return EXIT_SUCCESS;
}

16.2.5 注意すべき事柄

16.2.6 ポインタ変数

ポインタ変数はアドレスを代入してから用いる. この観点から, 以下はすべて誤った用法.

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	// ポインタ変数はy = &x;のようにアドレスを代入してから使う.
	
	printf("*y=%f\n", *y); // 誤り.
	*y = 10.0; // 誤り.
	printf("*y=%f\n", *y); // 誤り
	
	return EXIT_SUCCESS;
}
#include <stdio.h>
#include <stdlib.h>

typedef struct {
	double v1;
	double v2;
	double v3;
} VECTOR;

// ポインタ変数は, VECTOR x = {1.0, 2.0, 3.0}; y = &x; のように
// アドレスを代入してから用いる必要がある.

int main(void)
{
	VECTOR *y;

	y->v1 = 10.0; // 誤り.
	printf("x.v1 = %f\n", x->v1); // 誤り.

	return EXIT_SUCCESS;
}
#include <stdio.h>
#include <stdlib.h>

// ポインタ変数はアドレスを代入してから使う.
// double x[3] = {1.0, 2.0, 3.0}; のように記憶域上の領域を用意しておいてから,
// y = x; のようにアドレスを代入する必要がある.

int main(void)
{
	double *y;

	y[0] = 10.0; // 誤り.
	printf("y[0] = %f\n", y[0]); //誤り.
	
	return EXIT_SUCCESS;
}



(c)1999-2013 Tetsuya Makimura