[C言語] ファイルアクセス

ファイル記述子を使用する関数には、open, close, read, writeがある。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>

void usage(char *prog_name, char *filename){
    printf("使用方法: %s <%sに追加するデータ>\n", prog_name, filename);
    exit(0);
}

void fatal(char *);
void *ec_malloc(unsigned int);

int main(int argc, char *argv[]) {
    int fd;
    char *buffer, *datafile;

    buffer = (char *) ec_malloc(100);
    datafile = (char *) ec_malloc(20);
    strcpy(datafile, "./notes");

    if (argc < 2)
        usage(argv[0], datafile);

    strcpy(buffer, argv[1]);

    printf("[DEBUG] buffer @ %p: \'%s'\n", buffer, buffer);
    printf("[DEBUG] datafile @ %p: \'%s'\n", datafile, datafile);

    strncat(buffer, "\n", 1);

    fd = open(datafile, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR);
    if(fd == -1)
        fatal("main()内、ファイルのオープン中にエラーが発生しました。");
    printf("[DEBUG] ファイル記述子:%d\n", fd);

    if(write(fd, buffer, strlen(buffer)) == -1)
        fatal("main()内、ファイルへのバッファの書き込みでエラーが発生しました。");

    if(close(fd) == -1)
        fatal("main()内、ファイルのクローズ中にエラーが発生しました。");

    printf("メモが保存されました\n");
    free(buffer);
    free(datafile);

    return 0;
}

void fatal(char *message) {
    char error_message[100];

    strcpy(error_message, "[!!]致命的なエラー:");
    strncat(error_message, message, 79);
    perror(error_message);
    exit(-1);
}

void *ec_malloc(unsigned int size) {
    void *ptr;
    ptr = malloc(size);
    if(ptr == NULL)
        fatal("ec_malloc()内のメモリ割り当てでエラーが発生しました。");
    return ptr;
}

$ ./main
使用方法: ./main <./notesに追加するデータ>
$ ./main “this is a test note”
[DEBUG] buffer @ 0xaaaadc4a72a0: ‘this is a test note’
[DEBUG] datafile @ 0xaaaadc4a7310: ‘./notes’
[DEBUG] ファイル記述子:3
メモが保存されました

### ビット単位の演算

int main() {
    int i, bit_a, bit_b;
    printf("ビット単位の論理和演算子 |\n");
    for(i=0; i< 4; i++){
        bit_a = (i & 2) / 2;
        bit_b = (i & 1);
        printf("%d | %d = %d\n", bit_a, bit_b, bit_a | bit_b);
    }

    printf("ビット単位の論理積演算子 |\n");
    for(i=0; i< 4; i++){
        bit_a = (i & 2) / 2;
        bit_b = (i & 1);
        printf("%d | %d = %d\n", bit_a, bit_b, bit_a & bit_b);
    }

    return 0;
}

$ ./main
ビット単位の論理和演算子 |
0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1
ビット単位の論理積演算子 |
0 | 0 = 0
0 | 1 = 0
1 | 0 = 0
1 | 1 = 1

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>

void display_flags(char *, unsigned int);
void binary_print(unsigned int);

int main(int argc, char *argv[]) {
    display_flags("O_RDONLY\t\t", O_RDONLY);
    display_flags("O_WRONLY\t\t", O_WRONLY);
    display_flags("O_RDWR\t\t", O_RDWR);
    printf("\n");
    display_flags("O_APPEND\t\t", O_APPEND);
    display_flags("O_TRUNC\t\t", O_TRUNC);
    display_flags("O_CREAT\t\t", O_CREAT);

    printf("\n");
    display_flags("O_WRONLY|O_APPEND|O_CREAT", O_WRONLY|O_APPEND|O_CREAT);

    return 0;
}

void display_flags(char *label, unsigned int value){
    printf("%s\t %d\t:", label, value);;
    binary_print(value);
    printf("\n");
}

void binary_print(unsigned int value) {
    unsigned int mask = 0xff000000;
    unsigned int shift = 256*256*256;
    unsigned int byte, byte_iterator, bit_iterator;

    for(byte_iterator=0; byte_iterator < 4; byte_iterator++){
        byte = (value & mask) / shift;
        printf(" ");
        for(bit_iterator=0; bit_iterator < 8; bit_iterator++){
            if(byte & 0x80)
                printf("1");
            else
                printf("0");
            byte *= 2;
        }
        mask /= 256;
        shift /= 256;
    }
}

$ ./main
O_RDONLY 0 : 00000000 00000000 00000000 00000000
O_WRONLY 1 : 00000000 00000000 00000000 00000001
O_RDWR 2 : 00000000 00000000 00000000 00000010

