Subsections

17章 データ, 配列, 関数の整理

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

17.1 配列でない変数, 構造体

構造体は配列でない変数と同じように代入を行ったり, 関数の引数として用いることができる.

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


double func(double a)
{
	double ret;

	ret = a + 1.0;
	return ret;
}

int main(void)
{
	double a = 2.0;
	double b, c;

	b = a;
	printf("b = %f\n", b);

	c = func(a);
	printf("c = %f\n", c);
	
	return EXIT_SUCCESS;
}

次のプログラムでは struct COMPLEX が 上のプログラムの doubleと同じように扱われていることに注意.

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

struct COMPLEX {
	double x;
	double y;
};

struct COMPLEX func(struct COMPLEX a)
{
	struct COMPLEX ret;

	ret.x = a.x + 1.0;
	ret.y = a.y + 1.0;

	return ret;
}

int main(void)
{
	struct COMPLEX a = {10.0, 20.0};
	struct COMPLEX b, c;

	b = a;
	printf("b = (%f, %f)\n", b.x, b.y);

	c = func(a);
	printf("c = (%f, %f)\n", c.x, c.y);
	
	return EXIT_SUCCESS;
}

[課題1] 複素数の和, 積を求める関数を追加し動作を確認できるようmainを変更せよ。

関数の引数をアドレスにする場合も, double 型と struct COMPLEX型が等価である.

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

void func(double *a)
{
	*a += 1.0;
}

int main(void)
{
	double a = 2.0;

	printf("a = %f\n", a);

	func(&a);
	printf("a = %f\n", a);
	
	return EXIT_SUCCESS;
}

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

void swap(double *a, double *b)
{
	double tmp;
	tmp = *a;
	*a = *b;
	*b = tmp;
}
int main(void)
{
	double x = 2.0;
	double y = 4.0;

	printf("(x, y) = (%f, %f)\n", x, y);
	swap(&x, &y);
	printf("(x, y) = (%f, %f)\n", x, y);

	return EXIT_SUCCESS;
}

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

struct COMPLEX {
	double x;
	double y;
};


// (*a).x の部分は a->x と表記できる.

void func(struct COMPLEX *a)
{
	(*a).x += 1.0;
	(*a).y += 2.0;
}

int main(void)
{
	struct COMPLEX a = {10.0, 20.0};

	printf("a = (%f, %f)\n", a.x, a.y);
	func(&a);
	printf("a = (%f, %f)\n", a.x, a.y);
	
	return EXIT_SUCCESS;
}

[課題2] 上のプログラムを参考にして, 二つの複素数のを入れ替えるプログラムを作成せよ.

typedefにより定義しなおすと, double型や int型などの予め用意された型の変数と同様に, 自分で定義した構造体を扱うことができる.

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

struct COMPLEX {
	double x;
	double y;
};

typedef struct COMPLEX complex;

void func(complex *a)
{
	(*a).x += 1.0;
	(*a).y += 2.0;
}

int main(void)
{
	complex a = {10.0, 20.0};

	printf("a = (%f, %f)\n", a.x, a.y);
	func(&a);
	printf("a = (%f, %f)\n", a.x, a.y);
	
	return EXIT_SUCCESS;
}

17.2 配列

構造体の配列はdouble型の配列と同じように代入できる.

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

#define N 5

int main(void)
{
	int i;
	double a[N] = {1.0, 2.0, 3.0, 4.0, 5.0};
	double b[N];

	// 直接 b = a のように代入はできないので各要素を代入する.
	for(i=0; i<N; i++)
		b[i] = a[i];

	for(i=0; i<N; i++)
		printf("%f ", b[i]);
	printf("\n");

	return EXIT_SUCCESS;
}

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

struct COMPLEX {
	double x;
	double y;
};

typedef struct COMPLEX complex;

#define N 5

int main(void)
{
	int i;
	complex a[N] = {{1.1, 1.2}, {2.1, 2.2}, {3.1, 3.2}, {4.1, 4.2}, {5.1, 5.2}};
	complex b[N];

	// 直接 b = a のように代入はできないので各要素を代入する.
	for(i=0; i<N; i++)
		b[i] = a[i];

	for(i=0; i<N; i++)
		printf("(%f, %f) ", b[i].x, b[i].y);
	printf("\n");

	return EXIT_SUCCESS;
}

配列を受け取る関数.

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

#define N 5

// アドレスを受け取る
void func(double *x)
{
	int i;
	for(i=0; i<N; i++)
		x[i] += 0.3;
}

void double_print(double *x)
{
	int i;
	for(i=0; i<N; i++)
		printf("%f ", x[i]);
	printf("\n");
}

int main(void)
{
	double a[N] = {1.0, 2.0, 3.0, 4.0, 5.0};
	// 配列名a は &a[0] すなわち aの先頭アドレスを表すことになっている.

	double_print(a);
	func(a);          // aの先頭アドレスが渡される。
	double_print(a);

	return EXIT_SUCCESS;
}

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

struct COMPLEX {
        double x;
        double y;
};

typedef struct COMPLEX complex;

#define N 5

// アドレスを受け取る
void func(complex *p)
{
	int i;
	for(i=0; i<N; i++){
		p[i].x += 0.2;
		p[i].y += 0.4;
	}
}


