3
6

C言語 チートシート

Last updated at Posted at 2023-05-02

c言語もよく忘れる言語なので忘れガチなところだけチートシートを作ります。

char *a[]の意味するところ

最も基本ですが、メモリのイメージがしっかりできていないとエラーが出まくったりします。

どこで使われているか?

Cの教科書なんかによくでてくる、微妙にわかったようなわからないようなmainの引数でもつかわれています

#include <stdio.h>

int main(int argc,char *argv[]) {
  printf("argc=%d \n",argc);
  for (int i=0;i<argc;i++){
      printf("argv[%d]=%s\n",i,argv[i]);
  }  
  return 0;
}

実行結果
ターミナルやコンソールで
./main a b c d
とすると
argc=5
argv[0]=./main
argv[1]=a
argv[2]=b
argv[3]=c
argv[4]=d

となります。

char *a[]={"abc","xy"} の分解

①a   まずは変数名aが中心にきます
②a[]  *と[]では[]のほうが優先度が高いので、変数aは配列です
③*a[]  配列の中身はポインタです
④char *a[] ポインタの先は文字型です

pt1.JPG

例えば配列aが100番地、文字列"abc"が1000番地、'xy'が1100番地に割り付けられているとしたら

pt2b.JPG

シンプルなプログラムで実際のメモリイメージを確認してみましょう。

#include <stdio.h>

int main(void) {
    const char *a[]={"abc","xy"} ;  
    printf("%p %p %p\n",a,a[0],&a[0]);
    printf("%p %p %p\n",a,a[1],&a[1]);
    return 0;
}

pt3.JPG

replitでの結果です。
https://replit.com/@bkh4149/memoriPei-Zhi-a
ただし実際のアドレスは実行ごとに毎回変わります

メモリ配置.png

アクセス方法

配列はポインタ(配列の先頭アドレス)なので*や**を使ってもアクセスします
先程のコードを少し変化させると

#include <stdio.h>

int main(void) {
    const char *a[]={"abc","xy"} ;  
    printf("%p %p %p\n",a,a[0],&a[0]);
    printf("%p %p %p\n",a,a[1],&a[1]);
    printf("%p\n",*a);    //←ここ追加
    printf("%x\n",**a);   //←ここ追加
    return 0;
}

こうなります。(アドレスは毎回変わります)

0x7ffd7e375710 0x402004 0x7ffd7e375710
0x7ffd7e375710 0x402008 0x7ffd7e375718
0x402004
61

ちょっとくどいかもしれませんが、以下のイメージです。メモリ配置2.png
aは配列なので、単にaとだけ書くと&a[0]のアドレスになっています。その中身が*aで値はここでは0x402004です。
そのポインタが指し示す先(**a)に0x61(アスキーコードの'a')が入っています。

const char* 型

以下のパターンも突然出てくるとドギマギしてしまいがちなのではないでしょうか?

sample.c
#include <stdio.h>
const char* getMessage() {
    return "Hello from WebAssembly!";
}
int main(void) {
    const char *c;
    c=getMessage();
    printf("%s\n", c);
    return 0;
}

以下の問題にすぐに答えられたら、スルーしてください
①なぜ char *c;ではなくて const char *c;なのか
②getMessage()は何をかえすのか

回答
①getMessage()関数の中で文字列リテラルを使っています。文字列リテラルはデータセグメントに格納され、実行時に変更することはできません。そのため、返されるポインタは const char* 型であり、これは変更不可能な文字列を指すことを意味します。
char *c ではなく const char *c を使用するのは、この不変性を尊重するためです。constを付けることで、このポインタを通じて文字列を変更することができないことを明示しています。

② getMessage() は const char* を返し、これは文字列リテラル "Hello from WebAssembly!" の先頭アドレスを指しています。const char* が変更不可能な文字へのポインタである理由は、関数が文字列リテラルを直接変更することを防ぐためです。

sample.c
#include <stdio.h> 
const char* getMessage() {
    return "Hello from WebAssembly!";//文字列の先頭アドレスを返す
}
int main(void) {
    const char *c;
    c=getMessage(); // 先頭アドレスを cに代入
    printf("%s\n", c); // 文字列を出力
    return 0;
}

フォーマット指定方法

printf()関数やscanf()関数の中で使用されます。
以下にいくつかの基本的なフォーマット指定子を示します。

整数:

%d : 符号付き10進数整数
%i : %dと同じ
%u : 符号無し10進数整数
%o : 符号無し8進数整数
%x : 符号無し16進数整数 (小文字)
%X : 符号無し16進数整数 (大文字)