O_APPEND 1024 : 00000000 00000000 00000100 00000000
O_TRUNC 512 : 00000000 00000000 00000010 00000000
O_CREAT 64 : 00000000 00000000 00000000 01000000

O_WRONLY|O_APPEND|O_CREAT 1089 : 00000000 00000000 00000100 01000001

[C言語] メモリのヒープの使用

ヒープセグメントに割り当てるには、malloc関数を使用する

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

int main(int argc, char *argv[]) {
    char *char_ptr;
    int *int_ptr;
    int mem_size;

    if(argc < 2)
        mem_size = 50;
    else
        mem_size = atoi(argv[1]);

    printf("\t[+]ヒープから%dバイトを割り当て、先頭アドレスをchar_ptrに代入します。\n", mem_size);
    char_ptr = (char *) malloc(mem_size);

    if(char_ptr == NULL){
        fprintf(stderr, "エラー:ヒープメモリの割り当てに失敗しました。\n");
        exit(-1);
    }

    strcpy(char_ptr, "KOREHA HEAP NI COPY SAREMASU");
    printf("char_ptr (%p) --> '%s'\n", char_ptr, char_ptr);

    printf("\t[+]ヒープから12バイトを割り当て、先頭アドレスをint_ptrに代入します。\n");
    int_ptr = (int *) malloc(12);

    if(int_ptr == NULL){
        fprintf(stderr, "エラー:ヒープメモリの割り当てに失敗しました。\n");
        exit(-1);
    }

    *int_ptr = 31337;
    printf("int_ptr (%p) --> '%d'\n", int_ptr, *int_ptr);

    printf("\t[-]char_ptrが指しているヒープメモリを解放します。\n");
    free(char_ptr);

    printf("\t[+]ヒープから再び15バイトを割り当て、先頭アドレスをchar_ptrに代入します。\n");
    char_ptr = (char *)malloc(15);

    if(char_ptr == NULL) {
        fprintf(stderr, "エラー:ヒープメモリの割り当てに失敗しました。\n");
        exit(-1);
    }

    strcpy(char_ptr, "NEW MEMORY");
    printf("char_ptr(%p) --> '%s'\n", char_ptr, char_ptr);

    printf("\t[-]int_ptrのヒープメモリを解放します。\n");
    free(int_ptr);

    printf("\t[-]char_ptrが指しているヒープメモリを解放します。\n");
    free(char_ptr);

    return 0;
}

$ ./a.out
[+]ヒープから50バイトを割り当て、先頭アドレスをchar_ptrに代入します。
char_ptr (0xaaaac973c6b0) –> ‘KOREHA HEAP NI COPY SAREMASU’
[+]ヒープから12バイトを割り当て、先頭アドレスをint_ptrに代入します。
int_ptr (0xaaaac973c6f0) –> ‘31337’
[-]char_ptrが指しているヒープメモリを解放します。
[+]ヒープから再び15バイトを割り当て、先頭アドレスをchar_ptrに代入します。
char_ptr(0xaaaac973c710) –> ‘NEW MEMORY’
[-]int_ptrのヒープメモリを解放します。
[-]char_ptrが指しているヒープメモリを解放します。

割り当ての値を変えてテストを行う

$ ./a.out 100
[+]ヒープから100バイトを割り当て、先頭アドレスをchar_ptrに代入します。
char_ptr (0xaaaafbe3c6b0) –> ‘KOREHA HEAP NI COPY SAREMASU’
[+]ヒープから12バイトを割り当て、先頭アドレスをint_ptrに代入します。
int_ptr (0xaaaafbe3c720) –> ‘31337’
[-]char_ptrが指しているヒープメモリを解放します。
[+]ヒープから再び15バイトを割り当て、先頭アドレスをchar_ptrに代入します。
char_ptr(0xaaaafbe3c740) –> ‘NEW MEMORY’
[-]int_ptrのヒープメモリを解放します。
[-]char_ptrが指しているヒープメモリを解放します。

### エラー判定を関数化してしまう

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

void *errorchecked_malloc(unsigned int); 

