【C言語】ポインタ

ポインタって分かりにくいですよね。。。

改めて自分の理解のためにメモ書きしていきたいと思います。

今日のお話

環境

対象言語

  • c

メモ

  • int型、double型のように、ポインタ型がある。

  • ポインタ型にも ポインタ(int)型:int *ポインタ(double)型:double * がある。

→型として理解すればOKの認識。

  • ポインタ型は変数のアドレスを保持する

  • 1回作成した変数に対して、操作をしたい時に、アドレスに対して操作を行えばよいため、効率が良い。 →「正直使わなくても問題ないですよね?」と言われてしまうと、この段階では問題は発生しない。

#include <stdio.h>

void AddNumber(int *value)
{
    *value = *value + 100; // 直接対象のアドレスに対して値を加えている
}

int main()
{
    int a = 100;
    AddNumber(&a);
    printf("%d\n", a);
    return 0;
}
#include <stdio.h>

int AddNumber(int value) // 仮引数が生成される時に新たにvalue変数が生成されている
{
    value = value + 100;
    return value;
}

int main()
{
    int a = 100;
    a = AddNumber(a); // 戻ってきた値を改めてaに代入
    printf("%d\n", a);
    return 0;
}
  • ポインタが必要になるのは配列の場合で、生成した(or 引数で受け取った)値をアドレスを通じて戻さないといけない。
#include <stdio.h>
#include <stdlib.h>

int *AddNumber()
{
    int *values = (int *)malloc(sizeof(int) * 100);
    for (int i = 0; i < 100; i++)
    {
        values[i] = 100;
    }
    return values;
}

int main()
{
    // int a[100];
    int *a;
    a = AddNumber();

    for (int i = 0; i < 100; i++)
    {
        printf("No.%d: %d\n", i, a[i]);
    }
    return 0;
}

関数から配列(値)返すことができない。 →ローカル変数は関数の中でのみ有効になるため

↓はNG(出力するとおかしな値になる)

#include <stdio.h>

int *AddNumber()
{
    int values[100];
    for (int i = 0; i < 100; i++)
    {
        values[i] = 100;
    }
    return values; // returnした後にvaluesが解放されてしまう
}

int main()
{
    // int a[100];
    int *a;
    a = AddNumber();

    for (int i = 0; i < 100; i++)
    {
        printf("No.%d: %d\n", i, a[i]); // 正しい値が出力されない
    }
    return 0;
}
  • 関数の引数は値渡しになるため NULL 値を渡してローカル変数で設定しても 関数のスコープを抜けたときに解放されてしまう。 どうしてもやりたい場合は、ダブルポインターを使う。
#include <stdio.h>
#include <stdlib.h>

void AddNumber(int **value)
{
    *value = (int *)malloc(sizeof(int));
    **value = **value + 100;
}

int main()
{
    int *a = NULL;
    AddNumber(&a);
    printf("%d\n", *a);
    return 0;
}
  • 配列の指定方法はどちらで同じ
    • a[i] = 100;
    • *(pa + i) = 200;
int a[10];
int *pa = a;  // &a[0]と同じ

for (int i; i < 10; i++)
{
    a[i] = 100;
    *(pa + i) = 200;
    printf("No.%d: %d\n", i, *(pa + i));
}
  • 関数の引数で配列を使う場合、以下は全てパターン1と同じ意味になる。 →どれで書いても同じなので、パターン1を使った方が無難そう
int SampleFunc(int *a);  // パターン1
int SampleFunc(int a[]);  // パターン2
int SampleFunc(int a[5]);  // パターン3
  • 下記は全て同じ結果になる。
int a[10];
printf("&a    : %p\n", (void *)&a);
printf("&a[0] : %p\n", (void *)&a[0]);
printf("a     : %p\n", (void *)a);

printf("============\n");

for (int i = 0; i < 5; i++)
{
    printf("%d      : %p\n", i, (void *)&a[i]);
}

printf("============\n");

int *p = a;
for (int i = 0; i < 5; i++)
{
    printf("%d      : %p\n", i, (void *)&p[i]);
}

printf("============\n");

// この形式で追加しても良いが型がint[10]へのポインタになる
int(*pa)[10];
pa = &a;
for (int i = 0; i < 5; i++)
{
    // (int *)にキャストして、そこからポインタ演算でインクリメント
    printf("%d     : %p\n", i, (void *)(((int *)pa) + i));
}

戯言

  • ポインタもそうだけど、C言語に詳しくならないと。。。

【C++】コールバッククラスを作ってみた

コールバックを使ってみたかった。

あと、コールバック用のクラスも作ってみたかった。

という自分用のメモです。

今日のお話

環境

対象言語

プログラム

/**
 * @file task.cpp
 * @brief 
 * @details 
 * ■やってみたかったこと
 * ①外部から処理を設定する
 * ②処理の順番を決める
 * @date 2021-03-20
 */
#include <iostream>
#include <functional>
#include <stdio.h>
#include <time.h>

