C言語のファイル入出力

ファイル処理の手順は、ファールオープン->ファイルの読み書き->ファイルクローズとなる。

int main(void){

	FILE *fp;
	int ch;

	fp = fopen("myfile1.txt", "r");
	if (fp == NULL) return 1;

	ch = fgetc(fp);
	printf("ch=%c\n", ch);

	fclose(fp);

	return 0;
}

$ touch myfile1.txt
$ sudo vi myfile1.txt
$ ./dev
ch=a

ファイルが正常にオープンされると、ポインタを返す
FILE *fi, *foが良く使われる
入出力データの処理を効率的に行う為、ストリームという概念を使用する(stdin, stdout, stderr)
fp = fopen(“myfile1.txt”, “r”); と記述する

### fopen関数のオープンモード
r: read
w: write
a: apend
rb: binary fileのread
wb: binary fileのwrite
ab: binary fileのappend
※バイナリファイルとは自動変換を行わないこと

### ファイル入出力関数
fputs(), fgetc(), fputs(), fgets(), fprintf(), fscanf()

int main(void){

	FILE *fp;
	char ss[256];

	if((fp=fopen("myfile1.txt", "r")) == NULL){
		printf("file cannot open.\n");
		return 1;
	}

	while (fgets(ss, 256, fp) != NULL){
		printf("%s", ss);
	}

	fclose(fp);

	return 0;
}

### ファイル出力

int main(void){

	FILE *fp;
	int dt = 1234;

	if((fp=fopen("myfile1.txt", "w")) == NULL){
		printf("file cannot open.\n");
		return 1;
	}

	fputs("char output\n", fp);
	fputc('A', fp);
	fputc('\n', fp);
	fprintf(fp, "dt=%d\n", dt);

	fclose(fp);

	return 0;
}

ファイルコピー

int main(void){

	FILE *fin, *fout;
	char infile[80], outfile[80], ss[256];

	printf("inputfile="); gets(infile);
	printf("outputfile="); gets(outfile);

	if((fin=fopen(infile, "r")) == NULL){
		printf("cannot open input file.\n");
		return 1;
	}
	if((fin=fopen(outfile, "w")) == NULL){
		printf("cannot open output file.\n");
		return 1;
	}

	while(fgets(ss, 256, fin) != NULL){
		fputs(ss, fout);
	}

	fclose(fin);
	fclose(fout);

	return 0;
}

### コマンドラインから引数を渡す

int main(int argc, char *argv[]){

	FILE *fin, *fout;
	char ss[256];

	if(argc != 3){
		printf("引数の数が異なります\n");
		return 1;
	}

	if((fin=fopen(argv[1], "r")) == NULL){
		printf("cannot open inputfile\n");
		return 1;
	}
	if((fin=fopen(argv[2], "w")) == NULL){
		printf("cannot open outputfile\n");
		return 1;
	}

	while (fgets(ss, 256, fin) != NULL){
		fputs(ss, fout);
	}

	fclose(fin);
	fclose(fout);

	return 0;
}

argv[0]はコマンド名そのもの。
argcは全ての引数、argv[]は対応する引数

int main(int argc, char *argv[]){

	if(argc != 3) return 1;
	printf("argc =%d\n", argc);
	printf("argv[0] =%s\n", argv[0]);
	printf("argv[1] =%s\n", argv[1]);
	printf("argv[2] =%s\n", argv[2]);

	return 0;
}

C言語の標準ライブラリ関数

– 全て覚える必要はなく、使える関数だけ選択し、慣れるにしたがって利用関数を増やしていく
– ソースプログラム -> コンパイル -> オブジェクトモジュール + [標準ライブラリ] -> リンク -> 実行モジュール
– (1)ハードウェアにアクセスする関数、(2)数学処理、(3)一般ユーザでも記述可能な処理、(4)メモリ処理のような特殊動作関数 がある。(1)(2)(4)の独自開発は負担が大きい。

### 標準ライブラリ関数一覧
stdio.h: 入出力処理
stdlib.h: 一般ユーティリティ、数値変換や記憶割り当てなどサポート
string.h: 文字列操作
ctype.h: 文字操作
math.h: 数学処理
time.h: 時間処理

### 入出力関数 stdio.h
https://en.wikipedia.org/wiki/C_file_input/output

### 一般ユーティリティ関数 stdlib.h
abort, abs, atexit, atof, atoi, atol, bsearch, calloc, div, exit, free, getenv, labs, ldiv, malloc, qsort, rand, realloc, srand, strtod, strtol, strtoul, systemなど

### 文字列処理関数 string.h
https://en.wikipedia.org/wiki/C_string_handling

