アトリエ・エクレア

2DCG&3DCG, プログラミング, 日記などを掲載してます。

C言語

C言語

<免責事項> この記事は、管理人の個人的な覚書きのためのものです。内容に間違いがあっても責任は負いません。

  • msdn (C言語リファレンス) → Link
  • プログラム言語C (JISX3010) → Link

C言語

1972年にAT&Tベル研究所(アメリカ)のデニス・マカリスター・リッチー(Dennis MacAlistair Ritchie)氏が主体となって開発されたプログラミング言語。B言語の後継として開発されたためCと名づけられた。UNIXオペレーティングシステム作成のためのシステム記述言語として開発され、オペレーティングシステムカーネル向けの低レベルな記述ができることを特徴としている。UNIXの大部分はC言語によって書かれている。

ソースファイルの拡張子

sample.c のように、「 .c 」を使う。 ※ヘッダファイルは「 .h 」を使う。

出力

/* プリプロセッサ ディレクティブ */
#include <stdio.h>  /* 標準出力の関数printf()を使用するためインクルードする。*/

/* main関数 */
int main(void){
	printf("hello, world\n");  /* \nは改行 */
	return 0;
}

入力

/* プリプロセッサ ディレクティブ */
#include <stdio.h>  /* 標準入力の関数scanf()を使用するためインクルードする。*/

int main(void){
	double x;
	printf("実数を入力してください。\n");
	scanf("%lf", &x);                    /* 変換仕様%lf */
	printf("%fが入力されました。\n", x); /* 変換仕様%f ※%lfではないことに注意(C99からは%lfも使用可) */
}
#include <stdio.h>

int main(void){
	int x;
	printf("1文字入力してください。\n");
	x = getchar();  /* 1文字入力 */
	putchar(x);     /* 1文字出力 */
	printf("が入力されました。\n");

	return 0;
}
#include <stdio.h>

int main(void){
	char x, y;
	printf("1番目の1文字入力してください。\n");
	scanf("%c", &x);

	printf("2番目の1文字入力してください。\n");
	scanf("%*c%c", &y);  /* %*cで1文字目を読み飛ばす(\nが入るのを防ぐ) ※%*cの*は代入抑止のフラグ */
	/* または scanf(" %c", &y);  ブランクスペース ※入力前に空白や改行を読み飛ばす。但し空白文字は入力できなくなる */
	/* または while (getchar() != '\n'); scanf("%c", &y); */

	printf("1番目は%cです。\n2番目は%cです。\n", x, y);
	return 0;
}

コメント

// の一行コメントは、元々はC++の機能だったが、C99 から追加された。

/* 範囲コメント */

/* 複数行の
コメント */

// 一行コメント(改行まで)

変換仕様

#include <stdio.h>

int main(void){
	int x = 5;
	int *y;
	y = &x;

	printf("%c  は、1文字。  \n", 'A');
	printf("%s  は、文字列。 \n", "sample");

	printf("%d  は、10進数の 整数。      \n", -25);
	printf("%ld は、10進数の 倍精度整数。\n", -25);
	printf("%u  は、10進数の 符号無し 整数。      \n", 15);
	printf("%lu は、10進数の 符号無し 倍精度整数。\n", 15);

	printf("%o  は、 8進数の 整数。      \n", 9);
	printf("%lo は、 8進数の 倍精度整数。\n", 9);
	printf("%x  は、16進数の 整数。      \n", 19);
	printf("%lx は、16進数の 倍精度整数。\n", 19);

	printf("%f  は、小数形式浮動小数点数。\n", -5.72);
	printf("%lf は、小数形式浮動小数点数。\n", -5.72);   /* C99から */ 
	printf("%e  は、指数形式浮動小数点数。\n", 0.01);
	printf("%g  は、fまたはeの書式で適切なほう。\n", 0.01);

	printf("%p  は、ポインタの値。\n", y);     /* 結果は、変数xのアドレスと同じ。*/
	printf("降水確率100%%です。\n");           /* %(パーセント)記号を表示。*/

	return 0;
}

%と変換指定子(c,d,fなど)を用いて、引数の変換の型を指定する。
変換指定子は他にもある(nなど)。

scanf()では、float型は%f, double型は %lf で対応する。
printf()では、float型もdouble型も、%f で対応する。 (C99から、%lfも%fと同等として扱える)

特殊文字 (エスケープシーケンスの利用)

