[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

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

[bitcoin] Berkeley DB

Bitcoincoreで使用されているBerkeley DBについて

– Cで記述されたソフトウェアライブラリ
– Key/value API
– SQL APIとしてSQLiteが組み込まれている
– Bツリー、ハッシュ、キュー、RECNOストレージ
– C++, Java/JNI, C#, Python, Perlなど
– Java Direct Persistence Layer
– JavaコレクションAPI
– レプリケーション
– Berkeley DB is not a relational database, although it has database features including database transactions, multiversion concurrency control and write-ahead logging. BDB runs on a wide variety of operating systems including most Unix-like and Windows systems, and real-time operating systems.
– SQLのようなデータ操作言語を持たず、データベースへのアクセスは全てサブルーチン呼び出しによって行う。しかしdbmとは異なり、データ操作機能にトランザクションやレプリケーションに対応するインタフェースが備わっている
– Berkeley DB本体が対応するプログラミング言語はCおよびC++だけだが、Perl、Python、Tcl他多くの言語にバインディングが用意されており、それらから容易に利用することができる。

概要はわかったので、使い方ですな。

Scrypt proof of workのScriptとは?

https://cryptobook.nakov.com/mac-and-key-derivation/scrypt

Scrypt (RFC 7914) is a strong cryptographic key-derivation function (KDF). It is memory-intensive, designed to prevent GPU, ASIC and FPGA attacks (highly efficient password cracking hardware).

key = Scrypt(password, salt, N, r, p, derived-key-len)

### Script Parameters
N – iterations count (affects memory and CPU usage), e.g. 16384 or 2048
r – block size (affects memory and CPU usage), e.g. 8
p – parallelism factor (threads to run in parallel – affects the memory, CPU usage), usually 1
password– the input password (8-10 chars minimal length is recommended)
salt – securely-generated random bytes (64 bits minimum, 128 bits recommended)
derived-key-length – how many bytes to generate as output, e.g. 32 bytes (256 bits)

The memory in Scrypt is accessed in strongly dependent order at each step, so the memory access speed is the algorithm’s bottleneck. The memory required to compute Scrypt key derivation is calculated as follows:

Memory required = 128 * N * r * p bytes

Choosing parameters depends on how much you want to wait and what level of security (password cracking resistance) do you want to achieve:

Script hash generator
https://8gwifi.org/scrypt.jsp

$ sudo apt-get install python-dev-is-python3
$ pip3 install scrypt

import pyscript

salt = b'aa1f2d3f4d23ac44e9c5a6c3d8f9ee8c'
passwd = b'p@$Sw0rD~7'
key = pyscript.hash(passwd, salt, 2048, 8, 1, 32)
print("Derived key:", key.hex())

Litecoin, Dogecoinとは

### Litecoin
2011年にリリースされた通貨
scryptをproof of workのアルゴリズムとして使用している

– ブロック生成時間 2.5分
– 通貨総発行量: 2140年までに8400万litecoin
– コンセンサスアルゴリズム: Scrypt proof of work
– 開発者はチャーリーリー
https://github.com/litecoin-project/litecoin

Scrypt proof of workは基本的なハッシュ関数としてscryptを使用しているHashcash証明の証明

### Dogecoin
2013年12月にリリースされたもので、Litecoinのフォークに基づくもの。
支払いやチップの利用を促すもので、通貨発行のスピードを速くしている
– ブロック生成時間 60分
– 通貨総発行量: 2015年までに1000億doge
– コンセンサスアルゴリズム: Scrypt proof of work
– Billy MarkusとJackson Palmerが開発者
https://github.com/dogecoin/dogecoin

[C++] for文のautoの書き方

std::vector<int> v;

for(std::vector<int>::const_iterator it = v.begin(), e=v.end(); it != e; ++it){
    std::cout << *it << std::endl;
}

c++11の範囲for文を使うと以下のように書ける

std::vector<int> v;

for(const auto& e: v){
    std::cout << e << std::endl;
}

### 範囲for文(range-based for statement)
配列やコンテナなど複数の要素を持つものから、全ての要素に含まれる値を取り出して処理する

#include <iostream>
using namespace std;

int main() {
    int a[] = {1, 2, 3, 4, 5};
    int sum = 0;
    for(int x : a){
        sum += x;
    }
    cout << "sum = " << sum << end;
    return 0;
}

vector要素の出力

    vector<string> v(5);
    for (int i = 0; i < v.size(); i++){
        string& x = v[i];
        cin >> x;
    }

autoは型推論を行うキーワード

    vector<string> v(5);
    for (auto& x : v){
        cin >> x;
    }

[C++] ヘッダとソースでファイルを分ける

C++ではクラスの定義とそのメンバ関数の定義をヘッダファイルとソースファイルで分割するのが一般的である

### ファイルを分割していない例
c.hpp

#ifndef C_HPP
#define C_HPP

class c {
    private:
    int m_value;

    public:
    int get() {
        return m_value;
    }

    void set(int const value){
        m_value = value;
    }
};

#endif

### ファイルを分割した例
c.hpp

#ifndef C_HPP
#define C_HPP

class c {
    private:
    int m_value;

    public:
    int get();

    void set(int const value);
};

#endif

c.cpp

#include "c.hpp"

int c::get(){
    return m_value;
}

void c::set(int const value){
    m_value = value;
}

インラインで書くこともある

#ifndef C_HPP
#define C_HPP

class c {
private:
int m_value;

public:
int get();

void set(int const value);
};

inline int c::get(){
return m_value;
}

inline void c::set(int const value){
m_value = value;
}

#endif

bitcoinのソースコードで、基本hppとcppのファイル構造になっていたが、謎が解けた。

[C++].hppとは

### .hppとは
ヘッダーファイルで.hpp、.hというファイル形式で保存
.hppはc++が入っている。.hはc言語だがc++でも使用できる

sample.hpp

#ifndef Sample_hpp
#define Sample_hpp

#include <iostream>

using namespace std;

class Sample {
    public:
        string name;
};

#endif