int main(void){

	char *p, s1[80], s2[80], s3[] = "abcdefg";
	int n;

	strcpy(s1, "ABC");
	strcpy(s2, "XYZ");
	printf("s1=%s s2=%s\n", s1, s2);

	n = strlen(s1);
	printf("s1 length=%d\n", n);

	if(strcmp(s1, s2) > 0)
		printf("left is large\n");
	if(strcmp(s1, s2) < 0)
		printf("right is large\n");
	if(strcmp(s1, s2) == 0)
		printf("equal\n");
	
	strcat(s1, "1234");
	strcat(s1, s2);
	strncat(s1, s3, 4);
	printf("文字連結=%s\n", s1);

	p = strchr(s3, 'c');
	puts(p);
	p = strstr(s3, "def");
	puts(p);

	return 0;
}
&#91;/code&#93;

### 文字処理関数 ctype.h
<a href="https://en.wikipedia.org/wiki/C_character_classification">https://en.wikipedia.org/wiki/C_character_classification</a>

int main(void){

	int c, c2;

	printf("------isalnum:\n");
	for (c=0; c<=127; c++)
		if(isalnum(c)) putchar(c);
	
	printf("------isalpha:\n");
	for (c=0; c<=127; c++)
		if(isalpha(c)) putchar(c);

	printf("------isdigit:\n");
	for (c=0; c<=127; c++)
		if(isdigit(c)) putchar(c);

	printf("------islower:\n");
	for (c=0; c<=127; c++)
		if(islower(c)) putchar(c);

	printf("------isupper:\n");
	for (c=0; c<=127; c++)
		if(isupper(c)) putchar(c);

	printf("------isxdigit:\n");
	for (c=0; c<=127; c++)
		if(isxdigit(c)) putchar(c);

	printf("------isprint:\n");
	for (c=0; c<=127; c++)
		if(isprint(c)) putchar(c);

	printf("------isgraph:\n");
	for (c=0; c<=127; c++)
		if(isgraph(c)) putchar(c);

	printf("------ispunct:\n");
	for (c=0; c<=127; c++)
		if(ispunct(c)) putchar(c);

	printf("------iscntrl:\n");
	for (c=0; c<=127; c++)
		if(iscntrl(c)) putchar(c);

	printf("------isspace:\n");
	for (c=0; c<=127; c++)
		if(isspace(c)) putchar(c);

	printf("------tolower:\n");
	c = 'A';
	c2 = tolower(c);
	printf("c=%c tolower=%c\n", c, c2);

	printf("------toupper:\n");
	c = 'b';
	c = toupper(c);
	printf("toupper=%c\n", c);

	return 0;
}
&#91;/code&#93;
$ ./dev
------isalnum:
0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz------isalpha:
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz------isdigit:
0123456789------islower:
abcdefghijklmnopqrstuvwxyz------isupper:
ABCDEFGHIJKLMNOPQRSTUVWXYZ------isxdigit:
0123456789ABCDEFabcdef------isprint:
 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~------isgraph:
!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~------ispunct:
!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~------iscntrl:
------isspace:
------tolower:
c=A tolower=a
------toupper:
toupper=B

### 数学処理関数 math.h
sin, cos, tan, log, log10, pow, sqrtなど
関数の戻り値はdouble, 引数はdouble, 角度はラジアン
<a href="https://en.wikipedia.org/wiki/C_mathematical_functions">https://en.wikipedia.org/wiki/C_mathematical_functions</a>

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

int main(void){

	double x, r;

	printf(" x sin(x) cos(x)\n");
	for (x=0.0; x<=90.0; x+=15.0){
		r = x * 3.14159 / 180.0;
		printf("%6.2f %8.5f %8.5f\n", x, sin(r), cos(r));
	}

	return 0;
}
&#91;/code&#93;

### 時間処理関数 math.h
時間処理を行う時は複数の関数を組み合わせて使用する
<a href="https://en.wikipedia.org/wiki/C_date_and_time_functions">https://en.wikipedia.org/wiki/C_date_and_time_functions</a>

#include <stdio.h>
#include <time.h>

int main(void){

	time_t mytime;
	struct tm *ltime;
	char *p;

	time(&mytime);
	ltime = localtime(&mytime);
	p = asctime(ltime);
	printf("japan time: %s", p);

	p = ctime(&mytime);
	printf("ctime:  %s", p);

	printf("year=%d\n", ltime->tm_year+1900);
	printf("month=%d\n", ltime->tm_mon+1);
	printf("day=%d\n", ltime->tm_mday);
	printf("hour=%d\n", ltime->tm_hour);
	printf("minute=%d\n", ltime->tm_min);
	printf("second=%d\n", ltime->tm_sec);

	return 0;
}