浮動小数点数:

%f : 小数点形式
%e : 指数形式 (小文字)
%E : 指数形式 (大文字)
%g : %fと%eの中から短い方
%G : %fと%Eの中から短い方

文字と文字列:

%c : 単一文字
%s : 文字列

ポインタ:

%p : ポインタ

これらのフォーマット指定子の前には幅、精度、フラグを指定することができます。たとえば、%08dは8桁の10進数を0でパディングします。
また、printf()関数では可変引数を使って複数の変数を出力することができます。以下に一例を示します。

int a = 5;
float b = 3.14f;
printf("整数: %d, 浮動小数点数: %.2f\n", a, b);  // 整数: 5, 浮動小数点数: 3.14

以上がC言語の基本的なフォーマット指定方法になります。他にも細かいオプションがありますので、必要に応じてマニュアルやドキュメンテーションを参照してください。

フォーマット指定自体は文字列なのでこんな書き方もできます


#include <stdio.h>

int main(void) {
    char a[]="%d\n";	//文字列配列
    int b=3;
  printf(a,b);			//ここに注目!
  return 0;
}

型について

型は値の範囲とメモリ使用量を決定します。

整数型:

bit 概要 符号
char キャラクタを格納 符号付きまたは符号なし
int 32 整数 符号付き
short 16 intよりも小さい整数 符号付き
long 32/64 intよりも大きな整数 符号付き
long long 64 非常に大きな整数 符号付き

これらの型は、"unsigned"キーワードを前置することで、符号無し(つまり、非負)のバリエーションを作成できます。

浮動小数点型:

float: 単精度浮動小数点数を格納します。通常、32ビットで6-7桁の精度です。
double: 倍精度浮動小数点数を格納します。通常、64ビットで15-16桁の精度です。
long double: 拡張精度浮動小数点数を格納します。大抵のシステムでは80ビット以上です。

その他の型:

void: 値を持たず、戻り値または引数のない関数の型指定に使われます。
_Bool: 論理値(真または偽)を格納します。

ポインタ型:

メモリアドレスを格納します。
すべてのポインタ型は同じサイズですが、指しているデータの型は異なります。
また、C言語ではこれらの基本型を組み合わせて構造体(struct)、共用体(union)、列挙型(enum)などの複合データ型を定義することもできます。

C言語のプログラムエリア

主なメモリ領域は4つ

コード領域
データ領域
ヒープ領域
スタック領域

テキスト領域(またはコード領域):

実行コード(コンパイルされたマシンコード)が保存されます。読み取り専用。

データ領域:初期化済み

グローバル変数や静的変数で、初期値が設定されているものがここに保存されます。

データ領域: BSS:

初期値が設定されていないグローバル変数や静的変数がここに保存されます。これらの変数はデフォルトで0に初期化されます。

ヒープ領域:

動的に確保されるメモリ(malloc、calloc、reallocなどで確保)。ヒープはメモリの低位アドレスから高位アドレスへ向かって成長します。

スタック領域:

関数の呼び出しやローカル変数の格納に使用されるメモリ領域です。スタックは高位アドレスから低位アドレスへ向かって成長します。

strの7つ道具 string.h

IMG_0010.JPG

関数とポインタ

IMG_0009.JPG

ポインタと配列

IMG_0008.JPG

コンパイルのオプション

IMG_0007.JPG

fgetsとscanf

IMG_0006.JPG

Makeの基本

IMG_0005.JPG

スタックとキューの実現方法

超シンプル版ゼロから考えた原人的スタック

苦手意識高い系な人間が、書いたコードです

#include <stdio.h>

struct Stack{
   int top;
    int stk[30];//スタックは30個分
};

void showStk(struct Stack S){
    int top=S.top;
    printf("@showStk top=%d   \n",top);
    for (int i=top-1;i>=0;i--){
        printf("i=%d  stack=%d\n",i,S.stk[i]);
    }
}    

int main(void) {
    int pop;
    struct Stack S1;//push
    S1.top=0;
    showStk(S1);

    S1.stk[S1.top]=11;//push
    S1.top +=1;
    showStk(S1);
    
    S1.stk[S1.top]=22;//push
    S1.top +=1;
    showStk(S1);
    
    S1.stk[S1.top]=33;//push
    S1.top +=1;
    showStk(S1);

    S1.top -=1;      //pop
    pop = S1.stk[S1.top];
    showStk(S1);

    S1.stk[S1.top]=44;//push
    S1.top +=1;
    showStk(S1);

    S1.top -=1;      //pop
    pop = S1.stk[S1.top];
    showStk(S1);

    printf("end\n");
  return 0;
}