特殊文字の例 (※他にもある。)
文字 意味
¥a ベル ( 警告 )
¥b バックスペース
¥f フォームフィード: 改ページ
¥n 改行
¥r キャリッジリターン: 行頭へ復帰
¥t 水平タブ
¥v 垂直タブ
¥¥ 円記号 ( ¥ )
¥? リテラル疑問符 ( ? )
¥' 単一引用符: シングルクォーテーション ( ' )
¥'' 二重引用符: ダブルクォーテーション ( " )
¥0 NULL(ヌル)文字 ※ ヌルポインターとは異なるので注意
¥ooo 8進表記のASCII文字 ( 円記号と、1~ 3桁の8進数 )
¥xhh 16進表記のASCII文字 ( ¥xと、16進数)

※パーセント記号 ( % ) は、エスケープシーケンスではなく、
変換指定子を用いた変換仕様を用いる(→ %%)。

文字定数

文字定数: シングルクォーテーション ( ' ) で囲まれた、1文字の定数。
変換仕様では、%c を利用。

printf("%cは文字定数。\n", 'A');
printf("%cはダブルクォーテーション。\n", '\"');

文字列定数

文字列定数: ダブルクォーテーションで囲まれた、複数の文字による列。
変換仕様では、%s を利用。

printf("%sは文字列定数。\n", "hello, world");

整数定数

整数定数: 10進数, 8進数, 16進数の数値。
基本的に、符号有りint型。

符号無しint型で扱う場合、サフィックス(接尾子)として、 U または u を使う。 (ex. 25U, 75u)
符号有りlong型で扱う場合、サフィックスとして、 L または l を使う。 (ex. 25L, 75l)
符号無しlong型で扱う場合、サフィックスとして、 UL または ul を使う。 (ex. 25UL, 75ul)

8進数: プレフィックス(接頭子)として、0をつける。 (ex. 015, 09)
16進数: プレフィックスとして、0xまたは0Xをつける。 (ex. 0x15, 0x1A)

変換仕様では、int型の場合、%d を利用。
変換仕様では、long型の場合、%ld を利用。

printf("%dはint型の整数定数。\n", -57);
printf("%ldはlong型の整数定数。\n", -25L);

浮動小数点定数

浮動小数点定数: 符号付き実数を表す 10 進数
※符号付きであり、unsignedとして定義はできず、エラーになる (unsigned float→×, unsigned double→×) 。
基本的に、double型。

float型で扱う場合、サフィックス(接尾子)として、 F または f を使う。(ex. 3.14F, 2.38f)
long double型で扱う場合、サフィックスとして、 L または l を使う。(ex. 3.14L, 2.38l)

変換仕様では、double型の場合、%lf を利用。
変換仕様では、float型の場合、%f を利用。
※指数形式では、%eを利用。

printf("%lfはdouble型の浮動小数点定数。\n", 3.14);
printf("%fはfloat型の浮動小数点定数。\n", 3.14F);
printf("%eはfloat型の浮動小数点定数。\n", 3.14);

データ型

データ型の例 (※環境依存であり、値は目安。)
種類 データ型 サイズ 扱える値の種類 扱える値の範囲例
型無し void - - -
文字型 char 1 byte 英数字1文字 -128 ~ 127
unsigned char 1 byte 英数字1文字 (符号なし) 0 ~ 255
整数型 short int 2 byte 整数 -32768 ~ 32767
unsigned short int 2 byte 整数 (符号なし) 0 ~ 65535
int 4 byte 整数 -2147483648 ~ 2147483647
unsigned int 4 byte 整数 (符号なし) 0 ~ 4294967295
long int 4 byte 長整数 -2147483648 ~ 2147483647
unsigned long int 4 byte 長整数 (符号なし) 0 ~ 4294967295
浮動小数点型 float 4 byte 単精度浮動小数点数 3.4E-38 ~ 3.4E+38
double 8 byte 倍精度浮動小数点数 1.7E-308 ~ 1.7E+308
long double 8 byte 拡張精度浮動小数点数 1.7E-308 ~ 1.7E+308

変数

変数: データを一定期間記憶し、固有の名前(識別子)を付けたもの。

  • 英字、数字、アンダースコア(_)を利用する。※ 最初が数字からはダメ。
  • 環境により31文字まで。
  • 大文字、小文字は区別される。
  • キーワード(予約語)はダメ。
  • 型を指定する。
  • 規格C99より、変数宣言がブロックの先頭でなくても良くなった。
  • 関数の外で宣言 → グローバル変数。どの関数でも利用可能。
  • 関数(またはブロック)の中で宣言 → ローカル変数。宣言した関数(またはブロック)内でのみ利用可能。
  • 変数や関数が特定の名前で参照可能な範囲 → スコープ。
  • 異なる関数のローカル変数に同じ名前をつけてもよいが、異なる変数として扱われることになる。
  • グローバル変数とローカル変数に同じ名前をつけてもよいが、ローカル変数としての扱いが優先される。
  • ブロックの外側と内側で同じ変数名をつけてもよいが、ブロック内では内側の変数の扱いが優先される。
// (1)変数の宣言
int x, y;   // 宣言
x = 10;     // 代入
x = 50;     // 上書き
y = x;      // 代入 (yは50)

// (2)変数の初期化
int z = 10; // 初期化
#include <stdio.h>

int multiply(int x);
int increment1(void);
int increment2(void);

/* グローバル変数 ※関数外で宣言 */
int b = 50;         /* 動的グローバル変数 ※別のファイルでも利用可能 */
static int d = 70;  /* 静的グローバル変数 ※このファイル内での利用に限定される (static: 記憶クラス指定子) */

int main(void){
	int a = 75;             /* 動的ローカル変数 ※関数内で宣言 */

	printf("%d\n", b);      /* 結果: b==50  ※動的グローバル変数b */

	/* ブロックの先頭以外での変数宣言はC99から対応 */
	/* ローカル変数とグローバル変数が同じ名前の場合、ローカル変数が優先される */
	int b = 25;   /* ローカル変数のスコープは、宣言した場所から、その変数を囲むブロックの終了まで */
	printf("%d\n", b);      /* 結果: b==25  ※動的ローカル変数b */

	a = multiply(b) + a;    /* bは動的ローカル変数b */
	printf("%d\n", a);      /* 結果: a==40075 */
	if (a > 40000){
		int c = a - 40000;  /* cは動的ローカル変数c ※ブロック内で宣言 */
		printf("変数aの値は40000より%d大きい\n", c);    /* 結果: c==75 */
	}

	int x = 0;
	int y = 0;
	for (int i = 0; i < 10; i++){
		x = increment1();
		y = increment2();
	}
	printf("%d\n%d\n", x, y);  /* x==10, y==1 */

	return 0;
}

int multiply(int x){
	int a = 32;       /* 動的ローカル変数 ※別の関数のローカル変数と同じ変数名でも、別の変数として扱われる */
	x = x * a * b;    /* bは動的グローバル変数b */
	return x;
}

int increment1(void){
	static int a = 0; /* 静的ローカル変数aは、関数の処理が終了しても、プログラム終了まで保持される */
	a++;
	return a;
}

int increment2(void){
	int a = 0;        /* 動的ローカル変数aは、関数の処理が終了すると消滅 */
	a++;
	return a;
}

演算子

演算子の種類と優先順位 (※ 上:優先度高い。下:優先度低い。)
優先
順位
演算子 名前 演算子のグループ 結合規則
1 ( ) 関数呼び出し 関数呼び出し演算子
[ ] (配列の) 添字 添字演算子
. 直接メンバ参照(: ドット) 直接メンバ参照演算子
-> 間接メンバ参照(: アロー) 間接メンバ参照演算子
++ 後置インクリメント 算術演算子
-- 後置デクリメント
2 ! 論理否定 論理演算子
~ 補数 ビット演算子
+ 単項+ (プラス) 算術演算子
- 単項- (マイナス)
sizeof 記憶量 (: サイズ) sizeof演算子
++ 前置インクリメント 算術演算子
-- 前置デクリメント
& アドレス アドレス演算子
* 間接演算子 間接演算子
( ) 型変換 (: キャスト) 型変換演算子
3 % 剰余 算術演算子
* 乗算
/ 除算
4 + 加算
- 減算
5 << 左シフト ビット演算子
>> 右シフト
6 > 大なり (より大きい) 関係演算子
>= 大なりイコール(以上)
< 小なり (未満)
<= 小なりイコール (以下)
7 == 等価
!= 非等価
8 & ビット論理積 ビット演算子
9 ^ ビット排他的論理和
10 | ビット論理和
11 && 論理積 論理演算子
12 || 論理和
13 ? : 条件 条件演算子
14 = 単純代入 単純代入演算子
+= 加算代入 複合代入演算子
-= 減算代入
*= 乗算代入
/= 除算代入
%= 剰余代入
&= ビット論理積代入
^= ビット排他的論理和代入
|= ビット論理和代入
<<= 左シフト代入
>>= 右シフト代入
15 , 順次 順次演算子

<注意点>

インクリメント、デクリメント演算子は、前置と後置で処理が異なることに注意。

#include <stdio.h>

int main(void){
	int x, y, z;
	int a[2] = { 15, 75 };
	int b, c;
	int i;

	x = 5;
	y = ++x;      /* 加算してから代入 ※ x=x+1; → y=x; と同じ */
	x = 5;
	z = x++;      /* 代入してから加算 ※ z=x; → x=x+1; と同じ */
	printf("%d\t%d\n", y, z);     /* 結果: y==6, z==5 */

	i = 0;
	b = a[++i];   /* 加算してから代入 ※ i=i+1; → b=a[i]; と同じ */
	i = 0;
	c = a[i++];   /* 代入してから加算 ※ c=a[i]; → i=i+1; と同じ */
	printf("%d\t%d\n", b, c);     /* 結果: b==75, c==15 */
	return 0;
}

関係演算子や論理演算子による条件において、0以外の値 → 真, 0 → 偽 となる。

ステートメント

Nullステートメント : セミコロン( ; )だけ。実行しても何も起きない。

#include <stdio.h>

int main(void){
	int sample[20];
	int i;
	for (i = 0; i < 10; sample[i++] = 10){  /* 最初の10個の要素に値を代入 */
		;       /* Nullステートメント ※なにもしない */
	}
	for (i = 10; i < 20; sample[i++] = 25){  /* 残りの10個の要素に値を代入 */
		;       /* Nullステートメント ※なにもしない */
	}
	for (i = 0; i < 20; i++){
		printf("%d\n", sample[i]);
	}
	return 0;
}

returnステートメント :関数の実行を終了し、コントロールを呼び出し元の関数に戻す。呼び出し元の関数に値(戻り値)を返すこともできる。

  • returnステートメントを省略または、関数の終わりまでreturnステートメントが実行されなかった場合、return;が行われたとみなされる(戻り値は未定義)。
  • C99より、main関数に限り、戻り値の型がintであればreturn 0;があったと見なされる。
  • 戻り値が必要ない場合は、voidの戻り値の型を持つように関数を宣言する。
  • 既定の戻り値の型は intである。
#include <stdio.h>

int sq(int a);
void output(int b, int c);

int main(void){
	int x = 20;
	int y = 30;

	x = sq(x);
	y = sq(y);
	output(x, y);

	return 0;       /* returnステートメント */
}
int sq(int a){
	return(a*a);    /* returnステートメント */
}
void output(int b, int c){
	printf("%d\t%d\n", b, c);
	return;         /* returnステートメント */
}

式ステートメント :1 つの値, オブジェクト, メソッド, または名前空間に評価できる、1 つ以上のオペランドと 0 個以上の演算子のシーケンスによるステートメント。

#include <stdio.h>

int proc(int a, int b);
int f(int c);

int main(void){
	int x = 10;
	int y = 20;
	int z = 30;
	int arg1 = 40;
	int arg2 = 50;

	x = (y + 3);        /* 式ステートメント ※xは、y+3の値を代入される。*/
	x++;                /* 式ステートメント ※xは、インクリメントされる。*/
	x = y = 5;          /* 式ステートメント ※xとyの両方に、5が代入される。*/
	proc(arg1, arg2);   /* 式ステートメント ※関数を呼び出し、戻り値が返る。*/
	y = z = (f(x) + 3); /* 式ステートメント ※関数呼び出し。 */

	printf("%d\t%d\t%d\n", x, y, z);   /* 結果: x==5, y==28, z==28 */
	return 0;
}

int proc(int a, int b){
	return a + b;
}

int f(int c){
	return 5 * c;
}

複合ステートメント :波括弧{}で囲まれたステートメントの集合。ブロックのこと。

#include <stdio.h>

int main(void){
	int x = 100;
	int sample[5] = { 0, 1, 2, 3, 4 };
	int i = 3;

	if (i > 0){            /* 複合ステートメントの開始 */
		sample[i] = x;
		x++;
	}                      /* 複合ステートメントの終了 */
	printf("%d\t%d\n", sample[i], x);
	return 0;
}

ifステートメント :条件が真の場合に、指定した文を処理する。

#include <stdio.h>

int main(void){
	int num;
	printf("偶数を入力してください\n");
	scanf("%d", &num);
	if (!(num % 2)){      /* 条件判断 */
		printf("偶数%dが入力されました。\n", num); /* 1文のときは、ブロックにしなくてもいい */
	}
	printf("終了します。\n");
	return 0;
}

if~else :条件が真の場合と、偽の場合に、指定した文を処理する。

#include <stdio.h>

int main(void){
	int num;
	printf("整数を入力してください\n");
	scanf("%d", &num);
	if (!(num % 2)){      /* 条件判断 */
		printf("偶数%dが入力されました。\n", num); /* 1文のときは、ブロックにしなくてもいい */
	}
	else {
		printf("奇数%dが入力されました。\n", num); /* 1文のときは、ブロックにしなくてもいい */
	}
	printf("終了します。\n");
	return 0;
}

if~else if ~else :2つ以上の条件を判断し、処理する。

#include <stdio.h>

int main(void){
	int num;
	printf("整数を入力してください\n");
	scanf("%d", &num);
	if (!(num % 2) && num >= 20){      /* 条件判断 */
		printf("20以上の偶数%dが入力されました。\n", num);     /* 1文のときは、ブロックにしなくてもいい */
	}
	else if (!(num % 2)){
		printf("20より小さい偶数%dが入力されました。\n", num); /* 1文のときは、ブロックにしなくてもいい */
	}
	else{
		printf("奇数%dが入力されました。\n", num);             /* 1文のときは、ブロックにしなくてもいい */
	}
	printf("終了します。\n");
	return 0;
}

forステートメント :指定した回数だけステートメントを繰り返す

#include <stdio.h>

int main(void){
	int sum = 0;
	int i;

	for (i = 1; i <= 10; i++){   /* forステートメント */
		sum += i;
	}

	printf("1から10までの数の合計は%dです。\n", sum);
	return 0;
}

whileステートメント :指定した式が false になるまでステートメントを繰り返す。

#include <stdio.h>

int main(void){
	char string1[8] = "destiny";
	char string2[8] = "honesty";
	int i = 7;

	while (i >= 0){               /* whileステートメント */
		string1[i] = string2[i];  /* string2 から string1 に文字をコピー */
		i--;
	}

	printf("%s\n", string1);
	return 0;
}

do-whileステートメント :指定した式が false になるまでステートメントを繰り返す。ループの本体は少なくとも一度は常に実行される。

#include <stdio.h>

int main(void){
	char string1[8] = "destiny";
	char string2[8] = "honesty";
	int i = 7;

	do{                           /* do-whileステートメント */
		string1[i] = string2[i];  /* string2 から string1 に文字をコピー */
		i--;
	} while (i >= 0);

	printf("%s\n", string1);
	return 0;
}

switchステートメント :条件式の値に応じて、多分岐を行う。

#include <stdio.h>

int main(void){
	int a;
	printf("整数を入力してください。\n");
	scanf("%d", &a);

	switch (a){      /* switchステートメント */
	case 1:
		printf("1が入力されました。\n");
		break;
	case 2:
		printf("2が入力されました。\n");
		break;
	case 3:
	case 4:
		printf("3または4が入力されました。\n");
		break;
	case 5:
		printf("5が入力されました。\n");
		break;
	default:         /* defaultの処理が必要ない場合は省略可能 */
		printf("1~5以外の整数が入力されました。\n");
		break;
	}

	return 0;
}

continueステートメント :最も内側の、do, for, while のステートメントにおいて、残りのステートメントを実行せず、次のループに移る。

#include <stdio.h>

int main(void){
	int sum = 0;
	int i;

	for (i = 0; i <= 10; i++){
		if (i % 2){
			continue;     /* continueステートメント */
		}
		sum += i;
	}
	printf("0から10までの偶数の数の合計は%d\n", sum);  // 結果: sum==30

	return 0;
}

breakステートメント :最も内側の、do, for, switch または while のステートメントの実行を終了する。

#include <stdio.h>

int main(void){
	int flag = 0;
	int x = 0;
	int y = 0;
	int i, j;

	for (i = 0; i < 10; i++){
		for (j = 0; j < 10; j++){
			if (x*y == 50){
				flag = 1;
				break;    /* breakステートメント */
			}
			x++;
		}
		if (flag == 1){
			break;        /* breakステートメント */
		}
		y++;
	}

	printf("%d\t%d\n", x, y);  /* 結果: x==25, y==2 */
	return 0;
}

gotoステートメント :制御をラベルに移す。

#include <stdio.h>

int main(void){
	int x = 0;
	int y = 0;
	int i, j;

	for (i = 0; i < 10; i++){
		for (j = 0; j < 10; j++){
			if (x*y == 50){
				goto mylabel;   /* gotoステートメント */
			}
			x++;
		}
		y++;
	}

mylabel: printf("%d\t%d\n", x, y);  /* 結果: x==25, y==2 */
	return 0;
}

配列

配列 : 同じ型の値を複数まとめて記憶する機能。

#include <stdio.h>
#define NUM 3   /* マクロ */

int main(void){
	int sample1[3];                  /* 配列の宣言 ※要素数を利用 */
	int sample2[3] = { 25, 57, 12 }; /* 配列の初期化 ※初期化子を利用(25,57,12など) */
	int sample3[] = { 25, 57, 12 };  /* 配列の初期化では、要素数を省略可能 */
	int sample4[3] = { 25, 57 };     /* 要素数よりも初期化子が少ない場合、残りの要素に0が入る */
	int sample5[NUM] = { 25, 57, 12 };
	int i;

	sample1[0] = 25;  /* 配列要素への、値の代入 ※添字を利用 */
	sample1[1] = 57;
	sample1[2] = 12;

	for (i = 0; i < NUM; i++){
		printf("%d\t%d\t%d\t%d\t%d\n",
			sample1[i], sample2[i], sample3[i], sample4[i], sample5[i]); /* \tは水平タブ */
	}
	return 0;
}
#include <stdio.h>
#define NUM 8   /* マクロ */

int main(void){
	char str1[8];                                                 /* 文字配列の宣言 ※要素数を利用 */
	char str2[8] = { 'H', 'o', 'n', 'e', 's', 't', 'y', '\0' };   /* 文字配列の初期化 ※初期化子を利用('H','o'など) */
	char str3[] = { 'H', 'o', 'n', 'e', 's', 't', 'y', '\0' };    /* 文字配列の初期化では、要素数を省略可能 */
	char str4[8] = "Honesty";     /* ""による代入は、初期化のときのみ使用可能 ※自動的にNULL文字が代入される */
	char str5[] = "Honesty";      /* 文字配列の初期化では、要素数を省略可能 */
	char str6[NUM] = { 'H', 'o', 'n', 'e', 's', 't', 'y', '\0' }; /* マクロの利用 */
	int i;

	str1[0] = 'H';  /* 文字配列の要素への、値の代入 ※添字を利用 */
	str1[1] = 'o';
	str1[2] = 'n';
	str1[3] = 'e';
	str1[4] = 's';
	str1[5] = 't';
	str1[6] = 'y';
	str1[7] = '\0'; /* NULL文字 */

	/* 書き出し例 1 */
	for (i = 0; i < NUM; i++){
		printf("%c", str1[i]);
	}
	printf("\n");

	/* 書き出し例 2 */
	for (i = 0; str1[i] != '\0'; i++){
		printf("%c", str1[i]);
	}
	printf("\n");

	/* 書き出し例 3 */
	i = 0;
	while (str1[i]){  /* \0は、コード0 */
		printf("%c", str1[i]);
		i++;
	}
	printf("\n");

	/* 書き出し例 4 */
	printf("%s\n", str1);   /* 配列名は、先頭要素へのポインタのように扱われる。(但し他のアドレスを代入はできない) */

	// ------------------------------------------------------------------------------------------------------------------
	// 書き出し例 4 のやりかたで全ての結果を表示してみる
	printf("%s\t%s\t%s\t%s\t%s\t%s\n", str1, str2, str3, str4, str5, str6);

	return 0;
}

多次元配列 : 配列そのものを要素として持つ配列。

include <stdio.h>

int main(void){
	int i, j;
	int sample1[3][5];   /* 多次元配列の宣言 */

	/* 多次元配列の初期化 */
	int sample2[3][5] = { { 13, 42, 3, 12, 3 }, { 10, 9, 203, 62, 58 }, { 906, 0, 2058, 55, 7 } };

	/* 初期化では最初の配列要素数を省略可能 */
	int sample3[][5] = { { 13, 42, 3, 12, 3 }, { 10, 9, 203, 62, 58 }, { 906, 0, 2058, 55, 7 } };
	// ※これは無理 int sample4[][] = { { 13, 42, 3, 12, 3 }, { 10, 9, 203, 62, 58 }, { 906, 0, 2058, 55, 7 } };

	sample1[0][0] = 13;  /* 多次元配列への値の代入 */
	sample1[0][1] = 42;
	sample1[0][2] = 3;
	sample1[0][3] = 12;
	sample1[0][4] = 3;

	sample1[1][0] = 10;
	sample1[1][1] = 9;
	sample1[1][2] = 203;
	sample1[1][3] = 62;
	sample1[1][4] = 58;

	sample1[2][0] = 906;
	sample1[2][1] = 0;
	sample1[2][2] = 2058;
	sample1[2][3] = 55;
	sample1[2][4] = 7;

	for (i = 0; i < 3; i++){
		for (j = 0; j < 5; j++){
			printf("%d\t%d\t%d\n", sample1[i][j], sample2[i][j], sample3[i][j]);
		}
	}

	return 0;
}

関数

関数 : 識別子を持ち、いくつかの文がブロックでまとめられた、一連の命令群。

#include <stdio.h>

/* 関数形式マクロ ※引数の型は制限されない */
#define MULTIPLY(x, y) ((x) * (y))

/* 関数の定義 */
void display1(void){
	printf("関数1です。\n");

	/* サブルーチンからの復帰 */
	return;       /* 省略可 ※省略した場合もreturn;があると見なされる */
}

/* 関数プロトタイプ */
void display2(void);
double multiply(int x, double y);   /* 関数プロトタイプでは仮引数の名前を省略できる。
									但し、引数の意味や目的を、引数名から判断できなくなる。 */
int main(void){
	double x, y;
	display1();   /* 関数呼び出し */
	display2();   /* 関数呼び出し */
	x = multiply(5, 3.14);  /* 関数を呼び出し、戻り値を変数xに代入 ※5, 3.14 は実引数 */
	y = MULTIPLY(5, 3.14);  /* 通常の関数処理に比べて、関数形式マクロのほうが処理が早い場合がある */
	printf("%f\n%f\n", x, y);

	return 0;     /* C99(及びC++98)では、main関数に限り、戻り値の型がintであればreturn 0;があると見なされる */
}

/* 関数の定義 */
void display2(void){
	printf("関数2です。\n");
	return;       /* 省略可 */
}

/* 関数の定義 */
double multiply(int x, double y){  /* int x, double y は仮引数 */
	return x*y;   /* double型の戻り値を返す */
}

ファイルの分割

ヘッダファイル :外部に公開する個々の関数、オブジェクト、データ型などの宣言をする。

extern : 記憶クラス指定子の一つ。変数または関数を宣言し、外部リンケージを持つことを指定する。

<グローバル変数について>

  • 記憶クラス指定子と初期化子の無い外部データ宣言は仮定義となる。
    宣言された識別子がその後で定義されている場合、仮定義は externがあるのと同等に処理される。
    仮定義は参照宣言になる。
  • 識別子が、その識別子の以前の宣言が可視である有効範囲において、記憶クラス指定子externを伴って宣言される場合
    → 以前の宣言において内部結合又は外部結合が指定されているならば、新しい宣言における識別子は、以前の宣言と同じ結合をもつ。
    → 可視である以前の宣言がない場合、又は以前の宣言が無結合である場合、この識別子は外部結合をもつ。
  • グローバル変数はデフォルトで外部リンケージを持つ。
  • ヘッダファイルでグローバル変数を初期化すると、それをインクルードするソースファイルが複数ある場合に、初期化が重複するので、再定義エラーとなる。
  • ヘッダファイルをインクルードするソースファイルが一つであり、且つ、ソースファイルで初期化をしない場合は、ヘッダファイルで初期化をしてもエラーにならない。
  • ヘッダファイルでは宣言だけにするのが無難。
  • static(記憶クラス指定子の一つ)の利用で内部リンケージにすることが可能。

<関数ついて>

  • externを省略しても、externがあるのと同等なので、書いても書かなくても同じ。
    → 関数はデフォルトで外部リンケージを持つ。
  • ヘッダファイルで関数を定義すると、それをインクルードするソースファイルが複数ある場合に、定義が重複するので、再定義エラーとなる。
  • ヘッダファイルをインクルードするソースファイルが一つであり、且つ、ソースファイルで関数定義をしない場合は、ヘッダファイルで関数定義をしてもエラーにならない。
  • ヘッダファイルでは宣言(関数プロトタイプ)だけにするのが無難。
  • static(記憶クラス指定子の一つ)の利用で内部リンケージにすることが可能。
/* myheader.h */

/* インクルードガード ※インクルードが重複した時に、#ifndef ~ #endifまでの間のコードをコンパイルしない */
/* _MULTIPLY_H_の部分の文字は任意 */
#ifndef _MULTIPLY_H_         /* もしも_MULTIPLY_H_が未定義ならば#endifまでのコードをコンパイルする */
#define _MULTIPLY_H_

/* グローバル変数の宣言 */
extern int gblvar;           /* extern : 変数または関数を宣言し、外部リンケージを持つことを指定する */

/* 関数プロトタイプ */
int multiply(int x, int y);  /* 関数プロトタイプにより、関数の宣言をする */

#endif
/* multiply.c */

/* 自作のヘッダファイルのインクルード */
#include "myheader.h"  /* カレントディレクトリからのインクルード */

/* グローバル変数の定義 */
int gblvar = 10;

/* 関数の定義 */
int multiply(int x, int y){
	return gblvar * x * y;
}
/* sample.c */

/* 標準ライブラリのヘッダファイルのインクルード */
#include <stdio.h>     /* インクルードディレクトリからのインクルード */

/* 自作のヘッダファイルのインクルード */
#include "myheader.h"  /* カレントディレクトリからのインクルード */

int main(void){
	int x;
	x = multiply(20, 30);

	printf("gblvarの値は%d\n", gblvar);         /* gblvar == 10 */
	printf("multiply関数の戻り値は%d\n", x);    /* x == 6000 */

	return 0;
}

ポインタ

ポインタ : 特定のメモリ領域を参照するための値をもつ変数。メモリアドレスを格納することができる変数。

#include <stdio.h>

int main(void){
	int x = 25;
	int *a;        // intへのポインタ a の宣言 (宣言時における* : 名前(識別子)をポインタとして宣言する)
	               // この宣言は、記号表現として、*aという表現がintであることを表す。

	// ポインタの初期化 (& :アドレス演算子)
	int *b = &x;   // 変数xのアドレスを、ポインタbに代入。※ポインタbは変数xを指す。

	a = &x;        // 変数xのアドレスを、ポインタaに代入。※ポインタaは変数xを指す。
	printf("変数xのアドレスは%pです。\n", &x);              // アドレスの変換仕様には%pを利用する。
	printf("ポインタaの値は%pです。\n", a);                 // &xとaは同じ値。

	// 間接演算子(*)を利用して、ポインタが指すアドレス(の値)にアクセスする。(ポインタ利用時における* :間接演算子)
	printf("変数xの値は%dです。\n", x);                     // 結果:  x==25
	printf("ポインタaが指すアドレスの値は%dです。\n", *a);  // 結果: *a==25
	printf("ポインタbが指すアドレスの値は%dです。\n", *b);  // 結果: *b==25

	*b = 75;       // ポインタbが指すアドレスの値に、75を代入する。※変数xに値の代入をしたことになる。
	printf("変数xの値は%dです。\n", x);                     // 結果:  x==75 に変わる。
	printf("ポインタaが指すアドレスの値は%dです。\n", *a);  // 結果: *a==75 に変わる。
	printf("ポインタbが指すアドレスの値は%dです。\n", *b);  // 結果: *b==75 に変わる。

	return 0;
}
#include <stdio.h>

void replace1(int a, int b);
void replace2(int *a, int *b);

int main(void){
	int x = 25;
	int y = 75;

	replace1(x, y);
	printf("xは%d, yは%dです。\n", x, y);  /* 結果: x==25, y==75 */

	replace2(&x, &y);                      /* 実引数をアドレスにする */
	printf("xは%d, yは%dです。\n", x, y);  /* 結果: x==75, y==25 */

	return 0;
}

/* 値渡し */
void replace1(int a, int b){
	int c;
	c = a;
	a = b;
	b = c;
	return;
}

/* 参照渡し */
void replace2(int *a, int *b){   /* 仮引数をポインタにする */
	int c;
	c = *a;
	*a = *b;
	*b = c;
	return;
}
#include <stdio.h>

int main(void){
	char c[] = "Honesty";
	char *p = "Honesty";  /* どこかのアドレスに文字列が格納され、そのアドレスを指す */

	printf("%s\n", c);
	printf("%s\n", p);

	p = "destiny";        /* 配列では初期化後に""での代入はできないが、ポインタでは可能 */
	printf("%s\n", p);

	printf(p);            /* ポインタで出力 */
	printf("\n");

	return 0;
}
#include <stdio.h>

int main(void){
	int i;

	/* ""による文字列の初期化では'\0'が最後に格納されるので、各文字列は9文字以内にする */
	char str1[3][10] = { "Sunday", "Monday", "Tuesday" }; /* 10個までの値を格納可能な配列を、3つ格納可能な多次元配列 */

	char *str2[3] = { "Sunday", "Monday", "Tuesday" };    /* 文字列を指すポインタが3つの配列 */

	for (i = 0; i < 3; i++){
		printf("%s\t%s\n", str1[i], str2[i]);
	}

	str2[0] = "Wednesday";   /* ポインタの配列の場合、指す文字列を変更可能 */
	str2[1] = "Thursday";
	str2[2] = "Friday";
	for (i = 0; i < 3; i++){
		printf("%s\n", str2[i]);
	}

	return 0;
}

ポインタ演算

ポインタ演算 (※p, p1, p2はポインタ)
ポインタ演算 説明
p + n  pが指すアドレスに n*sizeof(pointer_type) を加算した
 アドレスを指す。
p - n  pが指すアドレスから n*sizeof(pointer_type) を減算した
 アドレスを指す。
p1 - p2  ((long)p1 - (long)p2)/sizeof(pointer_type)
  → p1 と p2 の間の要素数を得る。
p++  pが指すアドレスに sizeof(pointer_type) を加算した
 アドレスを指す。p+1の結果と同じ。
p--  pが指すアドレスから sizeof(pointer_type) を減算した
 アドレスを指す。p-1の結果と同じ。
#include <stdio.h>

int main(void){
	int sample[3] = { 56, 652, 12 };

	/* 値 ------------------------------------------------------------------------------------------------------------ */
	/* sample[0] と *sample は同じ値 */
	printf("sample[0] の値は、%dです。\n", sample[0]);
	printf("*sample の値は、%dです。\n", *sample);        /* 配列名は、先頭要素へのポインタのように扱われる。
											                 (但し他のアドレスを代入はできない) */

	/* sample[1] と *(sample + 1) は同じ値 */
	printf("sample[1] の値は、%dです。\n", sample[1]);
	printf("*(sample + 1) の値は、%dです。\n", *(sample + 1));

	/* sample[2] と *(sample + 2) は同じ値 */
	printf("sample[2] の値は、%dです。\n", sample[2]);
	printf("*(sample + 2) の値は、%dです。\n", *(sample + 2));

	/* アドレス ------------------------------------------------------------------------------------------------------ */
	/* &sample[0] と sample は同じ値 */
	printf("sample[0] のアドレスは、%pです。\n", &sample[0]);
	printf("sample の値は、%pです。\n", sample);          /* 配列名は、先頭要素へのポインタのように扱われる。
											                 (但し他のアドレスを代入はできない) */

	/* &sample[1] と sample + 1 は同じ値 */
	printf("sample[1] のアドレスは、%pです。\n", &sample[1]);
	printf("sample + 1 の値は、%pです。\n", sample + 1);  /* sampleが指すアドレスに、sizeof(int)を加算したアドレスを指す。
												             → sampleが指す要素の、1つ次の要素のアドレス値を意味する。 */

	/* &sample[2] と sample + 2 は同じ値 */
	printf("sample[2] のアドレスは、%pです。\n", &sample[2]);
	printf("sample + 2 の値は、%pです。\n", sample + 2);  /* sampleが指すアドレスに、2 * sizeof(int)を加算したアドレスを指す。
												             → sampleが指す要素の、2つ次の要素のアドレス値を意味する。 */
	return 0;
}
#include <stdio.h>

int add(int t[]);        /* 仮引数が配列 */
int multiply(int *p);    /* 仮引数がポインタ */
double average(int *p);  /* 仮引数がポインタ */

int main(void){
	int sample[3] = { 33, 652, 13 };
	int resultAdd, resultMultiply;
	double resultAverage;

	/* (1) */
	resultAdd = add(sample);  /* 実引数が配列名 */
	printf("3つの値を加算した結果は、%dです。\n", resultAdd);

	/* (2) */
	resultMultiply = multiply(sample);
	printf("3つの値を乗算した結果は、%dです。\n", resultMultiply);

	/* (3) */
	resultAverage = average(sample);
	printf("3つの値を平均した結果は、%fです。\n", resultAverage);

	return 0;
}

int add(int t[]){             /* 仮引数が配列 */
	int i;
	int summation = 0;
	for (i = 0; i < 3; i++){
		summation += t[i];
	}
	return summation;
}

int multiply(int *p){         /* 仮引数がポインタ */
	int i;
	int product = 1;
	for (i = 0; i < 3; i++){
		product *= *(p + i);  /* ポインタ演算の利用 */
	}
	return product;
}

double average(int *p){       /* 仮引数がポインタ */
	int i;
	double average = 0;
	for (i = 0; i < 3; i++){
		average += p[i];      /* 添字演算子[]の利用 */
	}
	return average / 3;
}

文字列の操作

#include <stdio.h>
#include <string.h> /* strlen(), strcpy(), strcat(), strcmp()の利用 */

int main(void){
	/* 配列の大きさに注意 */
	char str[9] = "Blue Sky";
	char str1[30];
	char str2[30];

	/* 文字列の長さ(length)を調べる ------------------------------------------------------------------------------------- */
	printf("文字列の長さは%dです。\n", strlen(str));   /* strlen(str) :str の文字数を返す。空白文字は含む。\0は含まない */

	/* 文字列を配列にコピーする(copy) ----------------------------------------------------------------------------------- */
	strcpy(str1, "Blue Sky");            /* strcpy(strDestination, strSource) :終端の\0を含めてstrSourceをstrDestinationで
										 指定された位置にコピーし、strDestinationを返す */
	strcpy(str2, "Twilight Sky");
	printf("str1は、%sです。\n", str1);
	printf("str2は、%sです。\n", str2);

	strcpy(str1, str2);
	printf("str1は、%sに変更されました。\n", str1);

	strcpy(str1, "Starry Sky");
	printf("str1は、%sに変更されました。\n", str1);

	/* 文字列を連結する(concatenate) ------------------------------------------------------------------------------------ */
	strcat(str1, str2);           /* strcat(strDestination, strSource) :strSourceをstrDestinationに追加し、終端に\0を付け、
								  strDestinationを返す。strSourceの先頭の文字はstrDestinationの終端の\0を上書きする */
	printf("文字列を連結すると%sです。\n", str1);

	/* 文字列を辞書式の順序で比較する(compare) -------------------------------------------------------------------------- */
	if (strcmp(str1, str2) == 0){                    /* strcmp(string1, string2) :string1とstring2を辞書式の順序で比較し、
													 その関係を示す値を返す */
		printf("str1とstr2の文字列は同じです。\n");
	}
	else if (strcmp(str1, str2) > 0){
		printf("str1は、str2より大きい。\n");
	}
	else if (strcmp(str1, str2) < 0){
		printf("str1は、str2より小さい。\n");
	}

	return 0;
}

動的メモリ確保

動的メモリ確保 : メモリ管理のひとつ。プログラムの実行をしながら、並行して必要なメモリ領域の確保と解放を行う仕組み。

#include <stdio.h>
#include <stdlib.h> /* malloc(), free()の利用。exit(), EXIT_FAILURE の利用。 */

int main(void){
	char *str;
	int num, i;

	printf("何文字入力しますか?\n");
	scanf("%d", &num);

	/* 動的メモリ確保 ※numに加算している1バイトは、\0 の分 */
	str = (char *)malloc((num + 1) * sizeof(char));  /* malloc(size)  : sizeで指定したバイトのメモリブロックを割り当て、その領域への
													 ポインタ(void型へのポインタ)を返す。void以外の型へのポインタを得るには戻り値に
													 キャストを行うが、ANSI以降では代入時に暗黙的に型変換されるので、明示的にキャスト
													 する必要は無い。→(char *)は書かなくても良い */
	if (str == NULL){
		printf("メモリが確保できませんでした。\n");
		exit(EXIT_FAILURE);
	}

	for (i = 0; i < num; i++){
		printf("%d文字目を入力してください。\n", i + 1);
		scanf("%*c%c", str + i);  /* %*cで1文字目を読み飛ばす(\nが入るのを防ぐ) ※%*cの*は代入抑止のフラグ */
	}
	*(str + num) = '\0';

	printf("文字列は、%sです。\n", str);

	/* メモリの解放 */
	free(str); /* free(memblock)  : mallocなどによって割り当てられたメモリブロック(memblock)を解放する */

	return 0;
}

関数ポインタ

関数ポインタ : 個々の関数のラベルへの単純なポインタ。関数の処理の先頭アドレスを格納できる。

#include <stdio.h>

int add(int x, int y);

int main(void){
	int num1, num2;
	int resultAdd;

	/* 関数ポインタの宣言 */
	int(*p)(int x, int y);

	/* 関数ポインタに、関数のアドレスを代入 */
	p = add;

	printf("1番目の整数値を入力してください。\n");
	scanf("%d", &num1);
	printf("2番目の整数値を入力してください。\n");
	scanf("%d", &num2);

	/* 関数ポインタと間接演算子による、関数呼び出し */
	resultAdd = (*p)(num1, num2);
	//resultAdd = p(num1, num2); これでも良いことになっている。

	printf("合計値は、%dです。\n", resultAdd);

	return 0;
}

int add(int x, int y){
	return x + y;
}
#include <stdio.h>

int add(int x, int y);
int multiply(int x, int y);

int main(void){
	int num1, num2, num3;
	int result;

	/* 関数ポインタの配列の宣言 */
	int(*p[2])(int x, int y);

	/* 関数ポインタの配列の要素に、関数のアドレスを代入 */
	p[0] = add;
	p[1] = multiply;

	printf("1つ目の整数値を入力してください。\n");
	scanf("%d", &num1);
	printf("2つ目の整数値を入力してください。\n");
	scanf("%d", &num2);
	printf("加算しますか? 乗算しますか? (加算:0,乗算:1)\n");
	scanf("%d", &num3);

	/* 関数ポインタと間接演算子による、関数呼び出し */
	result = (*p[num3])(num1, num2);

	printf("結果は、%dです。\n", result);

	return 0;
}

int add(int x, int y){
	return x + y;
}

int multiply(int x, int y){
	return x*y;
}

double average(int x, int y){
	return (x + y) / 2;
}

列挙型

  • 列挙型 : 複合型に属する、データ型の一つ(C89で追加)。
  • 識別子をもった整数定数のリストである。
  • 列挙型を用いることで、整数定数の代わりに識別子を使うことができ、ソースコードの可読性を高める。
  • 識別子を値(列挙定数)として格納できる。
  • 列挙定数は、int型の定数(整数定数)であり、列挙定数による数値演算が可能である。
#include <stdio.h>

/* 列挙型 enum WeekA を、新しい型名 WeekA として定義する ※typedefで新たな型名を定義するかは任意 */
typedef enum WeekA{ SUNA, MONA, TUEA, WEDA, THUA, FRIA, SATA }WeekA;           /* 左から、0,1,2,3,4,5,6 */
typedef enum WeekB{ SUNB, MONB, TUEB, WEDB, THUB, FRIB, SATB }WeekB;           /* 左から、0,1,2,3,4,5,6 */
typedef enum WeekC{ SUNC, MONC, TUEC = 0, WEDC, THUC, FRIC = 25, SATC }WeekC;  /* 左から、0,1,0,1,2,25,26 */

int main(void){
	WeekA dayA;    /* 列挙型WeekAの変数dayAの宣言 */
	WeekB dayB;
	WeekC dayC;

	dayA = WEDA;   /* 代入 ※列挙定数は、変数名などのような識別子なので、文字列リテラルのように""で括らない */
	dayB = MONB;
	dayC = SATC;

	switch (dayA)  /* 列挙定数を、識別子のまま利用 */
	{
	case SUNA:
		printf("日曜日です。\n"); break;
	case MONA:
		printf("月曜日です。\n"); break;
	case TUEA:
		printf("火曜日です。\n"); break;
	case WEDA:
		printf("水曜日です。\n"); break;
	case THUA:
		printf("木曜日です。\n"); break;
	case FRIA:
		printf("金曜日です。\n"); break;
	case SATA:
		printf("土曜日です。\n"); break;  /* 最後のbreak;は省略可 */
	}
	printf("\n");

	switch (dayB)  /* 列挙定数を、int型の定数(整数定数)として利用 */
	{
	case 0:
		printf("日曜日です。\n"); break;
	case 1:
		printf("月曜日です。\n"); break;
	case 2:
		printf("火曜日です。\n"); break;
	case 3:
		printf("水曜日です。\n"); break;
	case 4:
		printf("木曜日です。\n"); break;
	case 5:
		printf("金曜日です。\n"); break;
	case 6:
		printf("土曜日です。\n"); break;
	}
	printf("\n");

	switch (dayC)  /* 列挙定数を、int型の定数(整数定数)として利用 */
	{
	case 0:
		printf("日曜日または火曜日です。\n"); break;
	case 1:
		printf("月曜日または水曜日です。\n"); break;
	case 2:
		printf("木曜日です。\n"); break;
	case 25:
		printf("金曜日です。\n"); break;
	case 26:
		printf("土曜日です。\n"); break;
	}

	return 0;
}

構造体

構造体 : 複合型に属する、データ型の一つ。配列は同じ型のデータのみ格納できるのに対して、構造体は、同じ型と異なる型のデータをまとめて格納できる。

#include <stdio.h>

/* 構造体の型(struct AnthropometricData)の定義 */
struct AnthropometricData{
	char *name;       // メンバ変数 ※char *型のポインタname
	int age;          // メンバ変数
	double height;    // メンバ変数
	double weight;    // メンバ変数
};                    // セミコロン(;)が必要。

int main(void){
	struct AnthropometricData x;  /* 構造体 struct AnthropometricData 型の変数 x の宣言 */

	/* ドット演算子(.)を利用して、構造体の各メンバにアクセスする */
	x.name = "日本太郎";
	x.age = 23;
	x.height = 167.5;

	printf("体重を入力してください\n");
	scanf("%lf", &x.weight);      /* アドレス演算子(&)は、構造体変数名の前に使用する */

	printf("名前:%s\n年齢:%d歳\n身長:%fcm\n体重:%fkg\n", x.name, x.age, x.height, x.weight);

	return 0;
}
#include <stdio.h>

/* 構造体の型の定義と、構造体変数の宣言を同時に行う */
struct AnthropometricData{
	char *name;
	int age;
	double height;
	double weight;
}a, b;                /* 構造体変数aとbの宣言 */

/* 構造体の型名の省略 */
struct{
	char *name;
	int age;
	double height;
	double weight;
}c;                   /* 構造体変数cの宣言 */

int main(void){
	a.name = "日本太郎";
	a.age = 23;
	a.height = 170.5;
	a.weight = 70.3;

	b.name = "日本花子";
	b.age = 22;
	b.height = 160.2;
	b.weight = 45.2;

	printf("名前:%s\n年齢:%d歳\n身長:%fcm\n体重:%fkg\n", a.name, a.age, a.height, a.weight);
	printf("\n");
	printf("名前:%s\n年齢:%d歳\n身長:%fcm\n体重:%fkg\n", b.name, b.age, b.height, b.weight);
	printf("\n");

	c.name = "日本一郎";
	c.age = 22;
	c.height = 165.5;
	c.weight = 65.5;

	printf("名前:%s\n年齢:%d歳\n身長:%fcm\n体重:%fkg\n", c.name, c.age, c.height, c.weight);

	return 0;
}
#include <stdio.h>

/* 構造体の型(struct AnthropometricData)を、新しい型名 Data1 として定義する */
typedef struct AnthropometricData{
	char *name;
	int age;
	double height;
	double weight;
}Data1;

/* 構造体の(元の)型名の省略 */
typedef struct{
	char *name;
	int age;
	double height;
	double weight;
}Data2;

int main(void){
	/* 構造体変数の宣言 */
	Data1 a = { "日本太郎", 23, 170.5, 70.3 };    /* 構造体変数の初期化 */
	Data1 b;
	Data2 c;

	b.name = "日本花子";
	b.age = 22;
	b.height = 160.2;
	b.weight = 45.2;

	printf("名前:%s\n年齢:%d歳\n身長:%fcm\n体重:%fkg\n", a.name, a.age, a.height, a.weight);
	printf("\n");
	printf("名前:%s\n年齢:%d歳\n身長:%fcm\n体重:%fkg\n", b.name, b.age, b.height, b.weight);
	printf("\n");

	c.name = "日本一郎";
	c.age = 22;
	c.height = 165.5;
	c.weight = 65.5;

	printf("名前:%s\n年齢:%d歳\n身長:%fcm\n体重:%fkg\n", c.name, c.age, c.height, c.weight);
	printf("\n");

	a = b;      /* 値の代入 */

	printf("名前:%s\n年齢:%d歳\n身長:%fcm\n体重:%fkg\n", a.name, a.age, a.height, a.weight);
	printf("\n");
	printf("名前:%s\n年齢:%d歳\n身長:%fcm\n体重:%fkg\n", b.name, b.age, b.height, b.weight);
	printf("\n");

	return 0;
}
#include <stdio.h>

typedef struct AnthropometricData{
	char *name;
	int age;
	double height;
	double weight;
}Data;

void display1(Data x);
void display2(Data *x);

int main(void){
	Data a = { "日本太郎", 23, 170.5, 70.3 };

	display1(a);    /* 値渡し ※メンバが多いほど処理に時間がかかる */
	printf("\n");
	display2(&a);   /* 参照渡しでは、関数にアドレス値だけ渡すので、値渡しに比べて処理が速くなる場合がある */

	return 0;
}

/* 値渡し */
void display1(Data x){
	printf("名前:%s\n年齢:%d歳\n身長:%fcm\n体重:%fkg\n", x.name, x.age, x.height, x.weight);
}

/* 参照渡し */
void display2(Data *x){
	/* ポインタからメンバにアクセスするにはアロー演算子(->)を利用する */
	printf("名前:%s\n年齢:%d歳\n身長:%fcm\n体重:%fkg\n", x->name, x->age, x->height, x->weight);
}

共用体

共用体 : 複合型に属する、データ型の一つ。同じデータ型、または異なるデータ型が、同一のメモリー領域を共用する。

#include <stdio.h>

/* 共用体の型(union Identification)を、新しい型名IDとして定義する ※typedefで新たな型名を定義するかは任意 */
typedef union Identification{
	char name[6];
	int id_number;
	double password;
}ID;

int main(void){
	ID iddata;    /* 共用体ID型の変数iddataの宣言 */

	iddata.name[0] = 'T';
	iddata.name[1] = 'A';
	iddata.name[2] = 'R';
	iddata.name[3] = 'O';
	iddata.name[4] = 'U';
	iddata.name[5] = '\0';
	printf("%s\n", iddata.name);

	iddata.id_number = 75;
	printf("%d\n", iddata.id_number);

	iddata.password = 35.67;
	printf("%f\n", iddata.password);

	/* sizeof演算子による評価結果の型はsize_t型。変換仕様は%zu (C99からの規格対応限定になる) */
	/* サイズはそれぞれ 6,4,8byte ※環境依存 */
	printf("%u,%u,%u\n", (unsigned int)(sizeof(char)* 6), (unsigned int)sizeof(int), (unsigned int)sizeof(double));
	/* サイズ:8byte ※この環境では、それぞれの変数を宣言した場合18byteになる。共用体の利用により、8byteで済む */
	printf("%u\n", (unsigned int)sizeof(ID));

	return 0;
}

ストリーム

ストリーム : データの入力または出力の機能を提供する抽象データ型。ファイルの入出力、メモリバッファの入出力、ネットワーク通信を扱うものなどさまざま。ファイルの入出力には、標準ストリームと呼ばれる特別なストリームが用意されていることもある。

標準ストリーム : UNIXおよびUnix系OSや一部のプログラミング言語インタフェースにおいて、プログラムとその環境(通常は端末)を実行前から接続している入出力チャネル(:チャンネルと同じ)。現在、標準入力 (standard input)、標準出力 (standard output)、標準エラー出力 (standard error) と呼ばれる3つの入出力がある。

#include <stdio.h>

int main(void){
	int x[] = { 23, 756, -75, 12386, -45 };
	double y[] = { 23.355256, 12756.3397, -75.5823, 12386, -45.695 };
	int i;

	for (i = 0; i < 5; i++){
		printf("%5d", x[i]);    /* 5文字幅で出力 ※デフォルトで右寄せ */
	}                           //   23  756  -7512386  -45
	printf("\n");

	for (i = 0; i < 5; i++){
		printf("%3d", x[i]);    /* 指定の文字幅が、値よりも少ない場合、その指定文字幅は無視される */
	}                           // 23756-7512386-45
	printf("\n");

	for (i = 0; i < 5; i++){
		printf("%+7d", x[i]);   /* 符号付き7文字幅で出力 ※負の値は-で出力される */
	}                           //    +23   +756    -75 +12386    -45
	printf("\n");

	for (i = 0; i < 5; i++){
		printf("%-7d", x[i]);   /* 左寄せ7文字幅で出力 */
	}                           //23     756    -75    12386  -45
	printf("\n");

	for (i = 0; i < 5; i++){
		printf("%.2f", y[i]);   /* 小数点以下2桁で出力 ※指定桁数よりも桁が多い場合は四捨五入される */
	}                           //23.3612756.34-75.5812386.00-45.70
	printf("\n");

	for (i = 0; i < 5; i++){
		printf("%-6.3f", y[i]); /* 左寄り、6桁、小数点以下3桁(4桁目で四捨五入)で出力 */
	}                           //23.35512756.340-75.58212386.000-45.695
	printf("\n");

	return 0;
}
#include <string.h>  /* strchr(), strlen() を使用するためインクルード */
#include <stdio.h>

int main(void){
	char str[20];
	printf("文字列を入力してください。\n");

	/* ※gets()は脆弱性がありC11より廃止。
	gets(str);  改行('\n')までの1行分の文字列を読み込み、引数に格納し、'\n'を'\0'に置き換える */


	// ----------------------------------------------------------------------------------------------------
	/* ストリームから、改行までの1行分、または指定した文字数n - 1の文字列を読み込む関数 */
	fgets(str, 20, stdin);
	/* 最後に自動的に'\0'が格納されるので、この場合、入力できるのは19文字まで。
	途中で改行が入ると読み込みを終了し、'\n'を格納し、'\0'を追加する。※getsと挙動が異なる */

	/* '\n'が含まれているかの確認 */
	if (strchr(str, '\n') != NULL) { /* 文字列strから文字'\n'を探し、あれば'\n'へのポインタを返す。なければヌルポインタ(NULL)を返す */
		/* '\n'を'\0'に置換 */
		str[strlen(str) - 1] = '\0'; /* strlen():文字列の長さを返す。但し長さに'\0'は含まない。 */
	}
	else {
		/* 入力ストリームをクリアする */
		while (getchar() != '\n');
	}
	// ----------------------------------------------------------------------------------------------------

	// 出力1
	puts(str);              /* 文字列を書き出し、'\0'を'\n'に置き換える。 */

	// 出力2
	fputs(str, stdout);     /* 文字列を書き出す。'\0'は表示しない(コピーしない)。'\n'の追加もしない。 */
	printf("\n");

	// 出力3
	printf("%s\n", str);

	return 0;
}
#include <stdio.h>

int main(void){
	int ch;
	printf("文字を連続で入力してください。\n");

	/* EOF(End Of File):ファイルの終端のこと。stdio.hでマクロ定義され、値は -1 */
	while ((ch = getchar()) != EOF){  /* Windows(コマンドプロンプト)では、Ctrl+ZでEOFが返され、whileループが終了 */
		putchar(ch);
	}

	return 0;
}

ファイルの読み書き

モード  (※アクセス許可の種類)
モード 説明
w   書き込み用に、空のファイルを開く。
  指定したファイルが既に存在すると、そのファイルの内容は破棄
  される。
r   読み込み用に、ファイルを開く。
  ファイルが存在しない場合や見つからない場合、fopen 呼び出し
  は失敗する。
a   末尾に書き込みができるように、ファイルを開く (追加モード)。
  EOF マーカーを削除せずにファイルに新しいデータを書き込む。
  ファイルが存在しない場合は、作成する。
w+   読み込みと書き込みの両方のモードで、空のファイルを開く。
  指定したファイルが既に存在すると、そのファイルの内容は破棄
  される。
r+   読み込みと書き込みの両方のモードで開く。
  ファイルが存在している必要がある。
a+   読み込みと追加の両方のモードでファイルを開く。
  追加時には、ファイルに新しいデータを書き込む前に、
  EOF マーカーが削除され、書き込みが終了すると、
  EOF マーカーが復元する。
  ファイルが存在しない場合は、作成する。
wb   書き込み用に、バイナリファイルを開く。
rb   読み込み用に、バイナリファイルを開く。
ab   末尾に書き込みができるように、バイナリファイルを開く
  (追加モード)。

ファイルへの書き込み

#include <stdio.h>
#include <stdlib.h>  /* exit()を利用するためインクルードする。また、EXIT_FAILUREが1として定義されている */

int main(void){
	char x[7] = "Hello,";  /* 要素数は省略可 */
	char y[7] = "World!";
	char a[4] = "How";
	char b[4] = "are";
	char c[5] = "you?";

	/* ファイルポインタの宣言 ※FILE構造体へのポインタ */
	FILE *fp;

	/* ファイルのオープン ※fopen("ファイル名", "モード(:アクセス許可の種類)") */
	fp = fopen("sample.txt", "w");  /* fopen() : 開いているファイルへのポインタを返す。エラー時にはNULL(ヌルポインタ)を返す */

	if (fp == NULL){
		/* エラー処理 */
		printf("ファイルをオープンできませんでした。\n");
		exit(EXIT_FAILURE);   /* プログラムを終了 */
	}
	else{
		printf("ファイルをオープンしました。\n");
	}

	/* ファイルへの書き込み */
	fputs("Hello, World!\n", fp);   /* fputs():文字列を書き出す。'\0'は表示しない(コピーしない)。'\n'の追加もしない。 */
	fputs("How are you?\n", fp);
	printf("ファイルへの書き込みをしました。\n");

	/* ファイルへの書式付きの書き込み */
	fprintf(fp, "%-7s%s\n", x, y);  /* fprintf():文字列を書き出す。'\0'は表示しない(コピーしない)。'\n'の追加もしない。 */
	fprintf(fp, "%-4s%-4s%s\n", a, b, c);
	printf("ファイルへの書式付きの書き込みをしました。\n");

	/* ファイルのクローズ */
	fclose(fp);
	printf("ファイルをクローズしました。\n");

	return 0;
}

ファイルからの読み込み

#include <string.h>  /* strchr(), strlen() を使用するためインクルード */
#include <stdio.h>
#include <stdlib.h>  /* exit()を利用するためインクルードする。また、EXIT_FAILUREが1として定義されている */
#define NUM 20

int main(void){
	char str1[NUM];
	char str2[NUM];
	char x[NUM];
	char y[NUM];

	/* ファイルポインタの宣言 ※FILE構造体へのポインタ */
	FILE *fp;

	/* ファイルのオープン ※fopen("ファイル名", "モード(:アクセス許可の種類)") */
	fp = fopen("sample.txt", "r");  /* fopen() : 開いているファイルへのポインタを返す。エラー時にはNULL(ヌルポインタ)を返す */

	if (fp == NULL){
		/* エラー処理 */
		printf("ファイルをオープンできませんでした。\n");
		exit(EXIT_FAILURE);   /* プログラムを終了 */
	}
	else{
		printf("ファイルをオープンしました。\n");
	}

	/* ファイルからの読み込み ------------------------------------------------------------------------------ */
	/* 読み込み1 */
	fgets(str1, NUM, fp);   /* 最後に自動的に'\0'が格納されるので、読み込むのは(NUM-1)文字まで。
							   途中で改行が入ると読み込みを終了し、'\n'を格納し、'\0'を追加する。*/
	/* '\n'が含まれているかの確認 */
	if (strchr(str1, '\n') != NULL) { /* 文字列strから文字'\n'を探し、あれば'\n'へのポインタを返す。なければヌルポインタ(NULL)を返す */
		/* '\n'を'\0'に置換 */
		str1[strlen(str1) - 1] = '\0'; /* strlen():文字列の長さを返す。但し長さに'\0'は含まない。 */
	}
	else {
		/* 入力ストリームをクリアする */
		// while (getchar() != '\n');
	}

	/* 読み込み2 */
	fgets(str2, NUM, fp);
	if (strchr(str2, '\n') != NULL) {
		str2[strlen(str2) - 1] = '\0';
	}
	else {
		// while (getchar() != '\n');
	}
	/* ----------------------------------------------------------------------------------------------------- */

	/* 書き出し */
	printf("%s\n", str1);
	printf("%s\n", str2);
	printf("ファイルからの読み込みをしました。\n");

	/* ファイルからの書式付きの読み込み */
	fscanf(fp, "%19s %19s", x, y);  /* fscanf():空白文字,改行,タブなどの前まで文字列を読み込む。※文字列では\0を自動的に追加 */
	printf("%s\n%s\n", x, y);
	printf("ファイルからの書式付きの読み込みをしました。\n");

	/* ファイルのクローズ */
	fclose(fp);
	printf("ファイルをクローズしました。\n");

	return 0;
}

バイナリファイルへの書き込み

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

int main(void){
	int data[5] = { 25, 67, 19, 83, 66 };
	int i;
	FILE *fp;

	fp = fopen("sample.bin", "wb");
	if (fp == NULL){
		printf("ファイルをオープンできませんでした。\n");
		exit(EXIT_FAILURE);
	}
	else{
		printf("ファイルをオープンしました。\n");
	}

	for (i = 0; i < 5; i++){
		fwrite(&data[i], sizeof(int), 1, fp); /* fwrite(データへのポインタ, データサイズ, 個数, ファイルポインタ) */
	}
	/* for文の代わりに、fwrite(data, sizeof(data), 1, fp); でも可 */

	printf("ファイルへの書き込みをしました。\n");

	fclose(fp);
	printf("ファイルをクローズしました。\n");

	return 0;
}

バイナリファイルからの読み込み

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

int main(void){
	int data[5];
	int i;
	FILE *fp;

	fp = fopen("sample.bin", "rb");
	if (fp == NULL){
		printf("ファイルをオープンできませんでした。\n");
		exit(EXIT_FAILURE);
	}
	else{
		printf("ファイルをオープンしました。\n");
	}

	for (i = 0; i < 5; i++){
		fread(&data[i], sizeof(int), 1, fp); /* fread(データへのポインタ, データサイズ, 個数, ファイルポインタ) */
		printf("%d\n", data[i]);
	}
	/* for文の代わりに、fread(data, sizeof(data), 1, fp); でも可 */

	printf("ファイルからの読み込みをしました。\n");

	fclose(fp);
	printf("ファイルをクローズしました。\n");

	return 0;
}

アクセス方式

  • シーケンシャルアクセス :データへのアクセス方式のひとつ。記憶媒体の先頭から順に検索しアクセスする。
  • ランダムアクセス :データへのアクセス方式のひとつ。直接アクセスともいう。読み書きしたいデータの場所をインデックスなどの位置情報をもとに割り出し、直接その場所にアクセスする。

ランダムアクセス

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

int main(void){
	int data;
	int i;
	FILE *fp;

	fp = fopen("sample.bin", "rb");
	if (fp == NULL){
		printf("ファイルをオープンできませんでした。\n");
		exit(EXIT_FAILURE);
	}
	else{
		printf("ファイルをオープンしました。\n");
	}
	printf("何番目の値を読み込みますか?\n");
	scanf("%d", &i);

	/* アクセス位置の移動 (指定した場所にファイルポインタを移動する) */
	fseek(fp, (i - 1)*sizeof(int), SEEK_SET); /* fseek(ファイルポインタ, アクセス位置(初期位置からのバイト数:オフセット), 初期位置) */

	fread(&data, sizeof(int), 1, fp);         /* fread(データへのポインタ, データサイズ, 個数, ファイルポインタ) */
	printf("%d\n", data);
	printf("ファイルからの読み込みをしました。\n");

	fclose(fp);
	printf("ファイルをクローズしました。\n");

	return 0;
}
ファイルポインタの初期位置
初期位置 説明
SEEK_SET   ファイルの先頭。
SEEK_CUR   ファイルポインタの現在位置。
SEEK_END   ファイルの末尾(end of file)。

コマンドラインからの読み込み

①実行プログラム(.exeファイル)を用意する。

/* sample.c */
#include <stdio.h>
#include <stdlib.h>

/* コマンドライン引数(argument count, argument vector)の利用 */
int main(int argc, char *argv[]){  /* int argc(入力した値の個数), char *argv[](入力した値へのポインタ)*/
	int c;
	FILE *fp;

	/* 入力した値の個数をチェック */
	if (argc != 2){
		printf("パラメータの数が違います。\n");
		exit(EXIT_FAILURE);
	}

	/* 入力した2番目の値(ファイル名)を指定して、読み込み用にファイルを開く。 */
	fp = fopen(argv[1], "r"); /* argv[0]に1番目の値(実行プログラム名sample), argv[1]に2番目の値(ファイル名)が入っている */
	if (fp == NULL){
		printf("ファイルをオープンできませんでした。\n");
		exit(EXIT_FAILURE);
	}
	else{
		printf("ファイルをオープンしました。\n");
	}

	/* 文字の読み込み */
	while ((c = fgetc(fp)) != EOF){     /* int fgetc(FILE *stream); */
		putchar(c);                     /* int putchar(int c); */
	}
	printf("\n");
	printf("ファイルからの読み込みをしました。\n");

	fclose(fp);
	printf("ファイルをクローズしました。\n");

	return 0;
}

②読み書きするファイルを用意する。(ここでは、実行プログラムと同じディレクトリに用意してみる。)
③コマンドプロンプトを起動する。
④コマンドプロンプトで以下を実行する。
cd C:\Users\○○○○○\Desktop\Sample\Debug(ここでリターン)
cd C:\Users\○○○○○\Desktop\Sample\Debug>sample sample.txt(ここでリターン)
※ここでは実行ファイルがsample.exe, 読み込むファイル名がsample.txtと想定している。
また、sample.exeが、デスクトップ画面のSampleというファルダの中の、Debugファルダにあることを想定している。
⑤読み込みが実行されるのを確認する。
⑥終了する。

デバッグ

バグ(bug):コンピュータプログラムの製造(コーディング)上の誤りや欠陥。
デバッグ(debug):バグを探して取り除く作業。

条件付きコンパイル

#ifdef ディレクティブ :  プリプロセッサディレクティブの一つ。
マクロが定義されていた場合に、#ifdef ~ #endif までの間の文がコンパイルされる。

#include <stdio.h>
#define DEBUG   /* デバッグが終了したら削除、またはコメントアウト */

int main(void){
	int x[] = { 25, 36, 685, 94, 180 };
	int i;
	for (i = 0; i < 5; i++){
		x[i] += 75;
#ifdef DEBUG
		fprintf(stderr, "%d\n", x[i]);  /* 現在までの配列xの値を調べる */
#endif
	}

	for (i = 0; i < 5; i++){
		x[i] *= 23;
		printf("%d番目の要素は、%d\n", i + 1, x[i]);
	}

	return 0;
}

#ifndef ディレクティブ :  プリプロセッサディレクティブの一つ。
マクロが定義されていない場合に、#ifndef ~ #endif までの間のコードがコンパイルされる。
※インクルードガードなどによく利用される。

#include <stdio.h>
#define DEBUG   /* デバッグが終了したら削除、またはコメントアウト */

int main(void){

#ifdef DEBUG
	fprintf(stderr, "DEBUGが定義されている。\n");  /* この文がコンパイルされる */
#else
	fprintf(stderr, "DEBUGが未定義。\n"); 
#endif


#ifndef DEBUG
	fprintf(stderr, "DEBUGが未定義。\n");
#else
	fprintf(stderr, "DEBUGが定義されている。\n");  /* この文がコンパイルされる */
#endif

	return 0;
}

#if、#elif、#else、および #endif ディレクティブ :  それぞれプリプロセッサディレクティブの一つ。
#if または #elif に続く定数式の真偽により、コンパイルを制御する。

#include <stdio.h>
#define DEBIT

void credit(void);
void debit(void);
void printerror(void);

int main(void){

#if defined(CREDIT)  /* defined(identifier):識別子が定義されていればtrue(0以外)、未定義ではfalse(0) */
	credit();
#elif defined(DEBIT) /* この文がコンパイルされる */
	debit();
#else
	printerror();
#endif

	return 0;
}

void credit(void){
	printf("creditの処理です。\n");
}

void debit(void){
	printf("debitの処理です。\n");
}

void printerror(void){
	printf("エラーです。\n");
}

定義済みマクロ

ANSI 準拠の 定義済みマクロ  (※マクロの両端はアンダースコア2つ)
定義済みマクロ 説明
__DATE__   現在のソースファイルのコンパイル日。
  表示は、Mmm dd yyyy の形式の文字列リテラル。
__FILE__   現在のソースファイルの名前。
  表示は、文字列リテラル。
__LINE__   現在のソースファイル内の行番号。
  表示は、10進整定数。
__STDC__   ANSI C 標準に完全に準拠していることを示す。
  準拠していれば整定数1,そうでなければマクロ自体
  が未定義。
  ※Visual Studioの場合、プロジェクトのプロパティ →
  「C/C++」 → 「言語」 の 「言語拡張を無効にする」
  を「はい(/Za)」にすれば定義される。
__TIME__   現在のソースファイルの最新のコンパイル時刻。
  表示は、hh:mm:ss の形式の文字列リテラル。
__TIMESTAMP__   現在のソースファイルの最新の変更日時。
  表示は、Ddd Mmm Date hh:mm:ss yyyy (Ddd は
  曜日の省略形、Date は 1 ~ 31 の整数) の形式の
  文字列リテラル。
#include <stdio.h>

int main(void){
	printf("現在のソースファイルのコンパイル日: %s\n", __DATE__);
	printf("現在のソースファイルの名前: %s\n", __FILE__);
	printf("現在のソースファイル内の行番号: %d\n", __LINE__);
	printf("現在のソースファイルの最新のコンパイル時刻: %s\n", __TIME__);
	printf("現在のソースファイルの最新の変更日時: %s\n", __TIMESTAMP__);

#ifdef __STDC__
	puts("このコンパイラは、ANSI C 標準に準拠している。");   /* puts()は、文字列を書き出し、'\0'を'\n'に置き換える。 */
#else
	puts("このコンパイラは、ANSI C 標準に準拠していない。");
#endif

	return 0;
}





コメントの投稿

非公開コメント

マイブログへようこそ♪
PLEASANT_DRAGON

2DCG&3DCG,プログラミング,
日記などを掲載中☆

(*´▽`*)コメント大歓迎です☆

最新記事
記事一覧

全ての記事を表示する

カテゴリ
SAI (0)
mi (2)
C (1)
C++ (1)
C# (0)
VBA (0)
月別アーカイブ
最新コメント
RSSリンクの表示
リンク
FC2ブログランキング

FC2Blog Ranking

ピックアップ商品1♪










カレンダー
10 | 2017/11 | 12
- - - 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 - -
ブログ内検索フォーム
プロフィール

エクレア

Author:エクレア


  • 2DCG&3DCGの創作活動をしています。

  • SF系のメカが大好物。

アクセスカウンター
Twitter
ピックアップ商品2♪