$ ./dev
japan time: Fri May 15 23:23:52 2020
ctime: Fri May 15 23:23:52 2020
year=2020
month=5
day=15
hour=23
minute=23
second=52

int main(void){

	clock_t start, end;
	int keika;
	char s[80];

	start = clock();
	printf("started. put [enter], then end.");
	gets(s);
	end = clock();
	keika = (end - start) / CLOCKS_PER_SEC;
	printf("end: %d second passed. \n", keika);

	return 0;
}

### その他関数
atoi(), atol(), atof(), rand(), system(), exit()

int main(void){

int i, n;

srand(555);
printf("0 ~ 32767:\n");
for(i=1; i<=10; i++) printf("%d ", rand()); printf("\n0.0 ~ 1:\n"); for(i=1; i<=10; i++) printf("%5.3f ", rand() / (RAND_MAX+1.0)); printf("\n1 ~ 6:\n"); for(i=1; i<=10; i++){ n = (int)(rand() / (RAND_MAX+1.0) * 6.0); ++n; printf("%d ", n); } printf("\n"); return 0; } [/code] $ ./dev 0 ~ 32767: 1897361756 511234109 2040558095 299669925 1854125204 1147783809 1808688013 1528174380 1122472434 999190120 0.0 ~ 1: 0.224 0.826 0.696 0.201 0.311 0.565 0.758 0.135 0.223 0.791 1 ~ 6: 1 3 4 3 3 3 3 1 2 4 入出力、数学、文字、時間に加えて、ハードウェア周りか。 ハードウェア周りもマスターしたいな。

C言語の前処理指令

前処理司令とは、コンパイルの前に、テキスト上の処理を行う
前処理司令を使うことで、別のファイルを読み込んだり、記号定数を用意したり、条件付きコンパイルなどを行うことができる
前処理の工程を行うのがプリプロセッサ(preprocessor)

### 前処理司令
– #inclue: 指定されたファイルを挿入
– #define: マクロ定義
– #undef: マクロ定義を取り消す
– #if #ifdef #ifndef: 条件付きコンパイル
– #elif #else #endif: 同上
– defined: #if, #elifの中で判定処理に用いる単項演算子
– #line: 行番号の付け替え
– #pragma: コンパイラへのオプション指示
– #error: 前処理時のエラー表示
– # : #defineの中で使用時:仮引数の文字列化、単独使用時: 空司令であり何も実行しない
– ## : 字句の連結

### #inclue
指定されたファイルをディスクから読み込み、その場所に挿入する

/* myhead.h */
int hdt = 1234;

void myfunc(void){
	printf("test program\n");
}
#include <stdio.h>
#include "myhead.h"

int main(void){

	myfunc();
	printf("hdt=%d\n", hdt);

	return 0;
}

includeで指定するファイルはヘッダファイルと呼ばれる
ヘッダファイルは.hが付いており、標準ディレクトリから対象ファイルを探す

### #define
– オブジェクト形式マクロ
defineは文字列の置き換えを行う。関数のような形式でマクロ置換をすると言う意味をもつ
大文字で書くのが慣例

#define MYSIZE 100
#define MYSTR “abc”
#define MYCH ‘A’
#define begin {
#define end }

stdio.hではEOFは(-1)と定義されている

– 関数形式マクロ
#define average(a,b) (a+b)/2
#define putd(dt) printf(“%d\n”,dt);
関数式マクロは慎重に記述する必要がある

#include <stdio.h>

#define MAXSIZE 1234
#define average(a,b) (a+b)/2
#define putd(dt) printf("%d\n", dt)

int main(void){

	int n1, n2;

	n1 = MAXSIZE;
	n2 = average(1111, 3333);
	printf("n1=%d\n", n1);
	printf("n2=%d\n", n2);
	putd(n2);

	return 0;
}

$ ./dev
n1=1234
n2=2222
2222

### #if
if, elif, else, endifで条件付きコンパイル
デバッグ用によく使用される

#define JAPAN_MODE 1

#if JAPAN_MODE == 1
	char msg[] = "こんにちは";
#else
	char msg[] = "Hello";
#endif

int main(void){

	printf("msg=%s\n", msg);

	return 0;
}

フレームワークだと、#defineなどの定義はまとめて別ファイルに書くことが多いように思いますが、includeの下に書くんですね。言語の歴史が見れて面白い。

C言語構造体

### 構造体とは
関連したいくつもの変数がある時、これをまとめて一つの名前だけで処理できるようにするもの

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

struct Person {
	char name[40];
	int height;
	int weight;
};

void dataout(struct Person ps){
	printf("name=%s height=%d weight=%d\n", ps.name, ps.height, ps.weight);
}