/**
 * @brief std
 * @details 
 */
using namespace std;

/**
 * @brief タスク
 * @details 
 */
class Task
{
private:
    function<void()> prepare = nullptr;
    function<int()> execute = nullptr;
    function<void()> postproc = nullptr;
    function<void(int)> success = nullptr;
    function<void()> error = nullptr;

public:
    /**
     * @brief 準備処理
     * @details 
     * @param _prepare 
     */
    void onPrepare(function<void()> _prepare)
    {
        prepare = _prepare;
    }

    /**
     * @brief 実行処理
     * @details 
     * @param _execute 
     */
    void onExecute(function<int()> _execute)
    {
        execute = _execute;
    }

    /**
     * @brief 事後処理
     * @details 
     * @param _postproc 
     */
    void onPostproc(function<void()> _postproc)
    {
        postproc = _postproc;
    }

    /**
     * @brief 成功時の処理
     * @details 
     * @param _postproc 
     */
    void onSuccess(function<void(int)> _success)
    {
        success = _success;
    }

    /**
     * @brief 失敗時の処理
     * @details 
     * @param _postproc 
     */
    void onError(function<void()> _error)
    {
        error = _error;
    }

    /**
      * @brief 実行関数
      * @details 設定されている処理を順に実行していく
      */
    void Exec()
    {
        // 事前処理
        prepare();

        // 実行処理
        try
        {
            // 実行
            int res = execute();
            // 成功
            success(res);
        }
        catch (exception &ex)
        {
            // 失敗
            error();
        }
        // 後処理
        postproc();
    }
};

/**
 * @brief 乱数 初期値
 * @details 乱数の初期値設定関数
 */
inline void InitRand()
{
    srand((unsigned int)time(NULL));
}

/**
 * @brief メイン関数
 * @details 
 * @return int 
 */
int main()
{
    // タスクインスタンス
    Task task = Task();

    // 準備処理
    task.onPrepare([]() {
        cout << "** prepare **" << endl;
        InitRand();
    });
    // 実行処理
    task.onExecute([]() {
        cout << "exec..." << endl;
        // 乱数の発生
        int res = rand();
        if (res % 2 == 0)
        {
            throw exception();
        }
        return res;
    });
    // 事後処理
    task.onPostproc([]() {
        cout << "** postproc **" << endl;
    });
    // 成功時
    task.onSuccess([](int res) {
        cout << "success : " + to_string(res) << endl;
    });
    // 失敗時
    task.onError([]() {
        cout << "error" << endl;
    });

    // 実行
    task.Exec();

    return 0;
}

気付き

  • function型というものがある。

  • 関数を外部から注入することができるので、見通しをよくできそう。

戯言

  • もう春ですね

【Bash】ダミーファイル作成 ( +ファイル加工 )

システム作っているとダミーファイルを作ることもありますね。

テストの時とかね。

めんどくさいことはシェルにやってもらおう!

今日のお話

環境

対象言語

10個のファイルを作りたい場合

コマンド

$ for i in $(seq 0 9) ; do echo Value = $i > $i.txt; done

メモ

  • 0.txt ~ 9.txt までファイルが作られる
  • 中身は「Value = xx」になる。

10MBのサイズのファイルを作成する

コマンド

$ dd if=/dev/zero of=dummy bs=10485760 count=1

メモ

  • Linuxならbsのところは10MBと書けばいいと思う。
  • ifはインプットするファイル名(今回はNULLで敷き詰めている)
  • ofはアウトプットするファイル名

特定の文字を検索結果のファイルに挿入

コマンド

$ find . -name "*.txt" -print | while read LINE; do echo "add text" >> $LINE; done

メモ

  • txtファイルで見つけ出したファイルに対して「add text」を追加
  • while/forのセミコロンの位置が分からなくなるけど要するに do xxx なんだ。(今から〇〇しますよーみたいなね)

気付き

  • 今回は特にないですがあえていうのであれば、macLinuxで動作が違うことがあるから気をつけないとです。

戯言

  • whileとかforとかさらっと使えるとすごく時短になるよね。
  • こんな時に使えます!みたいなものがいっぱいあると読んでて楽しくなりそうだなー。

【Linux】Findを使ったコマンド例

Findコマンドについてメモしておく。

結構使うことあるんだよな。。。

都度更新していこう。

今日のお話

環境

対象言語

サンプルフォルダ構成

━app●
 ┝.git●
 ┝src●
 ┃┝hoge.cpp
 ┃┝hoge.h
 ┝lib●
  ┝libsample.so
● : ディレクトリ

コマンド

  • .git以外のフォルダの一覧

$ find app -name ".git" -type d -prune -o -type d -print

気付き

  • macLinuxではオプションが違うから注意が必要だ。

戯言

  • 随時更新していこう。。。
  • findはどんどん使って行きたい。
  • xargsとかも使えるようにしていきたい。

【C++】カスタム例外と継承

