はじめに
C言語は大学1年生の時に軽く触れたことあるよ
情報の授業はいつのまにか fortran になっていましたね
C言語なんて基本構文は特殊じゃないからいまさら学ぶことあるんですかね
ぽいんたー?ってなに?
古い教材で学び直した私のノートをここに残します
教材と参考文献
-
初心者のためのポイント学習C言語
-
初心者のためのポイント学習C言語 目次
-
visual studio shortcuts
-
ASCIIコード一覧表
-
printf()
-
関数library
-
pointer
-
記憶クラス1
-
記憶クラス2
-
ビット演算子1
-
ビット演算子2
-
コンパイラとプリプロセッサ
◆ 第2章データの扱い
◎ 定数
◎ データ型
型 | 型名 | バイト幅 |
---|---|---|
char | 文字 | 1 |
int | 整数 | 2 |
long | 倍精度整数 | 4 |
float | 少数 | 4 |
double | 倍精度少数 | 8 |
◎ 配列
型 配列名 要素数
int num[9];
宣言時に(型のバイト幅)×(要素数)のメモリを確保する
※文字列は文字の配列
char gender = 'M'; // シングルクォーテーション
char str[] = "Hello"; // ダブルクォーテーションマーク
◎ ASCIIコード
char
は整数を扱う文字型、つまり文字を計算できる
◎ 型変換
int sum, n;
doble average;
average = (double)sum / n;
3-3.インクリメント(++)・デクリメント(--)演算子
代入演算子(=)と共に用いると、結果が違ってきます
n = 2;
a = ++n; // a=3, n=3
b = n++; // b=3, n=4
// 後置演算の場合、計算より先に代入される
◆ 第5章
◎ printf() と scanf()
printf("こたえは %d です。\n", ans);
printf("整数を入力 > ");
scanf("%d", &input); // アドレス指定
特殊文字 | 型 | | | 特殊文字 | 型 |
---|---|---|---|---|
%c | 1文字 | | | %5d | スペース含め5文字 |
%d | 10進数 | | | %010d | 0詰め10文字 |
%x | 16進数 | | | %12f | 小数点含め12桁 |
%o | 8進数 | | | %9.2f | 小数点以下2桁 |
%f | 少数 | | | %ld | long |
%e | 指数 | | | %lf | double |
%s | 文字列 | | | %-d | 左詰め |
◎ visual studio 2019 セキュリティ問題
scanf
→ scanf_s
strcpy
→ strcpy_s
strcat
→ strcat_s
gets
→ gets_s
scanf_s
で %s
を読み込む際 &
が不要だが、
第3引数(直後の引数)に最大文字数が必要
◆ 第7章 標準ライブラリ関数
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <math.h>
◎ 文字列比較
if(strcmp(title, input_title) == 0){...}
◆ 第10章 pointer
*months_p[0]
int a, b;
int *p; // pointerの宣言(変数と同じ型)
p = &a; // a のアドレスを代入
*p = 100; // a の値に100を代入
b = *p + 1; // b の値に101を代入
|
|
int *p, *q, *r; /* p, q, r はint型ポインタ変数*/
int* p, q, r; /* ポインタ変数なのは p だけ */
◎ 配列とpointerと文字列
|
|
◆ 第11章 function
◎ 関数間のデータ授受
|
|
◎ コマンドライン引数
int main(int argc, char *argv[]) {
// 引数の個数をチェック
if (argc != 3) {
puts("引数の個数が違います");
return 0;
}
// 出力
printf("合計 = %d\n", atoi(argv[1]) + atoi(argv[2]));
return 0;
}
argc
: 引数の総個数(プログラム名も含む)
argv[]
: 引数の文字列を指すポインタの配列
◆ 第12章 記憶クラス
◎ 有効範囲
- ローカル変数
- グローバル変数
◎ 記憶クラスの種類
- 自動変数
通常
auto int num1;
auto
は省略され、初期値は不定値
関数が呼ばれるたびに初期化される - 外部変数(=グローバル変数)
関数外で定義される変数 - 静的変数
コンパイル時に一度だけ初期化される
static int num2;
◎ メモリ領域
Cで使うメモリ領域は4つに大別できる。
領域名 | 配置されるもの |
---|---|
プログラム領域 | プログラムコード |
静的領域 | 外部変数と静的変数 |
スタック領域 | 自動変数、関数の引数、関数の戻り値、長い計算式の一時変数など |
ヒープ領域 | メモリ割り当て関数などで動的に確保 |
◆ 第13章 データ型の修飾
short
や long
は int
を省略している
その他修飾子も普段省略してデフォルトを使用している
表現範囲
データ型ごとの表現範囲はビット数で決まる
ビット数は処理系によって異なるが、以下が広く普及している
data | bit |
---|---|
char | 1 |
short int | 2*8 |
int | 4*8 |
long int | 8*8 |
float | 4*8 |
double | 8*8 |
long double | 16*8 |
符号ビット
デフォルトで先頭1ビットは符号ビット
符号なしに修飾して倍の正数を表現できる
signed int num1; // =default
unsigned int num2; // 符号なし
auto signed short int // すべて省略なしで記述した場合
◆ 第14章 複雑な演算子
◎ ビット演算子
演算子 | 説明 |
---|---|
& | AND |
| | OR |
^ | XOR |
~ | 反転 |
<< | 左シフト |
>> | 右シフト |
- 算術シフト : 符号ビットを保存する
- 論理シフト : 符号ビットを考慮しない
◎ 条件演算子(三項演算子)
// 条件式?式1:式2
// 真なら式1、偽なら式2
int a,b,max;
max = (a >= b) ? a : b;
// 引数のバイト数を求める
sizeof(char); // 1
sizeof(char[20]); // 20
sizeof(int[50] / int); // 50
◆ 第15章 構造体
複数のデータをまとめて扱うには配列を用いますが、
異なる型のデータをまとめて扱いたいとき用いるのが「構造体」。
◎ structの宣言と初期化、入出力
メンバの参照は .
を用いる。
struct profile {
int no;
char name[20];
double weight;
};
int main(void){
struct profile my_info = {14105046, "TAKEDA", 59.9};
struct profile students_list[20] = {
{15095001, "AIBA", 48.7},
{16135101, "MIYAZAKI", 76.5},
};
// 入力
puts("学籍番号、名字、体重の入力");
scanf_s("%d %19s %lf",
&students_list[2].no,
students_list[2].name, sizeof(students_list[2].name)
&students_list[2].weight);
// 一括代入
student_list[4] = my_info;
// 出力
for(int i = 0; i < 4; i++) {
printf("%d %s %5.1f\n",
students_list[i].no,
students_list[i].name,
students_list[i].weight);
}
return 0;
}
◎ structの入れ子
初期値はすべてのメンバを順に並べる。参照は .
を2つ用いる。
|
|
|
◎ struct_pointer
構造体へのポインタを参照する際は、構造体名 -> メンバ名
struct score {
int id;
char name[20];
double average;
};
int main(void){
struct score student_scores = {
{1, "AOKI", 78.6},
{2, "WADA", 57.1},
};
struct score *p = student_scores;
for(int i=0; i<2; i++){
printf("%d %lf", (p+i)->id, (p+i)->average);
}
return 0;
}
入れ子の場合
p
はポインタなので ->
、 date
はポインタではないので .
printf("%d月分", p->date.month);
◎ struct_function
関数に構造体を渡す場合
構造体全体をコピーするのは重いので、ポインタを使うほうが良い
関数から構造体を受け取る場合
// typedef
struct analyzed_score data = {
int max;
int min;
double average;
};
// function
struct analyzed_score calc_data(int *p){
strict analyzed_score data;
int sum = 0, max = 0, min = 100;
int i = 0;
while(*(p+i)!=-1){
sum += *(p+i);
if(max < *(p+i)) max = *(p+i);
if(min < *(p+i)) min = *(p+i);
i++;
}
data.max = max;
data.min = min;
data.sum = (double)sum/i;
return data;
}
◎ struct_self_ref
自己参照構造体
struct list {
char name[20];
struct list *next;
}
リスト処理で用いられる
ヒープ領域の利用
- 確保 :
malloc
- 開放 :
free
// typedef
struct list {
int key;
char name[20];
struct list *next;
};
// function
/** リストにデータを登録 */
struct list * add_list(int key, char *name, struct list *head) {
// キーが重複する場合登録しない
struct list* check_p = head;
while (check_p != NULL) {
if (check_p->key == key) {
puts("キーが重複しています");
return head;
}
check_p = check_p->next;
}
struct list *p;
// 記憶領域の確保
if ((p = (struct list*)malloc(sizeof(struct list))) == NULL) {
puts("malloc error");
exit(EXIT_FAILURE);
}
// リストにデータを登録
p->key = key;
strcpy_s(p->name, name);
// ポインタの繋ぎ換え
p->next = head;
head = p;
return head;
}
/**
リストのデータを削除
リストの先頭のデータを削除する場合は head が変動する
*/
struct list * delete_list(int key, struct list *head) {
// リストが空の場合
if (head == NULL) return head;
// 探索
struct list *p = head;
struct list *pre_p = head->next; // 先頭削除の場合headを開放するために一時格納
while (p != NULL) {
if (p->key == key) break;
pre_p = p;
p = p->next;
}
// not found
if (p == NULL) {
puts("キーが見つかりませんでした");
return head;
}
// 削除
// 先頭の場合
if (p == head){
free(p);
return pre_p;
}
// それ以外の場合
pre_p->next = p->next;
free(p);
return head;
}
/** リストの表示 */
void show_list(struct list *p) {
while (p != NULL) {
printf("%3d %s\n", p->key, p->name);
p = p->next;
}
}
/** リストの解放 */
void free_list(struct list *p) {
struct list* p2;
while (p != NULL) {
p2 = p->next;
free(p);
p = p2;
}
}
// main
int main(void) {
struct list* head;
char name[20];
int key = 0;
head = NULL;
while (1) {
puts("キーと名前(MAX:19文字)を入力。(終了条件 : Ctrl+Z)");
// 終了条件
if (scanf_s("%d %s", &key, name, 19) == EOF) break;
// リストにデータを登録
head = add_list(key, name, head);
}
// リストの表示
show_list(head);
// リストの解放
free_list(head);
return 0;
}
address | memory |
---|---|
1234 | 1 |
1235 | matsu |
1236 | NULL |
3157 | 2 |
3158 | take |
3159 | 1234 |
4321 | 3 |
4322 | ume |
4323 | 3157 |
◆ 第16章 その他の型
◎ typedef
既にある型に新しい名前を宣言する。
型名が長くなるビット列や構造体などの宣言を簡潔にするためによく使われる。
#define
はプリプロセッサで解釈され、typedef
はコンパイラで解釈される。
typedef unsigned char BYTE;
BYTE data; // unsigned char data;
#define NINZU 3 // 学生の人数
typedef struct {
int no; // 学生番号
int ten[KAMOKU]; // 点数
int goukei; // 合計点
} SEISEKI; // 成績データ
int main(void){
SEISEKI mycls[NINZU] = {
{1001,85,74,63,90,0},
{1002,78,65,70,62,0},
{1003,89,92,88,76,0},
};
}
◎ union(共用体)
各メンバがすべて同じアドレスから割り振られている
宣言が struct
から union
になるだけで、使い方は構造体と全く同じ
共用体を含む構造体
ある条件のときのみデータの一部を変更したい場合、構造体を2つ作る必要がなくなる
struct kamoku1 {
int kokugo;
int eigo;
int syakai;
};
struct kamoku2 {
int suugaku;
int eigo;
};
struct seiseki {
int no;
char name[20];
int sentaku;
union kamoku {
struct kamoku1 k1;
struct kamoku2 k2;
}ka;
};
int main(void){
struct seiseki mycls[20] = {
{ 1001, "小柳幸江", 1, 76, 87, 69 },
{ 1002, "大場優", 2, 79, 48, 0 },
{ 1003, "新庄あやか", 2, 85, 98, 0 },
{ 1004, "野崎栄一", 1, 43, 76, 56 },
{ 1005, "本多健也", 1, 69, 91, 69 },
{ 9999, "" , 0, 0, 0, 0 },
}
}
◎ enum(列挙型)
int型整数値の並びに特定の名前を与える
#define SUN 0
#define MON 1
#define TUE 2
#define WED 3
#define THU 4
#define FRI 5
#define SAT 6
// 上記宣言と同じく、0から順に定数に整数値を与える
enum week { SUN, MON, TUE, WED, THU, FRI, SAT };
// 特定の値を与える場合
enum kabu { SUN=100, MON=96, TUE=90, WED=110, THU=130 };
// 列挙定数は一つ前に指定された値+1を持つ
enum number { ONE=1, TWO, THREE, FOUR };
/**
2024-04-26
演習16-3
takeda
*/
#include <stdio.h>
enum week { SUN, MON, TUE, WED, THU, FRI, SAT };
// function
/**
曜日を取得する関数(Zellerの公式)
@param y 西暦
@param m 月
@param d 日
@return 曜日(0:日, 1:月, 2:火, 3:水, 4:木, 5:金, 6:土)
*/
int youbi(int y, int m, int d) {
int w;
if (m == 1 || m == 2) {
m += 12;
y--;
}
w = (5*y/4 - y/100 + y/400 + (26*m + 16)/10 + d) % 7;
return w;
}
// main
int main(void) {
int year, month, day;
int yo;
// input
printf("西暦を入力 > ");
scanf_s("%d", &year);
printf("月を入力 > ");
scanf_s("%d", &month);
printf("日を入力 > ");
scanf_s("%d", &day);
// 曜日を取得
yo = youbi(year, month, day);
// 出力
printf("%4d年%d月%d日は", year, month, day);
switch (yo){
case SUN:
printf("日曜日です\n");
break;
case MON:
printf("月曜日です\n");
break;
case TUE:
printf("火曜日です\n");
break;
case WED:
printf("水曜日です\n");
break;
case THU:
printf("木曜日です\n");
break;
case FRI:
printf("金曜日です\n");
break;
case SAT:
printf("土曜日です\n");
break;
default:
break;
}
return 0;
}
◆ 第17章 ファイル入出力
関数 | 書式 | 説明 |
---|---|---|
fopen() セキュリティ↓ |
FILE* fopen(char *filename, char *mode); | エラー時NULL を返却 |
fopen_s() |
int fopen_s(FILE **fp, char *filename, char *mode); | 正常時0 を返却 |
fclose() |
int fclose(FILE *fp); | エラー時EOF を返却 |
fgets() |
char *fgets(char *s, int n, FILE *fp); | 1行取得しs に格納最大 n-1 文字終了時 NULL を返却 |
fputs() |
int fputs(char *s, FILE *fp); |
s を書き込み |
getc() |
int getc(FILE *fp) | 1文字取得 終了時 EOF を返却 |
putc() |
int putc(int c, FILE *fp) | 1文字書き込み |
fscanf() fscanf_s()
|
> | ファイルから書式付き入力 fpを指定する以外は scanf() と同じ |
fprintf() |
> | ファイルへ書式付き出力 fpを指定する以外は printf() と同じ |
mode | discription | ファイルがないとき |
---|---|---|
"r" | 読み込み専用 | エラー |
"w" | 書き込み専用 | 新規作成 |
"a" | 追記専用 | 新規作成 |
"r+" | 読み込み&書き込み | エラー |
"w+" | 書き込み&読み込み | 新規作成 |
"a+" | 読み込み&追記 | 新規作成 |
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
FILE* fp;
char s[256];
char c;
// file open
// if ((fp = fopen("smpl.txt", "r")) == NULL){
if ((fopen_s(&fp, "smpl.txt", "r")) != 0){
printf("file open error!!\n");
exit(EXIT_FAILURE);
}
// file read write
// 1行単位で読み込み
while (fgets(s, 256, fp) != NULL){
printf("%s", s);
}
// // 1文字単位で読み込み
// while ((c = getc(fp)) != EOF){
// printf("%c", c);
// }
printf("\n");
// file close
fclose(fp);
char* tp;
char filename[256];
char *ext_len;
// file open
if ((fopen_s(&fp, "BKCP.BAT", "w")) != 0) {
printf("file open error!!\n");
exit(EXIT_FAILURE);
}
// スペース区切りで抽出
tp = strtok(s, " ");
while (tp != NULL) {
// remove extension
ext_len = strrchr(tp, '.');
strncpy_s(filename, tp, ext_len-tp);
// file read write
fprintf(fp, "COPY %s %s.BAK\n", tp, filename);
tp = strtok(NULL, " ");
}
// file close
fclose(fp);
return 0;
}