int main(void)
{
	int i;
	complex a[N] = {{1.1, 1.2}, {2.1, 2.2}, {3.1, 3.2}, {4.1, 4.2}, {5.1, 5.2}};
	// 配列名a は &a[0] すなわち aの先頭アドレスを表すことになっている.

	// a の値を表示 (1)
	for(i=0; i<N; i++)
		printf("(%f, %f) ", a[i].x, a[i].y);
	printf("\n");

	func(a);          // aの先頭アドレスが渡される。

	// a の値を表示 (2)
	for(i=0; i<N; i++)
		printf("(%f, %f) ", a[i].x, a[i].y);
	printf("\n");

	return EXIT_SUCCESS;
}

[課題3] 上のプログラムの 「a の値を表示」している部分2ヶ所を関数化せよ.

[課題4] 以下のプログラムを参考にして, 大きさが最大のベクトルを返す関数とその動作が確認できる main 関数を作成せよ.

ベクトルの長さを求めるプログラム:

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

struct VECTOR {
	char label[50];
	double x;
	double y;
	double z;
};

typedef struct VECTOR vector;

#define N 3

double func(vector p)
{
	double r;
	r = sqrt( p.x * p.x + p.y * p.y + p.z * p.z );
	return r;
}

int main(void)
{
	int i;
	vector a = {"A", 10.0, 20.0, 30.0};
	vector b[N] = {
		{"B1", 1.0, 2.0, 3.0},
		{"B2", 1.1, 2.1, 3.1},
		{"B3", 1.2, 2.2, 3.2},
	};

	printf("%sの長さ: %f\n", a.label, func(a));

	for(i=0; i<N; i++)
		printf("%sの長さ: %f\n", b[i].label, func(b[i]));
	
	 return EXIT_SUCCESS;
}

ベクトルの和 (合成ベクトル) を求めるプログラム:

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

struct VECTOR {
	char label[50];
	double x;
	double y;
	double z;
};

typedef struct VECTOR vector;

vector add(vector p, vector q)
{
	vector r;

	// sprintf は書式に従って第1引数の文字列に代入する関数.
	sprintf(r.label, "%s + %s", p.label, q.label);
	r.x = p.x + p.x;
	r.y = p.y + p.y;
	r.z = p.z + p.z;
	
	return r;
}

void vector_print(vector p)
{
	printf("%s = (%f, %f, %f)\n", p.label, p.x, p.y, p.z);
}

int main(void)
{
	vector a = {"A", 10.0, 20.0, 30.0};
	vector b = {"B",  1.0,  2.0,  3.0};
	vector c;

	vector_print(a);
	vector_print(b);
	
	c = add(a, b);

	vector_print(c);
	
	 return EXIT_SUCCESS;
}

[課題5] 2点間の距離を求めるプログラムを作成せよ.

[課題6] 2つのベクトルがなす角を求めるプログラムを作成せよ.

[課題7] 次に示すように構造体のメンバーとして配列を有する場合, 代入はどのように行われるか. また関数の引数としての渡すとき, どのように渡されるかを, double 型の変数と比較して答えよ。

struct DATA {
    double x[100];
    double y[100];
    int n;         // データ点の数
};

[課題8] 次に示すように構造体のメンバーとしてポインタ変数を有する場合について 課題8と同様の視点から double 型と比較せよ.

struct DATA {
    double *x;
    double *y;
    int n;         // データ点の数
};

struct DATA を用いた例:

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

struct DATA {
	double *x;
	double *y;
	int n;
};

typedef struct DATA data;

#define N 100

void data_print(data *d)
{
	int i;

	for(i=0; i < d->n; i++)
		printf("(%f, %f)\n", d->x[i], d->y[i]);
}

int main(void)
{
	int i;
	double x[N], y[N];
	data d;

	// 初期化
	d.x = x;  // 領域の確保された配列を指すようにすること.
	d.y = y;
	d.n = N;

	// y = x*x であるデータを代入
	for(i=0; i<N; i++){
		d.x[i] = (double)i;
		d.y[i] = d.x[i] * d.x[i] ;
	}

	data_print(&d);
	 return EXIT_SUCCESS;
}

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

struct DATA {
	double *x;
	double *y;
	int n;
};

typedef struct DATA data;

#define N 100

void data_init(data *d, double x, double y, int n)
{
	d->x = x;
	d->y = y;
	d->n = n;
}

void data_print(data *d)
{
	int i;

	for(i=0; i < d->n; i++)
		printf("(%f, %f)\n", d->x[i], d->y[i]);
}

void data_set(data *d)
{
	int i;
	for(i=0; i < d->n; i++){
		d->x[i] = (double)i;
		d->y[i] = d->x[i] * d->x[i] ;
	}
}

int main(void)
{
	double x[N], y[N];      // 領域の確保が必要.
	data d;
	
	data_init(&d, x, y, N); // 初期化
	data_set(&d); 	        // y = x*x であるデータを代入
	data_print(&d);         // データの値を表示

	return EXIT_SUCCESS;
}

[課題9] 上記2種類の構造体について, const で修飾された引数として関数に渡したとき, const が及ぶ範囲を答えよ.

(gcc の場合, const で修飾された変数に代入を行おうとするとコンパイラーがメッ セージを出力するので, それを基準とするとよい.)



(c)1999-2013 Tetsuya Makimura