chatGPTによる修正版

上をchatGPTにコードレビューしてもらったところ、大量の修正点をあげていただきました。pushやpopを関数化して、スタックはポインタ渡しに変更してくれました。

#include <stdio.h>

#define MAX_SIZE 30  // Define stack size as a constant

struct Stack{
   int top;
   int stk[MAX_SIZE];
};

void push(struct Stack *S, int value){
    if(S->top == MAX_SIZE){
        printf("Stack overflow\n");
        return;
    }
    S->stk[S->top] = value;
    S->top += 1;
}

int pop(struct Stack *S){
    if(S->top == 0){
        printf("Stack underflow\n");
        return -1;  // return -1 or another specific value to indicate underflow
    }
    S->top -= 1;
    return S->stk[S->top];
}

void showStk(struct Stack S){
    int top = S.top;
    printf("@showStk top=%d\n", top);
    for (int i = top-1; i >= 0; i--){
        printf("i=%d  stack=%d\n", i, S.stk[i]);
    }
}

int main(void) {
    int popValue;
    struct Stack S1;
    S1.top = 0;

    push(&S1, 11);
    showStk(S1);

    push(&S1, 22);
    showStk(S1);

    push(&S1, 33);
    showStk(S1);

    popValue = pop(&S1);
    showStk(S1);

    push(&S1, 44);
    showStk(S1);

    popValue = pop(&S1);
    showStk(S1);

    printf("end\n");
    return 0;
}

さすがきれい、まあでもスタックの仕組みは捉えたきがします。

キューの実現 リングバッファ使用 chatGPT版

#include <stdio.h>

#define MAX_SIZE 5  // Define queue size as a constant

struct CircularQueue {
    int items[MAX_SIZE];
    int front, rear;
};

// Initializing the queue
void initializeQueue(struct CircularQueue *q) {
    q->front = q->rear = -1;
}

// Check if the queue is full
int isFull(struct CircularQueue *q) {
    if ((q->rear + 1) % MAX_SIZE == q->front) {
        return 1;
    }
    return 0;
}

// Check if the queue is empty
int isEmpty(struct CircularQueue *q) {
    if (q->front == -1) {
        return 1;
    }
    return 0;
}

// Adding elements to the queue
void enqueue(struct CircularQueue *q, int element) {
    if (isFull(q)) {
        printf("Queue is full!\n");
        return;
    }

    if (isEmpty(q)) {
        q->front = q->rear = 0;
    } else {
        q->rear = (q->rear + 1) % MAX_SIZE;
    }

    q->items[q->rear] = element;
}

// Removing elements from the queue
int dequeue(struct CircularQueue *q) {
    int data;
    if (isEmpty(q)) {
        printf("Queue is empty!\n");
        return -1;
    }

    data = q->items[q->front];
    if (q->front == q->rear) {
        q->front = q->rear = -1;
    } else {
        q->front = (q->front + 1) % MAX_SIZE;
    }

    return data;
}

// Function to display the elements of queue
void display(struct CircularQueue *q) {
    int i = q->front;
    while (i != q->rear) {
        printf("%d ", q->items[i]);
        i = (i + 1) % MAX_SIZE;
    }
    printf("%d\n", q->items[i]);
}

int main() {
    struct CircularQueue q;
    initializeQueue(&q);

    enqueue(&q, 1);
    enqueue(&q, 2);
    enqueue(&q, 3);
    enqueue(&q, 4);
    display(&q);

    dequeue(&q);
    display(&q);

    enqueue(&q, 5);
    display(&q);

    dequeue(&q);
    display(&q);

    dequeue(&q);
    display(&q);

    dequeue(&q);
    display(&q);

    enqueue(&q, 6);
    display(&q);

    enqueue(&q, 7);
    display(&q);

    enqueue(&q, 8);
    display(&q);

    enqueue(&q, 9);
    display(&q);

    enqueue(&q, 10);
    display(&q);

   return 0;
}

実行結果は以下の通り、これもきれいなコードですなー
1 2 3 4
2 3 4
2 3 4 5
3 4 5
4 5
5
5 6
5 6 7
5 6 7 8
5 6 7 8 9
Queue is full!
5 6 7 8 9

3
6
2

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
3
6