int main(void){
	struct Person dt, dt2;

	strcpy(dt.name, "tanaka kenichi");
	dt.height = 180;
	dt.weight = 70;

	dt2 = dt;
	dataout(dt2);

	return 0;
}

$ ./dev
name=tanaka kenichi height=180 weight=70

### 構造体の宣言
構造体はintやcharなどの基本データ型を好きなように組み合わせて新しいデータ型として扱うもの
struct 構造体名 {
必要な変数宣言の並び;
};
構造体は初期化することもできる
構造体の宣言はプログラムの先頭部分に記述する
構造体のメンバを指定する時は、2種類のメンバアクセス演算子を使う
-> 変数名で操作する時は”.”
-> ポインタで操作する時は”->”

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

struct Person {
	char name[40];
	int height;
	int weight;
};

int main(void){
	struct Person d1, d2;
	struct Person *p = &d2;

	strcpy(d1.name, "tanaka kenichi");
	d1.height = 180;
	d1.weight = 70;

	strcpy(p->name, "suzuki tomomi");
	p->height = 160;
	p->weight = 60;

	printf("%s %d %d\n", d1.name, d1.height, d1.weight);
	printf("%s %d %d\n", p->name, p->height, p->weight);

	return 0;
}

### 構造体の引数渡し

#include <stdio.h>

struct myst {
	int d1, d2;
};

struct myst add(struct myst x, struct myst y){
	struct myst wk;
	wk.d1 = x.d1 + y.d1;
	wk.d2 = x.d2 + y.d2;
	return wk;
}

int main(void){
	struct myst a, b = {10, 20}, c = {100, 200};
	
	a = add(b, c);
	printf("%d %d\n", a.d1, a.d2);

	return 0;
}

### 共用体
メモリ上の同じアドレスを複数の変数で共用すると言う機能
キーワードがstructではなくunionになる

union MyType {
	char ch;
	int n1;
	int n2;
};

int main(void){
	union MyType dt;

	dt.n1 = 0x41424344;
	printf("dt.ch=%X\n", dt.ch);
	printf("dt.n1=%X\n", dt.n1);
	printf("dt.n2=%X\n", dt.n2);

	return 0;
}

$ ./dev
dt.ch=44
dt.n1=41424344
dt.n2=41424344

### 列挙型
名前付き定数を使って、データの取り得る値を全て列挙したもの

enum RetType {YES, NO, CANCEL};

int main(void){
	enum RetType ret;

	ret = YES;
	if (ret == YES)
		printf("ret is yes\n");
	else if (ret == NO)
		printf("ret is no\n");
	else if (ret == CANCEL)
		printf("ret is cancel\n");
	printf("%d %d %d\n", YES, NO, CANCEL);
	
	return 0;
}

C言語ポインタ

### メモリ上のデータ配置
どのアドレスに確保するかはコンパイラとリンカとOSが決める
変数のアドレスは&を使う e.g. &mydt

int main(void){
	int mydt = 1234;
	printf("mydt=%d\n", mydt);
	printf("mydt address=%p\n", &mydt);
	return 0;
}

$ ./dev
mydt=1234
mydt address=0x7ffd3a448134

### ポインタ
アドレス値を格納できる
int *pt

int main(void){
	int *pt;
	int mydt = 1234, idt;

	pt = &mydt;
	idt = *pt;
	printf("mydt=%d\n", mydt);
	printf("*pt=%d\n", *pt);
	printf("idt=%d\n", idt);

	mydt = 555;
	printf("mydt=%d\n", mydt);
	printf("*pt=%d\n", *pt);

	*pt = 666;
	printf("mydt=%d\n", mydt);
	printf("*pt=%d\n", *pt);

	return 0;
}

$ ./dev
mydt=1234
*pt=1234
idt=1234
mydt=555
*pt=555
mydt=666
*pt=666

### ポインタの演算
&: アドレスを取り出す(アンド)
*: アドレスにある値を参照する(アスタリスク)

– ポインタを宣言するとき、型指定が必要

### ポインタの配列

int main(void){
	int ary[3] = {100, 110, 120};
	int *pt;

	pt = ary;
	printf("adress=%p value=%d\n", pt, *pt);

	pt = &ary[0];
	printf("adress=%p value=%d\n", pt, *pt);

	pt = &ary[1];
	printf("adress=%p value=%d\n", pt, *pt);

	return 0;
}

$ ./dev
adress=0x7fff517b4eec value=100
adress=0x7fff517b4eec value=100
adress=0x7fff517b4ef0 value=110

### ポインタの加減算
+ – ++ —

