C言語について

私が、専門学校時代に学んだC言語のノートを公開致します。
なお、教えて頂いたのは、美幌コンピュータ専門学校の山口和範先生です。
山口先生へメールを送る

C言語・・・システム記述用言語

UNIX・・・AT&T ベル研究所ケン・トンプソンによって開発されたオペレーティングシ
ステムPDP-7(DEC社)上にアセンブラで構築 後にUNIXを記述する為に開発されたのがC言
語である。

特徴

・比較的低水準のハードウェア操作に関する命令から高機能な命令(ライブラリ関数)
 までを持ち、幅広いプログラミングに対応している。

・プログラムは関数の集合から成り、主プログラム(メインルーチン)もプログラムの実
 行環境から呼び出される関数(main関数)である。



データ型が数多くあり、メモリと密接な関係がある。また変数の有効な範囲をもたらすメ
モリクラスを定義することができる。



問「Hello,world」を画面に表示せよ

#inlude 			※C言語における主ルーチンは、
					main関数と呼ばれる。
void main(void)・・・主ルーチンの開始	ひとつのプログラム内に
{					main関数は必ずひとつ
	printf("Hello,world\n");	なければならない。
}	↑文字列を[書式付きで]画面に表示

※文字列は(")(ダブルクォテーション)でかこまれた文字の集合

 "ABC"←という文字列は下のようにメモリに格納される
  ↓
'A' 'B' 'C' '\0'

※文字列はメモリ上において常に文字数より1バイト多い領域が確保され、文字列の終わ
 りを示す'\0'が付加されている。




#include 
↑
コンパイラ制御命令
この位置にstdio.hファイルを取り込む

※stdio.h・・・標準入出力ヘッダファイル入出力に関するライブラリ関数に関する情
 報が定義されている。この場合は、printf()を利用するため
#include 

void main(void)
{
	int a,b,c;
	a=5;
	b=3;
	c=a+b;
	printf("%d\n",c);
}
※%d・・・変数の内容を10進数表示

例
printf("%dと%dを足した値は%dです\n",a,b,c);

実行結果

3と5を足した値は8です

%d・・・整数を10進数表示
%x・・・16進数表示
%c・・・変数を文字として表示
%s・・・文字列の表示
%f・・・単精度実数表示

#include 

void main(void)
{
	printf("%x\n",146);
}
実行結果
92



四則演算に関して

i=3;
i+=5;
printf("%d\n",i);

実行結果
8

実数型の計算
~~~~~~
↑浮動小数点

整数型→int型(2byte)	-32758〜-32767

小数型→float型(4byte)

倍長整数型→long型


#include 

void main(void)
{
	float a = 10 , kotae;
	int b = 3;

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

変数タイプ

char	%d	1byte整数	文字の格納に適している
	%c
int	%d	2byte整数	比較的小さい整数を格納する

long	%ld	4byte整数	比較的大きい整数を格納する

float	%f	4byte実数	小数の使用可

double	%lf	8byte実数	floatよりも大きく精度の高い実数の表現が可能
		(倍精度)




入力を求める関数

scanf() ・・・ 書式付入力を行う

#include 

void main(void)
{
	int a,b;

	scanf("%d%d",&a,&b); /* キーボードからの入力を整数型と見てそれぞれを変数				a,bのアドレスに格納する */
	printf("%d%d",a+b);
}

& アドレス演算子 ・・・ 変数のアドレスを返す

#include 

main()
{
	char c1,c2;

	scanf("%c%c",&c1,&c2);
	/*     ~~~~文字として入力を求める */
	printf("%cと%cが入力された\n",c1,c2);
}	/*	~~~~~~変数を文字として表示 */

#include 

void main(void)
{
	char c1;

	scanf("%d",&c1); /* 65を入力*/
	printf("%x->%c\n",c1,c1);
}

結果
41->A



制御の流れ
if 〜else 文・・・2岐分岐

書式1			書式2
if(条件式)		if(条件式)
{			{
	処理;			真の処理;
}			}
			else
			{
				偽の処理;
			}

条件式に使われるもの・・・関係演算子

a > b aはbより大きい	a >= b aがbより大きいか等しい

a < b aはbより小さい	a <= b aがbより小さいか等しい

a != b aとbは等しくない

#include 

main()
{
	int a;

	scanf("%d",&a);
	if(5>a)
	{
		printf("美幌\n");
	}
	else
	{
		printf("北見\n");
	}
}

たくさんの時
if(式1)
{
	文;
}
else	if(式2)
{
	文;
}
else	if(式3)
{
	文;
}
else	{文;}

多岐分岐
swich(式)
{
	case

	case

	・
	・
	・
	default
}



制御の流れ


if文

if(式)
{
	評価式の結果が真(0以外)である場合
}
else
{
	評価力の結果が偽(0)である場合
}

while文・・・条件ループ

書式
while(式)
{
	式が真である間ループを続ける
}

例

#include 

main()
{
	int i = 0;

	while (i<10)
	{
		printf("Hello,world\n");
		i++;
	}
}

#include 

main()
{
	char c = 0;

	while ('a' != c)
	{
		scanf("%c",&c);
		printf("%c\n",c);
	}
}

''・・・シングルクォーテーションでくくられたものは文字1個を示す

#include 

main()
{
	int i = 1,s = 0;

	while (s < 15)
	{
		i++;
		s += i;
	}
	printf("%d\n",i);
}

実行結果
6
for文

#include 

main()
{
	int i = 0;
	while(i<10)
	{
		printf("%d\n",i);
		i++;
	}
}
これをfor文で直すと

#include 

main()
{
	int i;
	for (i=0;i<10;i++)	/*もし2ずつの場合第3式でi+=2とすればよい*/
	{
		printf("%d\n",i);
	}
}

複合文を使える
例1

	int i,j;

	for (i=0,j=0;i<10;i++,j+=2)
	{
		文
		・
		・
		・
	}

例2
	int i,s;

	for (i=0;s<20;i++)
	{
		s+=i;
	}
getchar() ・・・ 標準入力(キーボード)から一文字を読み込む

int getchar(void)
↑~	    ~↑~
戻り値	引数を取らない
int型

プロトタイプ	stdio.h

戻り値	キーボード入力より、一文字をint型で返す
	エラーの場合、EOFを返す

例1
#include 

void main(void)
{
		int a;
	a = getchar();
	printf("%c\n",a);
}

例2
#include 

main()
{
	int a;
	while ((a = getchar()) != 'q')
	{
		printf("%c",a);
	}
}

(a = getchar())ではキーボードより入力された一文字を変数aに代入

((a = getchar()) != 'q')で変数aと文字'q'を比較し、等しくない限り、真の値を返す

したがって'q'が入力されるまで、入力された文字を表示する

例3
#include 

main()
{
	int a = 5 , b = 3;
	int c;
	while ((c = getchar(()) != 'q')
	{
		if (c == '+')
			printf("%d\n",a+b);
		else if (c == '-')
			printf("%d\n",a-b);
		else
			printf("未対応\n");
	}
}
例4
#include 

main()
{
	int a = 5 , b = 3;
	int c;
	while ((c = getchar(()) != 'q')
	{
		if (c == '+')
			printf("%d %c %d = %d\n",a,c,b,a+b);
		else if (c == '-')
			printf("%d\n",a-b);
		else
			printf("未対応\n");
	}
}



文字列・・・複数個の文字列が連なって自然言語の単位等を形作っているデータ型

○原始プロクラム中で、文字列は""(ダブルクォーテーション)によって囲まれ、示され
 る。

○文字列はメモリ上で線形に記録されており、1バイト系文字より成る文字列は1アドレ
 スに1文字が格納される。

○文字列は、メモリ上での先頭アドレスを返す「式」である。

例
文字列 "ABCDE"
↓
A B C D E F \0

文字列の宣言

#include 

main()
{
	char str[128] = "ABCDE";
	printf("%s\n",str);
}

char str[128] = "ABCDE"; と宣言した場合

[0] [1] [2] [3] [4] [5]   [127]
 A   B   C   D   E   \0 〜 

128個の配列が用意され、先頭6バイトにABCDE\0が格納

char str[] = "ABCDE";と宣言した場合

"ABCDE"(\0を含む)を格納するのに十分な領域(6バイト)が配列として確保され、
格納される

char *str = "ABCDE";と宣言された場合

メモリ上に文字列が格納され、先頭番地がポインタ型変数strに格納される

関数

サブルーチンとしての関数
  ・・・繰り返し行われる処理をブラックボックス化する
	 閉じたサブルーチン
C言語における関数(サブルーチン)の設計
[戻り値型] 関数名([引数型],[引数型],・・・)
{
	処理;
	・
	・
	・
}

#include 

void main(void)
{
	int a;

	a = kakeru(5,3);
	printf("%d\n",a);
}

int kakeru(int a,int b) ・・・ 関数kakeru()は引数としてint型整数2個を取る
{				※a,bは関数kakeru()内のみでのローカル変数
	int rtn;
	rtn  a * b;
	return rtn; ・・・ 変数rtnを戻り値として呼び元に返している。
}

問int型引数を2個とり(それぞれarg1,arg2とする)、arg1をarg2乗し、結果をint型で返
す関数を作成せよ

int beki (int arg1,int arg2)
{
	int rtn = 1,i;

	for(i=0;i

void main(void)
{
	int a;

	a = beki(2,3);
	printf("%d\n",a);
}
int beki(int arg1,int arg2)
{
	int rtn = 1,i;

	for(i=0;i

void main(void)
{
	float menseki;

	menseki = tri_men(3,7);
	printf("%f\n",menseki);
}
float tri_men(float tei,float taka)
{
	float menseki;

	menseki = tei * taka * 0.5;
	return menseki;
}
問 1辺の長さがxである正六角形の面積を計算する関数を作成せよ。main関数から辺の長
さxは手入力で与えるものとし、関数の引数、戻値は共にfloat型とする。

関数名 → hex_men()

正三角形の面積 = x×√3×0.5×0.5
			   = √3×0.5×xの2乗×0.5

正三角形の面積 = 正三角形 × 6

float hex_men(float hen)
{
	float menseki;

	menseki = tri_men(hen,1732*hex*0.5)*6;
	return menseki;
}

float tri_men(float tei,float taka);
{
	float menseki;

	menseki = tei*taka*0.5;
	return menseki;
}

#include 

float tri_men(float,float);
float hex_men(float);

void main(void)
{
	float menseki,x;

	scanf("%f",&x);
	menseki = hex_men(x);
	printf("%f\n",menseki);
}
char c[]="ABCDE";
	||
char c[6]={'A','B','C','D','E','\0'};

※cを1ヶの変数として見た場合、cには文字列の先頭アドレスが格納されている。
			↓
		cは文字列ポインタである

ex

#include 

void main(void)
{
	char c[]="ABCDE";
	int i;

	for(i=0;i<6;i++)
		printf("%c\n",c[i]);
	printf("%S\n",c);
}



文字型配列
(char型)
	char a[] = "BCA";
				 ↓
				'B','C','A','\0'
aを変数とした場合、文字列"BCA"の先頭番地を示すポインタとなる
文字列a[]の表示法
	printf(a);・・・文字列"BCA"を表示
	printf("%s\n",a);・・・文字列"BCA"を書式付で表示
※%8s等の書式指定が可能

puts(a);・・・文字列"BCA"を表示後改行

printf("%s\n",a);とputs(a);をどの様に使い分けるか?

printf()→機能が多い・重い	puts()→機能が少ない・軽い

○同じメイン関数の中で文字列表示にputs()とprintf()を使用した場合、部分的に実行
は早くなるが多くのプログラムにおいてprintf()も使用される為、実行用のモジュール
にはputs()、printf()の双方がリンクされる事になり、プログラムサイズは大きくなる。
一方、printf()のみを使用した場合、実行速度の向上は望めないが、プログラムサイズは
小さくなる。配列とポインタ
			str[0]	str[1]	str[2]	str[3]
char str[] = "BCA"; →	  B	  C	  A	  \0

※文字列を格納するのに充分なメモリブロックが確保され1番地に1文字ずつ格納

			str[0]	str[1]	str[2]	str[3]〜str[127]
char str[128] = "BCA";→  B	  C	  A	  \0  〜   ?

※128バイトのメモリブロックが確保され、先頭に文字列が格納される
  '\0'以降の内容は保証されない

※配列で文字列を宣言する場合、変数と文字列は一意的である
  ↑静的(static)

char *str = "BCA";
     ~↑~
	*は間接演算子 strは変数名

※変数名に間接演算子を付加して宣言する事で、その変数型の実体が格納されたメモリブ
  ロックの先頭アドレスを示すポインタとなる。ポインタの初期化の際、文字列は、その文
  字配列をメモリ上に格納し、その先頭アドレスを返す式として機能する。

ポインタ版
#include 

void main(void)
{
	char	*a = "ABC",
		*b = "XYZ";

	printf("%S\n%S\n",a,b);
	a = b; /* ←先頭アドレスの代入 */
	printf("%s\n%s\n",a,b);
}動的

配列版
#include 

void main(void)
{
	char	a[] = "ABC",
		b[] = "XYZ";

	printf("%s\n%s\n",a,b);
	a = b; /* エラー・・・配列同士の代入は不可 */
	printf("%s\n%s\n",a,b);
}
#include 

void main(void)
{
	char a[] = "ABC";
	char b[] = "XYZ";

	printf("%s\n%s\n",a,b);
}

#include 

void main(void)
{		配列中、最長の文字列長 + 1
		 ↓
	char a[][4] = {"ABC","XYZ"};
			~~~↑~~~~
			文字列の配列
	printf("%s\n%s\n",a[0],a[1]);
}

char a[3][5] = {"AB","CDE","FGHI"};
※文字列3つと文字数の最大数5文字
AB\0の後2バイトには何が入っているか保証されない

#include 

void main(void)
{
	char a[][5] = {"AB","CDE","FGHI"};
	int i,j;

	for(i=0;i<3;i++)
	{
		printf("%s\n",a[i]);
		for(j=0;a[i][j] != '\0';j++)
			printf("%c\n",a[i][j]);
	}
}
実行結果
AB
A
B
CDE
C
D
E
FGHI
F
G
H
I

配列とポインタ

a[3][4] = {"a","bc","def"};

    a \0 ? ? b c \0 ? d e f \0 

・メモリ上に4バイト×3の領域が取得される
・全ての文字列はメモリ上で線形に配置される

char *a[]={"a,"bc","def"};
ポインタ(アドレスを持った変数)の配列

a[0] a[1] a[2]
(イ) (ロ) (ハ)

(イ)番地
a \0
(ロ)番地
b c \0
(ハ)番地
d e f \0

○ポインタの初期化に用いられる文字列がメモリ上に配置されそれぞれの先頭番地が
  ポインタ配列に格納される

char *str="abcd";

str	(イ)番地   ポインタ
(イ)	abcd\0    ↓
printf("%s\n",str);
        ~↑~
		ポインタの示す文字列を表示する
一文字ずつ表示
printf("%c\n",*str);
|             ↑
|		*は間接演算子
|       ポインタの示すアドレスに格納されたものを示す(オブジェクト)
↓
文字'a'を表示
printf("%c\n",*(str+1));
|               ~↑~~
↓			strの示すアドレスの1番地先
文字'b'を表示
printf("%c%c%c%c%c\n",*str,*(str+1),*(str+2),*(str+3),*(str+4));
↑
と
↓
printf("%s\n",str);
は同じ結果を表示!!
例 文字列中の指定文字を数える関数を作ろう → 関数名 chrcnt()

プロトタイプ宣言
int chrcnt(char *s,char c);
文字列s中に文字cが何個あるかを数える

int chrcnt(char *str,char chr)
{
	int cnt = 0; /* 指定文字を数える */

	while('\0' != *str)
    {            ↑間接演算子
                 ポインタの示すアドレスに格納された実体を返す
                 (この場合文字列ポインタなので文字のこと)
		if(*str == chr)
			cnt++;
/* 文字列中の文字と指定した文字が等しい時、cntをインクリメント */
			str++;
	}
return cnt; 結果を呼び元に返す
}
例
#include 

void main(void)
{
	int a;

	a = chrcnt("ABCACA",'A');
	printf("%d\n",a);
}
結果
3

例
#include 

void main(void)
{
	char str[256];
	int a;

	gets(str); /* 配列strにキーボード入力された文字を取得 */
	a = chrcnt(str,'A');
	printf("%sの中にはAが%d個ある\n",str,a);
}
入力
ABCDEFGABCDEFG
実行結果
ABCDEFGABCDEFGの中にはAが2個ある

引数・戻り値共にポインタである関数
例;文字列中で指定した文字が最初に登場するポインタを返す
プロトタイプ宣言

char *chr1st(char *,chr);
             1^^^^^ 2^^
1で指定した文字列中で、2で指定した文字が最初に登場するポインタを返す

1
100番地 102番地
↓      ↓
K  A  Z  U  Y  A  \0

2
Z

戻り値 → 102番地

char *chr1st(char *str,char c) /* 第1案 */
{
	while(*str != c)
		str++;
	return str;
}

char *chr1st(char *str,chr c) /* 第2案 */
{
	while(*str != c && *str != '\0')
		str++;
	if(*str == '\0')
		return NULL;
	else
		return str;
}

例
#include 
char *chr1st(char *,char); /* 関数プロトタイプ */

void main(void)
{
	char *s;

	s = chr1st("ONOKI",'Z');
	if (NULL != s)
		printf("%s\n",s);
	else
		printf("文字列中に指定の文字はありません\n");
}
ポインタ・・・実体のアドレスを持った変数

	char *str = "ABC";

	str == 100番地
			  ↓
			 "A" "B" "C" "\0"

ポインタstrは文字列"ABC"の先頭アドレスを格納している。
                      ||
ポインタstrは文字'A'の格納されているアドレスを格納している。

			char a[]="ABC";		char *a="ABC";
文字列の	
先頭アドレス	a						a
を示す場合	

文字列中の	A	→	a[0]		A	→	*a
文字を個別	B	→	a[1]		B	→	*(a+1)
に示す場合	C	→	a[2]		C	→	*(a+2)
↑間接演算子(ポインタの示す実体を返す)

◎ポインタ・配列間には高い互換性がある。ポインタで宣言した文字列に対して
  配列の型でアクセスする事が可能!!
  (但し、混乱を招くので、望ましくない)
例
main()
{
	char *a = "ABC";

	printf("%c %c %c \n",a[0],a[1],a[2]);
}
実行後
A B C

	char a[100];
	・・・初期化をともわない配列の宣言、a[0]〜a[99]のchar型配列を
	   メモリ上に確保

	char *a;
	・・・char型の実体が格納されたアドレスを格納出来る変数(ポインタ)を作成

malloc();・・・指定されたサイズのメモリブロックを確保し、
			その先頭アドレスを返す。

	char *a=malloc(100);
	・・・100バイトのメモリブロックを確保し、その先頭アドレスを
	   aに代入する。
void *malloc(size_t size);
プロトタイプ
alloc.h or stdlib.h

型を問わない・・・取得したいメモリブロックのバイト数

char *a = (char *)malloc(buf.size);

int *ip = (int *)malloc(sizeof(int)*10);
            ↑
			型キャスト・・・変数型を一時的に変更する。
※コンパイラによっては、malloc等使用の際に
  型キャストが必要な場合がある。

型キャストの例
int a = 5;
char b;
b = a;・・・warnning!!
b = (char)a;・・・正常

sizeof演算子
必要なバイト数を返す
例
sizeof(int);
(int)sizeof(char);
sizeof 変数名

#include 
#include 

void main(void)
{
	char *a = (char *)malloc(sizeof(char)*5);
	int i;

	for(i=0;i<4;i++)															ascii code
	*(a+i)='A'+i;		100番地	'A'			41h(A)
	*(a+i)='\0';		100番地+1番地に 'A'+1		42h(B)
	for(i=0;i<4;i++)	100番地+2番地に 'A'+2		43h(C)
	printf("%c\n",*(a+i));	100番地+3番地に'A'+3 に代入	44h(D)
}
※ポインタaに他の番地を格納する事で、容易に他の文字列を示す事が出来る
(配列にない)  →動的メモリ管理
malloc()

char *str = malloc(sizeof (char)*10);

char型変数を格納するのに充分なメモリ*10 = 10byte
10バイトのメモリブロックを取得し、先頭番地を返す

int a[]
   ↓
a[0]  a[1]  a[2]  ・・・
 10   3200  -6300 ・・・
2byte 2byte byte

int型の配列を用いたプログラムの例
#include 
void main(void)
{
	int a[]={1,2,3,4,5,6,7,8,9,10};
	int goukei = 0;
	int i;

	for(i=0;i<10;i++)
		goukei += a[i];
	printf("%d\n",goukei);
}

int *a = (int *)malloc(sizeof(int)*3);
         ^^^^^^^
         型キャスト
         (省略可)

                ↓
a = 100番地
↓
 a b   c d   e f
2byte 2byte 2byte
(図1)

*aは何を示すか?
・・・int型は2byte変数なので、aに格納されている100番地から始まる2byteの中に
   格納された整数を示す。
*(a+i)は何を指すか?
    ↑
   sizeof(int)*1
・・・図1において、int型変数は2byteの領域を必要とする物なので a b の組み合わせ
は、異なる実体の下位、上位バイトに過ぎないので、何等意味を持たない。
   ある変数のポインタに整数nを足した場合、sizeof()*nを足したと見なす。
構造体(←→配列)	struct
・配列が同一のデータ型の集合体であるのに対し、構造体は異なるデータ型の集合体で
ある
構造体の宣言
	struct {変数型;変数型;・・・;}構造体名;
			↑
			構造体の要素→構造体メンバ
	struct {char name[20];
			int  sinchyou;
			char taizyu;
			char seibetu;
		   }a = {"高橋和也",
				 160,
				 70,
				 1;
				};

ex
int a;のように かつ
struct {構造定義} a;
	    ^^^↑^^^
		プログラム作成者が任意に定義できる

例1
#include 
void main(void)
{
	struct {char name[20];int sinchyou;} a = {"高橋和也",160};

	printf("%s %d\n",a.name,a.sincyou);
}

例2
#include 

void main()
{
	struct {char name[20];int nenrei;}
			a = {"大野克彦",34},
			b = {"太田耐輝",42};

	printf("%s %d\n",a.name,a.nenrei);
	printf("%s %d\n",b.name,b.nenrei);
}
#include 

void main (void)
{
	struct{
		int tosi;
		char n[20];
	} x[5] = {
		{10,"田中"),
		{30,"吉崎"},
		{24,"上村"},
		{ 5,"松川"},
		{60,"大野"}
	},tmp;
	int i,j;

	for(i=0;i<5;i++){
		for(j=i+1;j<5;j++){
			if(x[i].tosi < x[j].tosi){
				tmp = x[i]; x[i] = x[j]; x[j] = tmp;
			}
		}
	}
	for(i=0;i<5;i++)
		printf("%d  %s\n",x[i].tosi,x[i].n);
}
構造体タグ名

struct test{構造体メンバの定義・・・};
       ~↑~
       タグ名

#include 

struct test{・・・・};

void main(void)
{
	struct test a;
	  ・
	  ・ 処理
	  ・
}

void fnc(void)
{
	struct test x[10];
	  ・
	  ・処理
	  ・
}

特定の処理単位(関数など)に所属しないで定義される事で、
全ての処理単位内で使用が可能!!

構造体
タグ名・・・定義された構造体に付けられた名前
タグ名を付けて定義すれば同じメンバを持つ構造体を宣言するときタグ名のみの
  指定でもよい。

#include 

struct tri {float tei;float taka;};

void main(void)
{
	float men;
	struct tri a;
	a.tei = 5; a.taka = 3.5;

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

float tri_men(struct tri x)
{
	return x.tei * taka * 0.5;
}

共用体 ・・・ 単一の記憶領域で、異なるデータを処理する
union

union {int a;char b[2];};

union {fload a;int b;char c;};
		4byte	2byte	1byte

※共用体は、一つの記憶領域を異なる変数型で利用する方法
  記憶領域のサイズは、メンバ中で最大の領域を占める変数型の物になる。

構造体を共用体のメンバとして取る例

struct t{int a;float b;char [20];}; ・・・全長26byteの構造体
union {struct t s ; char x[26];};
       上で定義した 構造体を26バイトの
	   構造体	    char型配列と見なす

#include 

void main(void)
{
	union {int a;char s[2];}u;

	strcpy (u.s,"A");

	printf("%d\n",u.a);
}
構造体・・・異なるデータ型の集合体 (struct)
共用体・・・ひとつのメモリの領域を様々なデータ型と見なす (union)

struct
{
	int a;
	char b[2];
};

int a 	char b[2];
|	|	|	|	|
		b[0] b[1]

union
{
	int a;
	char b[2];
};

int a
|	|	|
char b[2]

構造体のポインタ

struct test {略};・・・メンバ定義
	   タグ名

struct test  a;・・・
構造体test型 構造体名

struct test *p;
p = &a;

(*p).a = 2;	→ポインタで示される構造体メンバには、
(*p).b = 3;   間接演算子(*)を付けてアクセス
	          ↓
		別の書きかた

p -> a = 2;	→ハイフン('-')と不等号('>')の組み合わせ
p -> b = 3;
入出力

標準入出力・・・コンソール(操作卓)からの入出力→キーボード、ディスプレー

ex シリアルポートで接続できるもの
	コンピュータ同士
	モデム
	スキャナ
	プロッタ
	MIDIインタフェース
	その他

プリンタ出力・・・プリンタに対する出力
外部入出力	・・・シリアルポートの入出力→RS232C等
エラー出力	・・・通常のリダイレクションが不可能な画面出力で、
			   エラーメッセージの表示などに用いられる

C言語上の表現
stdin	・・・標準入力
stdout	・・・標準出力
stdprn	・・・プリンタ出力
stdaux	・・・外部入出力
stderr	・・・エラー出力

putchar ('A');	・・・文字'A'を表示

putc()	・・・指定のストリームに一文字出力
putc('A',stdout);	・・・上のputchar と同じ 標準出力ストリームに一文字出力

printf("山口和範\n");	・・・画面に文字列"山口和範"を出力
fprintf(stdout,"山口和範\n");	・・・上と同じ意味

scanf("%s",str);	・・・書式付文字列取得
	キーボード入力を文字列としてポインタstrの示すメモリブロックに取得
fscanf(stdin,"%s",str);	・・・上と同じ意味
入出力
・定義済ストリームを使った入出力

stdout	・・・画面出力       →↓
stdin	・・・キーボード入力 →標準入出力※コンソール(操作卓)
stdprn	・・・プリンタ出力
stdaux	・・・外部入出力
stderr	・・・エラー出力

printf("山口和範\n");・・・画面に文字列出力
	↓
fprintf(stdout,"山口和範\n");
		||
	ストリームの指定・・・出力ストリーム指定付文字列出力

※OSのレベルで、各入出力装置はファイルと同等に扱われているので、Cプログラム内に
  おいては、ストリーム指定可能な入出力関数を用いる事で、芽先の変更ができる

#include 

void main()
{
	fprintf(stdout,"文字列\n");
}
	↓
A>prog > test.txt ・・・ファイルへのリダイレクション付で、プログラムを実行
※プログラム中ではstdoutを指定しているにもかかわらず、ファイルへの
  リダイレクションが行われる

ファイル入出力
ファイル操作・・・オープン、クローズを伴う
ファイル操作種類
・ストリームを使用したファイル操作・・・移植性が高い、遅い
・unix(OS)のファンクションコールを・・・移植性が低い、速い
 用いたファイル操作

fopen()・・・ファイルストリームのオープン
例
#include 

void main(void)
{
	FILE *fp;・・・ファイルストリームを示す FILE型ポインタを宣言

	fp = fopen ("test.txt","wt");・・・オープンしたファイルをポインタに接続

	fprintf (fp,"山口和範");・・・ポインタfpの示すファイルストリームに
                               文字列出力
	fclose (fp);・・・ポインタfpの示すファイルストリームをクローズ
}
入出力
fopen ()・・・ファイルをオープンし、ストリームに連結
fclose ()・・・ストリームに連結されたファイルをクローズ

FILe *fp;・・・ファイル型構造体(stdio.h内で定義)
	※オープンしたファイルの情報を保持している
1文字入・出力関数
標準入・出力
	入: getchar ();
	出: putchar ();
ストリームを対象とした入・出力関数
	入: getc ();
	出: putc ();

getc ・・・ストリームからの一文字取得
形式 int getc (FILE *stream);
プロトタイプ stdio.h
※指定ストリームから1文字読み込んで、ファイルポインタをインクリメント
戻り値	成功の場合
		→読み込んだ文字をint型で返す
		失敗の場合
		→EOF(-1)を返す
例 getcでgetcharと同じ動作

	int c;

	c = getc (stdin);
画面出力 stdout・・・標準出力・・・リダイレクション可能
         stderr・・・エラー出力・・・リダイレクション不可能(MS-DOSの場合)

※オペレティングシステムのコマンドの出力は、用途としてプリンタに
  印字されるべきものであっても標準出力に出力するように設計すべきである。
ex標準出力はパイプ処理を行うことが出来るし、リダイレクションに
  よって出力先を容易に変更できるから

OSコマンドの骨格
if (ファイル名の指定(or入力ストリームの指定)がある)
	指定したファイルからの入力を取得
else
	標準入力からの入力を取得
	 ・
	 ・
	 ・
	処理結果を標準出力に出力する

指定ファイルを画面出力するプログラム(cat コマンド)
	※ファイル名の指定なき場合は、標準入力を取得
#include 

void main(int argc,char *argv[])
{
	FILE *fp;
	int c;

	if (argc < 2)
		fp = stdin;
	else
		if (NULL == (fp = fopen(argv[1],"rt")))
			exit (1);
	while (EOF != (c=getc(fp)))
		putchar (c);
	fclose (fp);
}
バイト(文字)単位でファイルの複写を行う

#incluede 

void main(int argc,char *argv[])
{
	if(argc < 3)
		printf("ファイル名を指定せよ\n");
	else{
		FILE *f_in,*f_out;
		int chr;

		if(NULL != (f_int = fopen(argv[1],"rt"))){
			if(NULL != (f_out = fopen(argv[2],"wt"))){
			while(EOF != (chr = getc(f_in)))
				putc(chr,f_out);
			fclose(f_out);
		}
		else
			printf("出力ファイルがオープンできない\n");
		fclose(f_in);
		}
	else
		printf("にゅりょくファイルがオープン出来ない\n");
}
例題
test.txt

山口	10	5
茅山	20	4
康		15	18
平行	3	29
中島	18	14
林		21	47

上のようなテキストファイルが存在し、各行あたり1レコードで、レコードひとつにつ
き、氏名、リンゴ、ミカン、の項目を持つものとする。
ファイル中各人におけるリンゴとミカンの合計数および全員のリンゴ・ミカンの総合計
を求めるプログラムを作成せよ。

#include 

void main(int argc,char *argv[])
{
	FILE *fp;

	fp = fopen (argv[1],"rt");
	if (NULL != fp)
	{
		char name[80];
		int rin,mik,sum = 0,i;

		for (i=0;i<6;i++)
		{
			fscanf(fp,"%s%d%d",name,&rin,mik);
			sum += (rin+mik);
			printf("%s -> %d個\n",name,rin+mik);
		}
	printf("総計 -> %d\n",sum);
	}
}

書式付文字列読み込み・・・scanf()

#include 

void main(void)
{
	int a;
	char str[128];

	scanf("%d%s",&a,str);
	printf("%d%s\n",a,str);
}

scanf系関数・・・プロトタイプ

fscanf()・・・ストリームからの書式付文字列取得
書式
fscanf(FILE *fp,format,変数・・・);
       ^^^^^^^^ ^^^^^^ ^^^^^^^
       1        2      3

1 ストリーム
2 書式文字列
3 値を取得する

scanf()・・・文字列からの書式付文字列取得
書式
sscanf(char *,format,変数・・・);
       取得元文字列

scanf系 欠点
○書式文字列の示す以後の値が入力された時、暴走の危険がある
  (特に手入力対象のscanf())
○EOFの検出が出来ない
※手入力の必要な場合
→getsで連続した文字列として取り込みsscanfで切り分ける
fscanf()・・・対象はストリーム(動的)
sscanf()・・・対象は文字列(静的)

※fscanfは動的データを取得の対象としているのでEOFの検出が
  可能であるが、sscanfは静的データが対象であるので、EOFの検出不可
  (常に同じ値が取得される)
問
下のようなファイル、test.txtがあるスペース(空白文字)で区切られた各行の2つの数字
文字列を数値としてプログラム中に取得し、その合計が10以上の偶数であれば、ファイルt
est2.txtにその合計を出力するプログラムを作成せよ。

test.txt
1 2
5 10
6 8
4 6
5 3
4 1

test2.txt
14
10

#include 

void main (void)
{
	FILE *f1,*f2;

	f1 = fopen ("e:\\test.txt","rt");
	if (f1 != NULL){
		f2 = fopne ("e:\\test2.txt","wt");
		if (f2 != NULL){
			char str[128];
			int a,b;

			while (NULL != fgets(str,128,f1)){
				sscanf (st,"%d%d",&a,&b);
				if ((a + b) >= 10 && ((a + b) % 2) == 0){
					fprintf (f2,"%d\n",a + b);
				}
			}
			fclose (f2);
		}
		else{
			printf ("出力ファイルがオープン出来ない\n");
		}
		fclose (f1);
	}
	else{
		printf("入力ファイルがオープン出来ない\n");
	}
}
ストリーム入・出力
 ストリーム(コンピュータ内の情報の流れ)に沿った入出力
 →シーケンシャル・アクセス

※ランダムアクセスする為には?
 →ストリーム上のファイルポインタの移動(位置付け)
   を任意に行う必要あり!!

fseek()・・・ストリーム上のファイルポインタを移動する
プロトタイプ	stdio.h
書式	int fseek (FILE *stream , long int offset , int whence);
引数	stream ・・・ 処理対象となるストリームの指定
    	offset ・・・ whenceで指定された位置から離れるバイト数
    	whence ・・・ ストリーム上の基準となる位置

※whenceの取れる値
シンボル 値 意味
SEEK_SET 0  ファイルの始め
SEEK_CUR 1  現在のファイルポインタ
SEEK_END 2  ファイルの末尾
↑
stdio.h中で、#define文で定義されているので、原始プログラム中では
この言葉(シンボリック定数)で代用ができる。
char型配列	str[]は以下のように初期化されている

char str[10] = "ZYXWVUTSR";

str中の各文字を、昇順に並べ変えるプログラムを作成せよ

#include 

void main(void)
{
	char str[10] = "ZYXWVUTSR";
	char w;
	int i,j;

	printf("%s\n",str);

	for (i=0;i<9;i++){
		for (j=i+1;j<9;j++)
			if (str[i] > str[j]){
				w = str[i];
				str[i] = str[j];
				str[j] = w;
			}
		}
	}
	printf ("%s\n",str);
}

  • 高橋和也のホームページへ戻る