1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C言語を1ヶ月勉強したノート

Last updated at Posted at 2024-05-27

はじめに

C言語は大学1年生の時に軽く触れたことあるよ

情報の授業はいつのまにか fortran になっていましたね

C言語なんて基本構文は特殊じゃないからいまさら学ぶことあるんですかね

ぽいんたー?ってなに?

古い教材で学び直した私のノートをここに残します

教材と参考文献


◆ 第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 セキュリティ問題

scanfscanf_s
strcpystrcpy_s
strcatstrcat_s
getsgets_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を代入
char c;
char *p;

c = 'A';
p = &c;
アドレス メモリ
&c 1000 A c
p ^ ^ *p
2000 1000 p
int *p, *q, *r;   /* p, q, r はint型ポインタ変数*/
int* p, q, r;     /* ポインタ変数なのは p だけ */

◎ 配列とpointerと文字列

char st[10] = "ABC";
char *p1, *p2;

p1 = &st[0];
p2 = st;
// st = &st[0] = &st[0][0]

printf("%c", *(p1+2)); // C
++p2;
printf("%c", *p2); // B
char *p = "XYZ";
char *months_p[3] = {
    "January",
    "February",
    "March"
}
アドレス メモリ .
p 1234 X *p
1235 Y .
1236 Z .
1237 \0 .
1501 J *months_p[0]
1502 a .
1508 \0 .
1509 F *months_p[1]
5000 1234 p
5555 1501 months_p[0]
5556 1509 months_p[1]
5557 1518 months_p[2]

◆ 第11章 function

◎ 関数間のデータ授受

int main(void){
    int a = 100;
    int sum = wa(a, 299);
}
// 実引数を仮引数に渡す
int wa(int x, int y){
    return x+y;
}
int main(void){
    int a[10] = { 1, 2, 5, 6, -1 };
    int sum = goukei(a);
}
// 配列の先頭アドレスを渡す
int goukei(int *dt){
    int kei = 0;
    while(*dt != -1){
        kei += *dt;
        dt++;
    }
    return kei;
}

◎ コマンドライン引数

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[] : 引数の文字列を指すポインタの配列

compile実行方法


◆ 第12章 記憶クラス

◎ 有効範囲

  • ローカル変数
  • グローバル変数

◎ 記憶クラスの種類

  • 自動変数
    auto int num1;
    
    通常 auto は省略され、初期値は不定値
    関数が呼ばれるたびに初期化される
  • 外部変数(=グローバル変数)
    関数外で定義される変数
  • 静的変数
    static int num2;
    
    コンパイル時に一度だけ初期化される

◎ メモリ領域

Cで使うメモリ領域は4つに大別できる。

領域名 配置されるもの
プログラム領域 プログラムコード
静的領域 外部変数と静的変数
スタック領域 自動変数、関数の引数、関数の戻り値、長い計算式の一時変数など
ヒープ領域 メモリ割り当て関数などで動的に確保

◆ 第13章 データ型の修飾

shortlongint を省略している
その他修飾子も普段省略してデフォルトを使用している

表現範囲

データ型ごとの表現範囲はビット数で決まる
ビット数は処理系によって異なるが、以下が広く普及している

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 date {
    int year;
    int month;
    int day;
};
struct purchase {
    int id;
    char product_name[20];
    struct date purchase_date;
};
struct purchase {
    int id;
    char product_name[20];
    struct date {
        int year;
        int month;
        int day;
    }
};
struct purchase purchase_info = {1, "book", 2024, 4, 1};
purchase_info.date.day = 22;

◎ 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;
}
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?