int main(void){
	int ary[3] = {100, 110, 120};
	int *pt;

	pt = ary;
	while(1){
		printf("%d\n", *pt);
		if (*pt == 120) break;
		++pt;
	}

	pt = ary;
	printf("%d\n", *pt);
	printf("%d\n", *(pt+1));
	printf("%d\n", *(pt+2));

	return 0;
}

### ポインタと文字列

int main(void){
	char s[10] = "ABCD";
	char *p;

	p = s;

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

	while(*p != '\0') {
		*p = tolower(*p);
		++p;
	}

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

	return 0;
}

./dev
s=ABCD
p=ABCD
s=abcd
p=abcd
ポインタは文字列アドレスだけでなく、文字列リテラルのアドレスも持つことができる。

### ポインタを引数にする

void func(int *dt){
	printf("value=%d\n", *dt);
}

int main(void){
	int nn = 1234;
	func(&nn);

	return 0;
}

### ポインタによる配列渡し
関数への文字列渡しはポインタを使うのが一般的

void strout(char s[]){
	int i;
	printf("%s\n", s);
	for(i=0; s[i] != '\0'; i++)
		printf("%X ", s[i]);
	printf("\n");
}

void strout(char *p){
	printf("%s\n", p);
	while(*p){
		printf("%X ", *p);
		++p;
	}
	printf("\n");
}

int main(void){
	char st[] = "ABCD";

	strout(st);
	strout_p(st);

	return 0;
}

ポインタを戻り値にする関数を書くこともある

ポインタも、メモリのアドレスにアドレスを格納するという概念さえ理解できれば言うほど難しくはありませんね。

C言語の関数

### 関数基本形
何らかの仕事をするのを「サブルーチン」、計算を「関数」と読んでいる言語もある
int foo(double a) // int型を返す関数, double aを引数という
double foo(double a) // double型を返す関数
void foo(double a) // データを返さない関数
関数から値を返す時はreturn文を使う
関数を呼び出す場合、関数の書式が先に判明している必要がある(関係書式だけ記述したものを関数プロトタイプという)

#include <stdio.h>

double ave(double x, double y){
	double wk;
	wk = (x + y) / 2.0;
	return wk;
}

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

	a = 11.11;
	b = 33.33;
	c = ave(a, b);
	printf("a=%f b=%f ave=%f\n", a, b, c);

	return 0;
}

$ ./dev
a=11.110000 b=33.330000 ave=22.220000

数値を渡す場合は、Call by value(値呼び出し)という
配列を渡す場合は、アドレスによる呼び出し

void disp_ary(int n[]){
	printf("n[0]=%d\n", n[0]);
	printf("n[4]=%d\n", n[4]);
}

int main(void){
	int dt[5] = {10, 20, 30, 40, 50};
	disp_ary(dt);

	return 0;
}

$ ./dev
n[0]=10
n[4]=50
※配列名を指定すると、配列の先頭アドレスとして評価される
Cでは配列の長さはプログラマーが上手くやってくださいとなっている

### 文字列の引き渡し
最後の”\n”を利用することで長さを知ることができる

#include <stdio.h>

void strout(char s[]){
	int i;
	printf("s=%s\n", s);
	for(i=0; s[i] != '\0'; i++)
		printf("%X ", s[i]);
	printf("\n");
}

int main(void){
	char st[] = "ABCD";
	strout(st);
	strout("abcde");
	return 0;
}

### ローカル変数とグローバル変数
グローバル変数はmain()の中の値でも使える
ローカル変数は衝突しない。グローバルよりローカルが優先される

#include <stdio.h>

int g;

void func(void){
	int b;
	b = 99;
	printf("func b=%d g=%d\n", b, g);
}

int main(void){
	int a;
	a = 10;
	g = 20;
	printf("func a=%d g=%d\n", a, g);
	func();

	return 0;
}

$ ./dev
func a=10 g=20
func b=99 g=20

staticの静的変数は値を保持する

#include

int g;

void func(int n){
int a = 0;
static int s = 0;

a = a + n;
s = s + n;
printf(“a=%d s=%d\n”, a, s);
}

int main(void){
int i;
for(i=1; i<=5; i++){ func(i); } return 0; } [/code] $ ./dev a=1 s=1 a=2 s=3 a=3 s=6 a=4 s=10 a=5 s=15

C言語printf, scanf

### printfの機能
文字列や変数値を出力する機能
他にもputchar(), puts()などがあるが、printf()でも記述できる。
putchar(ch);
puts(ss);
printf(“%c”, ch);

### printfの変換指定
%o: 8進数出力
%d: 10進数出力
%x: 16進数出力
%X: 16進数出力
%ld: long型数値を10進数で出力
%f: 不動小数点として出力
%e: 指数形式で出力
%E: 指数形式で出力
%c: 文字として出力
%s: 文字列として出力
%p: アドレスを表示