カスタム例外を作りたくなりました。
さらにカスタム例外クラスを継承したくなりました。
なので試してみようと思います。

今日のお話

開発言語

やりたいこと

  • 例外クラスのインスタンスを作る時にメッセージを設定できるようにしたい
  • ↓みたいな階層構造にしたいと思いました。

  exception:標準例外クラス
  ┗BaseException:カスタム例外クラス(親クラス)
   ┗SubException:カスタム例外クラス(子クラス)

サンプルソース

#include <iostream>

using namespace std;

// カスタム例外クラス(親クラス)
class BaseException : public exception
{
public:
    BaseException(const string &msg) { message = msg; }
    char const *what() const noexcept { return message.c_str(); }

private:
    string message;
};

// カスタム例外クラス(子クラス)
class SubException : public BaseException
{
public:
    SubException(string msg) : BaseException(msg) {}
};

// サンプル関数
void SampleFunc()
{
    try
    {
        throw SubException("aaa");
    }
    catch (SubException subex)
    {
        cout << subex.what() << endl;
        throw;  //ここで再スロー
    }
}

// メイン関数
int main()
{
    try
    {
        SampleFunc();
    }
    catch (BaseException bex) // SubExceptionも受け取れる
    {
        cout << bex.what() << endl;
    }
    return 0;
}

気付き

  • exceptionクラスはデフォルトではコンストラクタにメッセージを追加することができないみたいです。*1
  • SubException のスローすると BaseException でもキャッチすることができますね。(当たり前ですがw)
  • これで SubException に詳細エラーを書いて、 BaseException でもう少し全体的なエラーにすることができます。

戯言

  • exception クラスにはデフォルトでメッセージを登録するようなことはできないみたいです。
  • 例外も階層化すればエラーがあった時にトレースが便利になると思うから、設計の時とかに意識して使っていけるようにしたいな。

*1:例外クラスとstd::exception:https://teratail.com/questions/79022

【CentOS7】C++でReflection ~RTTRライブラリのインストール~

C++でReflectionがしたい。。。
そんな時にGithubでRTTRと言うライブラリを見つけました。
今回は試行錯誤してインストールした方法を書いてみたいと思います。

github.com

注意事項

このインストール方法は公式の方法ではありません。
試行錯誤したらやっとライブラリが使えるようになったので、同じ悩みを抱えている人の参考になるかと思い書いてます。
公式インストール手順は、ちゃんと下記に載ってます。

https://www.rttr.org/doc/master/building_install_page.html

今日のお話

開発言語

環境

インストール方法

必要なパッケージをインストール

root ユーザで実行

# yum install -y gcc-c++
# yum install -y epel-release
# yum install -y —enablerepo=epel cmake3

RTTRライブラリのビルドとインストール

root ユーザで実行

# wget https://www.rttr.org/releases/rttr-0.9.6-src.tar.gz
# tar zxvf rttr-0.9.6-src.tar.gz
# mkdir build
# cd build/
# cmake3 -DCMAKE_BUILD_TYPE=Release ../rttr-0.9.6/
# make
# make install
# cp -rp install/lib64/librttr_core.so /usr/lib64/
# cp -rp install/lib64/librttr_core.so.0.9.6 /usr/lib64/

RTTRライブラリを使ったcppファイルを作成

main.cpp *1

#include <iostream>
#include <rttr/registration>
static void f() { std::cout << "Hello World" << std::endl; }
using namespace rttr;
RTTR_REGISTRATION
{
    using namespace rttr;
    registration::method("f", &f);
}
int main()
{
    type::invoke("f", {});
}
// outputs: "Hello World"

RTTRライブラリを使ってアプリをビルド

  • build ディレクトリにmain.cppを作ってる想定です。
  • gcc実行は build ディレクトリで実行します。
  • ビルドは通常ユーザで実施です。

○ビルドコマンド

$ g++ -std=c++11 main.cpp -I./install/include -lrttr_core

最終的に↓みたいなディレクトリ構成です!

/usr/lib64                     #共有ライブラリ
┣librttr_core.so
┗ librttr_core.so.0.9.6
/home/vagrant
┣rttr-0.9.6/
┗build/
  ┣main.cpp           #作ったcppファイル
  ┗install/
    ┗include/      #ヘッダー

気付き

  • 共有ライブラリパス /usr/lib64 にライブラリを配置することでgccコマンドでライブラリのパス指定が不要になる。
  • ライブラリの指定は、-Lでできるらしい。※今回はコンパイルが上手くいかなかったのであえてファイルをcpしました。
  • ライブラリ名は「libxxx.so」となっている必要があり、gcc側でパラメータを指定する時は、「-lxxx」となる。( lib , .so は不要になる)

戯言

  • cmakeについてもっと少し理解を深める必要があるなー。
  • って言うか、c++のライブラリインストール難しいよ!
  • あとgccもわかりにくいよ!

*1:サンプルコードの出典元( 1行目の #include だけ追加してます) https://www.rttr.org/doc/master/register_hello_world_page.html