int main(int argc, char *argv[]) {
    char *char_ptr;
    int *int_ptr;
    int mem_size;

    if(argc < 2)
        mem_size = 50;
    else
        mem_size = atoi(argv[1]);

    printf("\t[+]ヒープから%dバイトを割り当て、先頭アドレスをchar_ptrに代入します。\n", mem_size);
    char_ptr = (char *) errorchecked_malloc(mem_size);

    strcpy(char_ptr, "KOREHA HEAP NI COPY SAREMASU");
    printf("char_ptr (%p) --> '%s'\n", char_ptr, char_ptr);

    printf("\t[+]ヒープから12バイトを割り当て、先頭アドレスをint_ptrに代入します。\n");
    int_ptr = (int *) errorchecked_malloc(12);

    *int_ptr = 31337;
    printf("int_ptr (%p) --> '%d'\n", int_ptr, *int_ptr);

    printf("\t[-]char_ptrが指しているヒープメモリを解放します。\n");
    free(char_ptr);

    printf("\t[+]ヒープから再び15バイトを割り当て、先頭アドレスをchar_ptrに代入します。\n");
    char_ptr = (char *) errorchecked_malloc(15);

    strcpy(char_ptr, "NEW MEMORY");
    printf("char_ptr(%p) --> '%s'\n", char_ptr, char_ptr);

    printf("\t[-]int_ptrのヒープメモリを解放します。\n");
    free(int_ptr);

    printf("\t[-]char_ptrが指しているヒープメモリを解放します。\n");
    free(char_ptr);

    return 0;
}

void *errorchecked_malloc(unsigned int size){
    void *ptr;
    ptr = malloc(size);
    if(ptr == NULL) {
        fprintf(stderr, "エラー: ヒープメモリの割り当てに失敗しました。\n");
        exit(-1);
    }
    return ptr;
}

[C言語] メモリのスタック領域

void test_function(int a, int b, int c, int d){
    int flag;
    char buffer[10];

    flag = 31337;
    buffer[0] = 'A';
}

int main() {
    test_function(1, 2, 3, 4);
    return 0;
}