int main(void){
	char c;
	int n;
	long l;
	float f;
	double d;
	char s[20] = "abcde";

	c = 65;
	printf("c: %c\n", c); //文字列
	printf("10: %d\n", c); //10進数
	n = 66;
	printf("c: %c\n", n); //文字列
	printf("10: %d\n", n); //10進数
	n = 1000;
	printf("8: %o\n", n); //8進数
	printf("10: %d\n", n); //10進数
	printf("16x: %x\n", n); //16進数
	printf("16X: %X\n", n); //16進数 英大文字
	l = 1234567890;
	printf("ld: %ld\n", n); //long
	f = 12.34F;
	printf("f: %f\n", n); //浮動小数点数
	printf("e: %e\n", n); //指数形式
	printf("E: %E\n", n); //指数形式 英大文字
	d = 56789.12;
	printf("f: %f\n", d); //浮動小数点数
	printf("e: %e\n", d); //指数形式
	printf("E: %E\n", d); //指数形式 英大文字
	printf("s: %s\n", s); // 文字列
	printf("c=%d n=%d s=%s", c, n, s);

	return 0;
}

### printfのオプション
%d: 必要な幅で出力
%8d: 8文字で出力
%-8d: 左寄せ8文字幅で出力
%2d: 指定値の方が小さくても必要幅は確保
%8X: 先行スペース文字を入れて8文字
%08X: 先行0を入れて8文字出力 ※16進数に便利
%12f: 小数点を入れて12桁
%9.2f:小数点を入れて9桁、小数点以下2桁
%9.f: 整数部のみ9桁

### scanfの機能
scanf()は書式付き入力を行う関数
scanf(書式指定文字列, 変数アドレスの並び);
格納先は変数名ではなく変数のアドレス
※単純変数の場合は&を付け、配列変数の場合は&は不要

### scanfの変数指定
%o: 8進数
%d: 10進数
%ld: long型変数に10進数読み込み
%x: 16進数
%f: float型
%lf: double型変数
%c: 1文字
%s: 文字列

int main(void){
	int i;
	float f;
	double d;
	long l;
	char s[80];

	printf("1文字入力:");
	scanf("%c", &i);
	printf("1文字出力=%c\n", i);

	printf("文字列入力:");
	scanf("%s", s);
	printf("文字列出力=%s\n", s);

	printf("8進入力:");
	scanf("%o", &i);
	printf("10進出力=%d\n", i);

	printf("10進入力:");
	scanf("%d", &i);
	printf("10進出力=%d\n", i);

	printf("16進入力:");
	scanf("%x", &i);
	printf("10進出力=%d\n", i);

	printf("long入力:");
	scanf("%ld", &l);
	printf("long出力=%ld\n", l);

	printf("float入力:");
	scanf("%f", &f);
	printf("float出力=%f\n", f);

	printf("double入力:");
	scanf("%lf", &d);
	printf("double出力=%16f\n", d);

	return 0;
}

$ ./dev
1文字入力:A
1文字出力=A
文字列入力:abc
文字列出力=abc
8進入力:11
10進出力=9
10進入力:9
10進出力=9
16進入力:11
10進出力=17
long入力:1234567890
long出力=1234567890
float入力:12.3456
float出力=12.345600
double入力:123456.78901
double出力= 123456.789010

gets()は空白を含む文字列を読み込むが、scanf()は空白類文字列に出会うと終了

### 標準入力と標準出力
リダイレクトを指示すると、ファイルから入力し、ファイルから出力するようになる
bb.txt // 出力先をbb.txtにする

int main(void){
	char ss[256];

	gets(ss);
	puts(ss);

	return 0;
}

$ ./dev
abcd
abcd
$ ./dev >aa.txt
hogehoge
$ ./dev bb.txt
$ ls
aa.txt bb.txt dev hello.c
$ cat bb.txt
hogehoge

標準出力の意味がここでやっとはっきりしました。

C言語のコンソール入出力基本パターン1

### 基本用法
– getchar(), gets(), scanf()は「標準入力」(キーボード)から入力
– putchar(), puts(), printf()は「標準出力」(ディスプレイ)に出力

int main(void){
	int ch; // 変数確保

	ch = getchar();
	putchar(ch); // 入力文字を画面に出力

	return 0;
}

$ ./dev
hello
h
※getchar()関数はキーボードから1文字を入力する

### 連続文字入力

#include <stdio.h>
#include <ctype.h> /* for toupper() */

int main(void){
	int ch; // 変数確保

	ch = getchar();
	while (ch != EOF){
		ch = toupper(ch);
		putchar(ch);
		ch = getchar();
	}

	return 0;
}