$ gcc -g main.c
$ gdb -q ./a.out
Reading symbols from ./a.out…
(gdb) disass main
Dump of assembler code for function main:
0x000000000000087c <+0>: stp x29, x30, [sp, #-16]!
0x0000000000000880 <+4>: mov x29, sp
0x0000000000000884 <+8>: mov w3, #0x4 // #4
0x0000000000000888 <+12>: mov w2, #0x3 // #3
0x000000000000088c <+16>: mov w1, #0x2 // #2
0x0000000000000890 <+20>: mov w0, #0x1 // #1
0x0000000000000894 <+24>: bl 0x814
0x0000000000000898 <+28>: mov w0, #0x0 // #0
0x000000000000089c <+32>: ldp x29, x30, [sp], #16
0x00000000000008a0 <+36>: ret
End of assembler dump.

(gdb) disass test_function
Dump of assembler code for function test_function:
0x0000000000000814 <+0>: stp x29, x30, [sp, #-64]!
0x0000000000000818 <+4>: mov x29, sp
0x000000000000081c <+8>: str w0, [sp, #28]
0x0000000000000820 <+12>: str w1, [sp, #24]
0x0000000000000824 <+16>: str w2, [sp, #20]
0x0000000000000828 <+20>: str w3, [sp, #16]
0x000000000000082c <+24>: adrp x0, 0x10000
0x0000000000000830 <+28>: ldr x0, [x0, #4072]
0x0000000000000834 <+32>: ldr x1, [x0]
0x0000000000000838 <+36>: str x1, [sp, #56]
0x000000000000083c <+40>: mov x1, #0x0 // #0
0x0000000000000840 <+44>: mov w0, #0x7a69 // #31337
0x0000000000000844 <+48>: str w0, [sp, #36]
–Type for more, q to quit, c to continue without paging–
0x0000000000000848 <+52>: mov w0, #0x41 // #65
0x000000000000084c <+56>: strb w0, [sp, #40]
0x0000000000000850 <+60>: nop
0x0000000000000854 <+64>: adrp x0, 0x10000
0x0000000000000858 <+68>: ldr x0, [x0, #4072]
0x000000000000085c <+72>: ldr x2, [sp, #56]
0x0000000000000860 <+76>: ldr x1, [x0]
0x0000000000000864 <+80>: subs x2, x2, x1
0x0000000000000868 <+84>: mov x1, #0x0 // #0
0x000000000000086c <+88>: b.eq 0x874 // b.none
0x0000000000000870 <+92>: bl 0x6a0 <__stack_chk_fail@plt>
0x0000000000000874 <+96>: ldp x29, x30, [sp], #64
0x0000000000000878 <+100>: ret
End of assembler dump.

4, 3, 2, 1はスタックフレームにpushされる

(gdb) list main
6
7 flag = 31337;
8 buffer[0] = ‘A’;
9 }
10
11 int main() {
12 test_function(1, 2, 3, 4);
13 return 0;
14 }
(gdb) break 12
Breakpoint 1 at 0x884: file main.c, line 12.
(gdb) break test_function
Breakpoint 2 at 0x82c: file main.c, line 3.
(gdb) run
Starting program: /home/vagrant/dev/c/a.out
[Thread debugging using libthread_db enabled]
Using host libthread_db library “/lib/aarch64-linux-gnu/libthread_db.so.1”.

Breakpoint 1, main () at main.c:12
12 test_function(1, 2, 3, 4);

メモリ中の各セグメントは、低位アドレスから高位アドレスへ向かって並べられていく

– 低位アドレス
テキスト(コード)セグメント
データセグメント
bssセグメント
ヒープセグメント
スタックセグメント
– 高位アドレス

### メモリセグメント

int global_var;
int global_initialized_var = 5;

void function() {
    int stack_var;

    printf("function()のstack_varは、アドレス0x%08xにあります。\n", &stack_var);
}

int main() {
    int stack_var;
    static int static_initialized_var = 5;
    static int static_var;
    int *heap_var_ptr;

    heap_var_ptr = (int *)malloc(4);

    // データセグメントに確保される
    printf("global_initialized_varはアドレス0x%08xにあります。\n", &global_initialized_var);
    printf("static_initialized_varはアドレス0x%08xにあります。\n", &static_initialized_var);

    // bssセグメントに確保される
    printf("static_varはアドレス0x%08xにあります。\n", &static_var);
    printf("global_varはアドレス0x%08xにあります。\n", &global_var);

    // ポインタ変数はヒープセグメント内に確保される
    printf("heap_var_ptrはアドレス0x%08xにあります。\n", heap_var_ptr);

    // 以下の変数はスタックセグメント内に確保される
    printf("stack_varはアドレス0x%08xにあります。\n", &stack_var);

    return 0;
}

$ ./a.out
global_initialized_varはアドレス0xc5b21010にあります。
static_initialized_varはアドレス0xc5b21014にあります。
static_varはアドレス0xc5b21020にあります。
global_varはアドレス0xc5b2101cにあります。
heap_var_ptrはアドレス0xefedb2a0にあります。
stack_varはアドレス0xecf4957cにあります。

[c言語] デバッカツールgdbの活用

$ sudo apt-get update
$ sudo apt-get install gdb
$ gdb -v
GNU gdb (Ubuntu 12.1-0ubuntu1~22.04) 12.1
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

#include <stdio.h>

int main() {
    int i;
    for(i=0; i< 10; i++){
        printf("Hello world\n");
    }
    return 0;
}

$ gcc main.c
$ ls -l a.out
-rwxrwxr-x 1 vagrant vagrant 8872 Feb 11 18:48 a.out

$ objdump -D a.out | grep -A main.:
grep: main.:: invalid context length argument
vagrant@vagrant:~/dev/c$ objdump -D a.out | grep -A20 main.:
0000000000000754

:
754: a9be7bfd stp x29, x30, [sp, #-32]!
758: 910003fd mov x29, sp
75c: b9001fff str wzr, [sp, #28]
760: 14000007 b 77c
764: 90000000 adrp x0, 0 <__abi_tag-0x278>
768: 911ec000 add x0, x0, #0x7b0
76c: 97ffffb1 bl 630 770: b9401fe0 ldr w0, [sp, #28]
774: 11000400 add w0, w0, #0x1
778: b9001fe0 str w0, [sp, #28]
77c: b9401fe0 ldr w0, [sp, #28]
780: 7100241f cmp w0, #0x9
784: 54ffff0d b.le 764
788: 52800000 mov w0, #0x0 // #0
78c: a8c27bfd ldp x29, x30, [sp], #32
790: d65f03c0 ret

Disassembly of section .fini:

0000000000000794 <_fini>:

$ gdb -q ./a.out
Reading symbols from ./a.out…
(No debugging symbols found in ./a.out)
(gdb) break main
Breakpoint 1 at 0x760
(gdb) run
Starting program: /home/vagrant/dev/c/a.out
[Thread debugging using libthread_db enabled]
Using host libthread_db library “/lib/aarch64-linux-gnu/libthread_db.so.1”.

Breakpoint 1, 0x0000aaaaaaaa0760 in main ()
(gdb) info registers
x0 0x1 1
x1 0xfffffffff138 281474976706872
x2 0xfffffffff148 281474976706888
x3 0xaaaaaaaa0754 187649984431956
x4 0x0 0
x5 0x840f359fc93a2758 -8930860575660300456
x6 0xfffff7f9cc90 281474842086544
x7 0x4554415649 297766311497
x8 0xd7 215
x9 0x10 16
x10 0xfffff7fc2490 281474842240144
x11 0x0 0
x12 0x0 0
x13 0x0 0
–Type for more, q to quit, c to continue without paging–
x14 0xfffff7fff000 281474842488832
x15 0xfffff7ff8e60 281474842463840
x16 0xfffff7fd6774 281474842322804
x17 0xfffff7f9b080 281474842079360
x18 0x2 2
x19 0xfffffffff138 281474976706872
x20 0x1 1
x21 0xaaaaaaab0d98 187649984499096
x22 0xfffff7ffe040 281474842484800
x23 0xaaaaaaaa0754 187649984431956
x24 0xfffff7f9a000 281474842075136
x25 0x0 0
x26 0xfffffffff148 281474976706888
x27 0xaaaaaaab0d98 187649984499096
–Type for more, q to quit, c to continue without paging–
x28 0x0 0
x29 0xffffffffefa0 281474976706464
x30 0xfffff7e273fc 281474840556540
sp 0xffffffffefa0 0xffffffffefa0
pc 0xaaaaaaaa0760 0xaaaaaaaa0760
cpsr 0x80001000 [ EL=0 BTYPE=0 SSBS N ]
fpsr 0x0 [ ]
fpcr 0x0 [ RMode=0 ]
pauth_dmask 0x7f000000000000 35747322042253312
pauth_cmask 0x7f000000000000 35747322042253312
(gdb) quit
A debugging session is active.

Inferior 1 [process 1266503] will be killed.

Quit anyway? (y or n) y

gdbを使うと別の世界に来た感じがしますね。

[C言語] const修飾子

int main( void )
{
	char s1[] = "abc";
    char s2[] = "xyz";
    char *s3 = "ABC";

    char *p = s1;
    printf("[1]p=%s\n", p);

    p = s2;
    printf("[2]p=%s\n", p);

    *p = '1';
    printf("[3]p=%s\n", p);

    p = s3;
    printf("[4]p=%s\n", p);

	return 0;
}

$ gcc main.c -o main -lm && ./main
[1]p=abc
[2]p=xyz
[3]p=1yz
[4]p=ABC

*p=1 とすると、1yzとなるのか…

### const char*

int main( void )
{
	const char s1[] = "abc";
    const char s2[] = "xyz";

    const char *p = s1;
    printf("[1]p=%s\n", p);

    p = s2;
    printf("[2]p=%s\n", p);

	return 0;
}

$ gcc main.c -o main -lm && ./main
[1]p=abc
[2]p=xyz

int main( void )
{
	char s1[] = "abc";
    char s2[] = "xyz";

    char *const p = s1;
    printf("[1]p=%s\n", p);

    *p = '1';
    printf("[2]p=%s\n", p);

	return 0;
}

$ gcc main.c -o main -lm && ./main
[1]p=abc
[2]p=1bc

[C言語] ポインタの派生型 本格入門

### 2次元のポインタ配列

int main( void )
{
	int *k[2][3];
    int t0=0, t1=1, t2=2, t3=3, t4=4, t5=5;
    int i,j;

    k[0][0] = &t0;
    k[0][1] = &t1;
    k[0][2] = &t2;
    k[1][0] = &t3;
    k[1][1] = &t4;
    k[1][2] = &t5;

    for(i=0; i<2; i++) {
        for(j=0; j<3; j++){
            int m = *k[i][j];
            printf("%d ", m);
        }
        printf("\n");
    }


	return 0;
}

$ gcc main.c -o main -lm && ./main
0 1 2
3 4 5
入れ子のfor文は基本

### ポインタ配列を指すポインタ

int main( void )
{
	int t1=1, t2=2, t3=3;
    int *a[3] = {&t1, &t2, &t3};
    int *(*k)[3] = &a;
    int j1, j2, j3;

    j1 = *(*k)[0];
    j2 = *(*k)[1];
    j3 = *(*k)[2];

    printf("j1=%d, j2=%d, j3=%d", j1, j2, j3);

	return 0;
}

$ gcc main.c -o main -lm && ./main
j1=1, j2=2, j3=3
ポインタのポインタと同じように配列もポインタ配列のポインタができる

int main( void )
{
	int a[2][3] = {{1,2,3},{4,5,6}};
    int (*k)[2][3] = &a;
    int i, j;

    for(i=0; i<2; i++){
        for(j=0; j<3; j++){
            int m = (*k)[i][j];
            printf("%d ", m);
        }
        printf("\n");
    }

	return 0;
}

### 関数を指すポインタを返す関数

double f1(char c){
    return c + 1.0;
}

double f2(char c){
    return c + 2.0;
}

double(*f(int flag))(char){
    if(flag == 0)
        return f1;
    else
        return f2;
}

int main( void )
{
	double (*p)(char); // 関数を指すポインタ
    char c = (char)0;
    double x;

    p = f(0);
    x = p(c);
    printf("[1]x = %g\n", x);

    p = f(1);
    x = p(c);
    printf("[2]x = %g\n", x);

	return 0;
}

### 配列を指すポインタを返す関数

int (*a(double x))[2] {
    static int t[2] = {10, 20};

    t[0] += x;
    t[1] += x;
}

int main( void )
{
	int (*q)[2] = a(2.0);
    int q1 = (*q)[0];
    int q2 = (*q)[1];

    printf("q1=%d, q2=%d\n", q1, q2);

	return 0;
}

これはそのまま

int *(*f(double x, int y))[2] {
    static int t1 = 10, t2 = 20;
    static int *t[2] = {&t1, &t2};

    t1 += x;
    t2 += y;

    return &t;
}

int main( void )
{
	int *(*q)[2] = f(2.0, 5);
    int q1 = *(*q)[0];
    int q2 = *(*q)[1];

    printf("q1=%d, q2=%d\n", q1, q2);

	return 0;
}

$ gcc main.c -o main -lm && ./main
q1=12, q2=25
これもそのままです。

### 不完全型の配列を指すポインタ

int main( void )
{
	int a[3] = {1, 2, 3};
    int (*p)[] = (int (*)[])a;

    int a1 = (*p)[0], a2 = (*p)[1], a3 = (*p)[2];

    printf("a1=%d, a2=%d, q3=%d\n", a1, a2, a3);

	return 0;
}

[C言語] 関数を指すポインタ [その2]

コールバック関数

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

double callback(double x){
    return x+1.0;
}

double g(double x, double (*f)(double)){
    return f(x);
}

int main(void) {
    
    double ans = g(5.0, callback); 
    printf("ans = %g\n", ans);

    ans = g(5.0, sqrt);
    printf("ans = %g\n", ans);

    return 0;
}

$ gcc main.c -o main -lm && ./main
ans = 6
ans = 2.23607

関数を呼び出す際に、引数はポインタで指定する

double callback(double x, double y){
    return x+y;
}

double g(double x, double (*f)(double,double)){
    return f(x, x);
}

int main(void) {
    
    double ans = g(5.0, callback); 
    printf("ans = %g\n", ans);

    ans = g(5.0, pow);
    printf("ans = %g\n", ans);

    return 0;
}

$ gcc main.c -o main -lm && ./main
ans = 10
ans = 3125
不思議な感じがしてくる

### シグナル処理関数
シグナルとは. プロセスやプロセスグループへ様々なイベントを通知するためにあるカーネルの機能 (ソフトウェア割り込み)。

#include<stdio.h>
#include<signal.h>

void printMessage(int sigNo){
    printf("割り込み発生:シグナル=%d\n", sigNo);
}

int main(void) {
    
    int x[1];
    int y;

    if(signal(SIGSEGV, printMessage) != SIG_ERR)
        fprintf(stderr, "シグナル設定成功\n");
    else
        fprintf(stderr, "シグナル設定失敗\n");
    y = x[10000];

    printf("プログラム終了: これは実行されません\n");

    return 0;
}

なんだこれは…

### プログラム終了時の実行関数登録

void pr1(void) {
    printf("pr1 called\n");
}
void pr2(void) {
    printf("pr2 called\n");
}
void pr3(void) {
    printf("pr3 called\n");
}

int main(void) {
    
    if(!atexit(pr1)) printf("pr1 OK\n"); else printf("pr1 No\n");
    if(!atexit(pr2)) printf("pr2 OK\n"); else printf("pr2 No\n");
    if(!atexit(pr3)) printf("pr3 OK\n"); else printf("pr3 No\n");

    return 0;
}

$ gcc main.c -o main -lm && ./main
pr1 OK
pr2 OK
pr3 OK
pr3 called
pr2 called
pr1 called
ん?

### バイナリサーチ

int compare(const int *n1, const int *n2){
    printf("%d %d\n", *n1, *n2);
    return *n1 - *n2;
}

int main(void) {
    
    int data[] = {-2, 0, 1, 3, 5, 6, 9, 10, 11, 12, 100,};
    int num = sizeof data / sizeof data[0];

    int target = 0;
    int *t;

    if((t = (int *)bsearch(&target, data, num, sizeof(data[0]), (int(*)(const void*, const void*))compare))!= NULL)
        printf("%dがありました。\n", *t);
    else
        printf("%dがありませんでした。\n", target);
    return 0;
}

$ gcc main.c -o main -lm && ./main
0 6
0 1
0 0
0がありました。
どういう流れかよくわからん…

### 文字列のバイナリサーチ

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

int compare(const char *name1, const char **name2){
    printf("%s %s\n", name1, *name2);
    return strcmp(name1, *name2);
}

int main(void) {
    
    char *names[] = {"atexit", "atol", "bsearch", "calloc", "malloc", "qsort","rand", "system", "wcstombs"};
    int num = sizeof names / sizeof names[0];

    char *name = "atol"; //検索する文字列
    char **s; // ポインタのポインタ

    if((s = bsearch(name, names, num, sizeof names[0], (int(*)(const void*, const void*))compare))!= NULL)
        printf("%sがありました。\n", *s);
    else
        printf("%sがありませんでした。\n", name);
    return 0;
}

$ gcc main.c -o main -lm && ./main
atol malloc
atol bsearch
atol atol
atolがありました。

strcmpでcompareしているのはわかるが、なぜ連続になるのだろうか…

### クイックソート

int compare(const int *n1, const int *n2){
    return *n1 - *n2;
}

int main(void) {
    
    int data[] = {4, 2, -2, 8, -9, 6, 4, 2, 1, 0, 17, -3, 23};
    int num = sizeof data / sizeof data[0];

    int k;

    qsort(data, num, sizeof(data[0]), (int(*)(const void*, const void*)) compare);

    for(k=0; k<num; k++)
        printf("%d ", data[k]);
    printf("\n");
    return 0;
}

$ gcc main.c -o main -lm && ./main
-9 -3 -2 0 1 2 2 4 4 6 8 17 23

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

int compare( const char **name1, const char **name2 ) /* 比較関数 */
{
	printf( "%s, %s\n", *name1, *name2 ); /* テスト用表示:実際には不要 */
	return strcmp( *name1, *name2 );
}

int main( void )
{
	char *names[] = { "rand", "calloc", "malloc", "wcstombs", "atol", "qsort", "atexit", "system", "bsearch", };
	int  num = sizeof names / sizeof names[0];

	int k;

	qsort( names, num, sizeof( names[0] ), (int (*)(const void *, const void * ))compare );

	for( k=0; k<num; k++ ) /* 結果の表示 */
		printf( "%s ", names[k] );
	printf( "\n" );

	return 0;
}

$ gcc main.c -o main -lm && ./main
rand, calloc
malloc, wcstombs
calloc, malloc
rand, malloc
rand, wcstombs
atol, qsort
system, bsearch
atexit, bsearch
atol, atexit
atol, bsearch
qsort, bsearch
qsort, system
calloc, atexit
calloc, atol
calloc, bsearch
calloc, qsort
malloc, qsort
rand, qsort
rand, system
wcstombs, system
atexit atol bsearch calloc malloc qsort rand system wcstombs

[C言語] 関数を指すポインタ

double f1(double x) {
    return x+1.0;
}

int main(void) {
    
    double (*p)(double) = f1; // ポインタpに関数fをセット

    double ans = p(5.0);

    printf("p(5.0)=%g\n", ans);

    return 0;
}

$ gcc main.c -o main && ./main
p(5.0)=6

ポインタは値、配列だけでなく、関数も指すことができる

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

double f1(double x) {
    return x+1.0;
}

double f2(double x) {
    return x+2.0;
}

int main(void) {
    
    double (*p)(double) = f1; // ポインタpに関数fをセット

    double ans1 = p(5.0);
    double ans2, ans3;

    p = f2;

    ans2 = p(5.0);

    p = sqrt;
    ans3 = p(2.0);

    printf("ans1=%g, ans2=%g, ans3=%g\n", ans1, ans2, ans3); // 6.0, 7.0, 4.0

    return 0;
}

$ gcc main.c -o main -lm && ./main
ans1=6, ans2=7, ans3=1.41421
コンパイル時に-lmオプションがないとエラーになる

double f1(double x) {
    return x+1.0;
}

double f2(double x) {
    return x+2.0;
}

double f3(double x) {
    return x+3.0;
}

int main(void) {
    
    double (*p[3])(double) = {f1, f2, f3}; // ポインタpに関数fをセット
    int k;
    for (k=0; k<3; k++){
        double ans = p[k](5.0);
        printf("p[%d](5.0)=%g\n", k, ans); // 6.0, 7.0, 8.0
    }

    return 0;
}

$ gcc main.c -o main -lm && ./main
p[0](5.0)=6
p[1](5.0)=7
p[2](5.0)=8

これはそのまま

typedef double(*funcTable[3])(double); // 関数を指すポインタの配列型

int main(void) {
    
    funcTable p = {f1, f2, f3};
    int k;
    for (k=0; k<3; k++){
        double ans = p[k](5.0);
        printf("p[%d](5.0)=%g\n", k, ans); // 6.0, 7.0, 8.0
    }

    return 0;
}

このように書くことができる

[C言語] ポインタの派生型

### ポインタのポインタ

int main(void) {
    
    int p0 = 12345;
    int *p1;
    int **p2; // ポインタのポインタ
    int ***p3; // ポインタのポインタのポインタ
    int t1, t2, t3;

    p1 = &p0;
    p2 = &p1;
    p3 = &p2;

    t1 = *p1;
    t2 = **p2;
    t3 = ***p3;

    printf("t1=%d, t2=%d, t3=%d", t1, t2, t3);


    return 0;
}

$ gcc main.c -o main && ./main
t1=12345, t2=12345, t3=12345
全てp0の値を出力する

### 配列を指すポインタ

int main(void) {
    
    int t[3] = {1, 2, 3};
    int (*p)[3];
    int t1;

    p = &t;
    t1 = (*p)[1];
    printf("t1=%d\n", t1);

    return 0;
}

$ gcc main.c -o main && ./main
t1=2
配列は通常の配列だけでなく、ポインタも配列として表現できる

int main(void) {
    
    int a[2][3][4] = {
        {{1,2,3,4}, {5,6,7,8},{9,10,11,12}},
        {{13,14,15,16}, {17,18,19,20},{21,22,23,24}}
    };
    int *b;
    int (*c)[4];
    int (*d)[3][4];

    int a1, a2, a3;

    b = a[1][2]; // 21,22,23,24 の先頭アドレス
    c = a[1]; // {13,14,15,16}, {17,18,19,20},{21,22,23,24}の先頭アドレス
    d = a; // aの先頭アドレス

    a1 = b[3]; // 24
    a2 = c[1][2]; // 19
    a3 = d[0][1][2]; // 7 

    printf("a[1][2][3] = %d\n", a[1][2][3]); // 24
    printf("a1=%d, a2=%d, a3=%d\n", a1, a2, a3); // 24, 19, 7

    return 0;
}

$ gcc main.c -o main && ./main
a[1][2][3] = 24
a1=24, a2=19, a3=7

ポインタは配列だけでなく、連想配列としても宣言できる。
想定通りの時の高揚感が良い

int main(void) {
    
    int k1=1, k2=2, k3=3;
    int *p[3] = {&k1, &k2, &k3};
    int t;

    for (t=0; t<3; t++){
        int value = *p[t];
        printf("value=%d\n", value); // 1, 2, 3
    }

    return 0;
}

$ gcc main.c -o main && ./main
value=1
value=2
value=3
for文にしただけ

int main(void) {
    
    int k;
    char *mes[] = {"one", "two", "three", (char *) NULL};

    for(k=0; mes[k]; k++){
        printf("mes=%s, ", mes[k]); // one two three
    }

    return 0;
}

$ gcc main.c -o main && ./main
mes=one, mes=two, mes=three,

for文でmes[k]となっているので、kに値があるまでで(char *) NULLは出力されない

[C言語] ポインタの利用

#include<stdio.h>

int main(void) {
    int *p;
    int k = 123456;
    int t;

    p = &k;
    t = *p;

    printf("p=%p, t=%d\n", p, t);

    return 0;
}

$ gcc main.c -o main && ./main
p=0xfffff6a796e8, t=123456

&kはアドレスで、ポインタを代入は値の代入になる

### 割り当てられていない領域をアクセス

int main(void) {
    int *p;
    int k = 123456;
    int t;

    p = &k;
    p++;
    t = *p;

    printf("p=%p, t=%d\n", p, t);

    return 0;
}

p=0xffffd0f0d40c, t=65535
pのアドレスを一つ進めているので、異なる値が表示される

### 割り当てられている領域をアクセス

int main(void) {
    int *p;
    int k[2] = {12345, 67890}; // 配列
    int t;

    p = &k[0];
    p++;
    t = *p;

    printf("p=%p, t=%d\n", p, t);

    return 0;
}

$ gcc main.c -o main && ./main
p=0xfffff62356e4, t=67890
配列の場合は、ポインタを一つ進めると、配列の次の値となる。

int main(void) {
    int *p;
    int k[2] = {12345, 67890}; // 配列
    int t;

    p = k;
    p++;
    t = *p;

    printf("p=%p, t=%d\n", p, t);

    return 0;
}

$ gcc main.c -o main && ./main
p=0xffffecd87384, t=67890

配列kは、kの先頭アドレスを指す

int main(void) {
    int *p;
    int k[3] = {111, 222, 333}; // 配列
    int t;

    p = &k[1];
    p++;
    t = *p;

    printf("p=%p, t=%d\n", p, t);

    return 0;
}

$ gcc main.c -o main && ./main
p=0xffffc27bcfc0, t=333

これは説明不要

int main(void) {
    int *p;
    int k[3] = {111, 222, 333}; // 配列
    int i;

    p = k;
    for(i =0; i < 3; i++){
        int value = *p++;
        printf("value=%d\n", value);
    }

    return 0;
}

$ gcc main.c -o main && ./main
value=111
value=222
value=333

配列の先頭アドレスから出力している
配列をポインタに代入して、ポインタを進めることで、配列の値を出力するというのは多用できそうですね。