$ ./dev
hello
HELLO
※EOFはend of fileで、[ctl]+[z]で終了。
※バッファリング処理で、内部バッファで1行単位で読み込み、内部的に1文字ずつ処理している。

### 1行入出力

int main(void){
	char s[80];

	gets(s);
	puts(s);

	return 0;
}

$ ./dev
abc def
abc def

gets()はキーボードから文字列を1行入力する為の関数
puts()はディスプレイに文字列を出力する為の関数
– 文字列だけしか出力できない
– 文字列を出力した後、自動的に改行を行う

hello.c:(.text+0x24): warning: the `gets’ function is dangerous and should not be used.
gets()関数の配列オーバーに対する危険性は広く認識されている。C11では安全なgets_s()関数が使えるようになっている。

### 連続文字列入力

int main(void){
	char s[80];

	while (gets(s) != NULL){
		puts(s); puts(s);
	}

	return 0;
}

$ ./dev
abcde
abcde
abcde
XYZ
XYZ
XYZ

[ctl]+[z]でNULLを返す。NULLの値はstdio.hの中で定義されている

int main(void){
	char s[80];

	while(1) {
		gets(s);
		if (strcmp(s, ".") == 0) break;
		printf("input char is %s.\n", s);
	}

	return 0;
}

strcmp()は文字列を比較する関数

### 整数値の入出力

int main(void){
	int n;

	scanf("%d", &n);
	printf("%d\n", n);

	return 0;
}

scanf()関数はキーボードから色々なデータを入力する機能をもつ。
scanf(“%d”, &n)は、変数のアドレスを記述する

double ddt;
scanf(“%lf”, %ddt);
printf(“%f\n”, ddt);

scanf()は数値入力のみ受け付ける

int main(void){
	int n = 9999;

	scanf("%d", &n);
	printf("number is %d\n", n);

	return 0;
}

gets()関数で文字列として受け取って、atoi()関数で整数に変換という方法もある
不動小数点の場合はatof()

int main(void){
	int n;
	char s[80];

	gets(s);
	n = atoi(s);
	printf("number is %d.\n", n);

	return 0;
}

scanf や fgetsは、格納先に入力できる最大長を指定することでバッファオーバーランを回避することができる
getsは入力される文字列の長さを制限する機能が存在しない
C言語の設計者は、配列格納とは異なる意図でgets()を作ったのだろうか
プログラムの脆弱性をつくプログラマーも相当ロジカルでないと務まらんな

C言語の制御文

### 制御文の記述
– if, for, whileなどの制御式を使う
– 制御式が条件を満たしていることをture, 満たしていないことをfalse
– 何もしない空文は ; と書く

### if文

int main(void){
	int a = 10, b = 20, c;

	if (a == 10)
		printf("a is 10. \n");
	if (a == b){
		printf("a is equal to b. \n");
	} else {
		printf("a is not equal to b. \n");
	}

	if (b > 100) c = 1;
	else if (b > 50) c = 2;
	else if (b > 10) c = 3;
	else c = 9;
	printf("b=%d c=%d\n", b, c);
	return 0;
}

$ ./dev
a is 10.
a is not equal to b.
b=20 c=3

### for

int main(void){
	int i, sum, mul;

	sum = 0; mul = 1;
	for(i=1; i<=5; i++){
		sum = sum + i;
		mul = mul * i;
	}
	printf("sum=%d mul=%d\n", sum, mul);

	for(i=40; i>=20; i=i-2){
		printf("%d ", i);
	}
	printf("\n");
	return 0;
}

$ ./dev
sum=15 mul=120
40 38 36 34 32 30 28 26 24 22 20

### while
while(1)と書くと、breakを使わないと無限ループになる

int main(void){
int i=1;
while(i < 1000){ printf("%d ", i); i = i * 2; } printf("\n"); return 0; } [/code] $ ./dev 1 2 4 8 16 32 64 128 256 512 ### do-while do-whileは使用頻度が然程高くない [code] int main(void){ int n; char a[80], b[] = "ABCD"; n = -1; do { ++n; a[n] = b[n]; } while(b[n] != '\0'); printf("a=%s b=%s\n", a, b); return 0; } [/code] $ ./dev a=ABCD b=ABCD ### switch [code] int main(void){ int a; for(a=1; a<=5; a++){ printf("-------%d\n", a); switch(a){ case 1: printf("a=1\n"); break; case 3: printf("a=3\n"); break; case 5: printf("a=5\n"); break; default: printf("others\n"); } } return 0; } [/code] $ ./dev -------1 a=1 -------2 others -------3 a=3 -------4 others -------5 a=5 ### break & continue [code] int main(void){ int n; printf("----- break test\n"); for (n=1; n<=3; n++){ printf("%d start\n", n); if (n == 2) break; printf("%d end\n", n); } printf("----- continue test\n"); for (n=1; n<=3; n++){ printf("%d start\n", n); if (n == 2) continue; printf("%d end\n", n); } return 0; } [/code] $ ./dev ----- break test 1 start 1 end 2 start ----- continue test 1 start 1 end 2 start 3 start 3 end 意図的に無限ループを指定できるってのは面白いです。

C言語の演算子

### 算術演算子
+: 加算
-: 減算
*: 乗算
/: 除算
%: 余り

int main(void){
	int a, b, n;
	double d1, d2, dd;

	a = 100; b = 30;

	n = a + b; printf("%d\n", n);
	n = a - b; printf("%d\n", n);
	n = a * b; printf("%d\n", n);
	n = a / b; printf("%d\n", n);
	n = a % b; printf("%d\n", n);

	d1 = 90.0; d2 = 40.0;
	dd = d1 / d2; printf("%f\n", dd);
	return 0;
}

$ ./dev
130
70
3000
3
10
2.250000
intとdoubleなど混合計算も可能。その場合、表現能力の高いデータ型(double)に合わせて計算される。

### 関係演算子と等価演算子
<: 小さい <=: 小さいか等しい >: 大きい
>=: 大きいか等しい

等価演算子
==: 等しい
!=: 等しくない
※[C = 10]は代入、[C == 10]は等しいなので、注意が必要

### 論理演算子
!: NOT
&&: AND
||: OR

### インクレメント・デクレメント演算子
++: ++a(先加算) or a++(後加算);
–: –a(先加算) or a–(後加算);

先加算とは、先に1を足してから代入
e.g.
a = ++b  ↓
b = b+1; a = b

後加算とは、先に代入してから加算処理
e.g.
a = b++;
a = b; b=b++;

### 代入演算子
単純代入演算子: =
複合代入演算子: +=, -=, *=, /=, %=, &=, ^=, |=, <<=, >>=
a = b = 20
a *= b + c; ↓
a = a *(b + c); と解釈される

### キャスト演算子

int idt;
double ddt = 123.456;

idt = ddt;
printf("idt=%d ddt=%f\n", idt, ddt);

doubleからintへの変換で小数点以下のデータが失われる為、warningが出ることがある。
キャスト演算子では、 idt = (int)ddt; と言うように書く
warningを消す目的だけでなく、整数部分のみ取得したい場合にも使われる

### 条件演算子(三項演算子?)
条件式 ? 式1 : 式2
e.g.
b = (a < 10) ? 200: 300; ### ビット演算子 ビット単位のデータ操作をする &: ビット毎のAND a = b & 0x7FFF |: ビット毎のOR a = b | 0x8000 ^: ビット毎のXOR a = b ^ 0x000F ~: ビットの反転 a = ~a; <<: 左シフト a = a << 2; >>: 右シフト a = a >> 2;

int main(void){
	int a = 0x00005555;
	int b = 0x000000FF;
	int c;

	printf("a=%08X\n", a);
	printf("b=%08X\n", b);
	c = a & b; printf("&=%08X\n", c);
	c = a | b; printf("|=%08X\n", c);
	c = a ^ b; printf("^=%08X\n", c);
	c = ~a; printf("~=%08X\n", c);
	return 0;
}

$ ./dev
a=00005555
b=000000FF
&=00000055
|=000055FF
^=000055AA
~=FFFFAAAA
※[%08X]は、先行0埋め、8文字幅の16進数
※負の数の右シフトはC処理系によって動作が異なることがある

### sizeof演算子
引数のサイズをバイト数で返す。

int main(void){
	int a, b[10];

	printf("%d\n", sizeof a);
	printf("%d\n", sizeof b);
	printf("%d\n", sizeof(char));
	printf("%d\n", sizeof(short int));
	printf("%d\n", sizeof(int));
	printf("%d\n", sizeof(long int));
	printf("%d\n", sizeof(float));
	printf("%d\n", sizeof(double));
	return 0;
}

$ ./dev
4
40
1
2
4
8
4
8

### 演算子の優先順位
優先順位の高い演算子が先に計算される
* / % は + – より優先処理される
()を使うと、括弧の内部を先に計算する
同じ優先順位の演算子が連続した時は、左から(あるいは右)処理を行う
a = b = 10; // この場合は、右から処理
結合規則で右からは前置(++, –)、キャスト(型名)、条件(?:)、代入(=など)。それ以外は左から処理

ビット演算子は状態管理やサブネットマスクなど多種多様に使われているとのこと。実際にビット演算子を使って作らないと、いまいち頭に入ってきませんな。