いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

gccの __attribute__ 拡張メモ (GCC attribute specifier, GCC attribute syntax, cleanup, packed, constructor, always_inline, unused)

関連記事

GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ · GitHub

概要

以下、自分用のメモです。よく忘れるのでここにメモメモ。。。。

C言語でコンパイラに gcc とか clang 使っている場合、GCC拡張が使えます。

拡張って名前が付いているので、純粋なC規格には含まれていないものですが、知ってると便利なものが沢山あります。

実務で実装する場合は、MISRA準拠とかが結構多いので、そういうところでは使えないです。なので、あまり知られていないのかもしれませんね。

個人的に cleanup は、Goのdeferに近い動きになるので、めっちゃ便利です。ツールをC言語とかで作る場合はよく利用しています。

こんな感じ。

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

static void fclosep(FILE **pf) {
    printf("CALL fclosep\n");

    if (pf && *pf) {
        fclose(*pf);
        *pf = NULL;
    }
}

static void freep(void *p) {
    printf("CALL freep\n");

    void **pp = p;
    free(*pp);
    *pp = NULL;
}

int process_file(const char *path) {
    FILE *fp __attribute__((cleanup(fclosep))) = fopen(path, "r");
    char *buf __attribute__((cleanup(freep)))  = malloc(4096);

    if (!fp || !buf) {
        return -1;  // ここで return しても自動で fclose/free
    }

    fgets(buf, 4096, fp);
    printf("%s\n", buf);

    return 0;  // ここでも同様に自動解放
}

int main(void) { process_file("main.c"); }
$ gcc -o app main.c
$ ./app
#include <stdio.h>

CALL freep
CALL fclosep

よく利用されるもの

数が多いので、Claudeさんに手伝ってもらいました。

関数につけるもの

// 戻り値を無視した場合にコンパイラ警告を出す
__attribute__((warn_unused_result))
int read_data(void *buf, size_t len);

// 引数が NULL であれば未定義動作と明示(最適化ヒント)
__attribute__((nonnull(1, 2)))
int process(const char *path, void *buf);

// 関数が戻らないことを明示(exit, abort 等)
__attribute__((noreturn))
void fatal(const char *msg);

// printf 系のフォーマット文字列チェックを有効化
// 第1引数: フォーマット文字列の位置, 第2引数: 可変引数の開始位置
__attribute__((format(printf, 1, 2)))
void my_log(const char *fmt, ...);

// インライン展開を強制
__attribute__((always_inline))
static inline int clamp(int v, int lo, int hi);

// インライン展開を禁止
__attribute__((noinline))
void debug_dump(void);

// 使用されていても「未使用」警告を抑制
__attribute__((unused))
static void legacy_func(void);

// 非推奨マーク(呼び出し側に警告)
__attribute__((deprecated("use new_func instead")))
void old_func(void);

// 関数がグローバル状態を参照・変更しない純粋関数
__attribute__((pure))    // グローバル読み取りはOK
__attribute__((const))   // グローバルアクセス完全禁止(より強い)
int square(int x);

変数とか型につけるもの

// パディングを除去してメモリを詰める
struct __attribute__((packed)) packet_header {
    uint8_t  type;
    uint16_t length;
    uint32_t seq;
};

// アライメントを指定
__attribute__((aligned(64)))
static uint8_t dma_buffer[4096];  // キャッシュライン境界合わせ

// 変数が使われなくても警告を抑制
__attribute__((unused))
static int debug_flag = 0;

// 変数をBSSではなく特定セクションに配置
__attribute__((section(".flash_data")))
const uint8_t firmware_version[] = {1, 0, 0};

共有ライブラリで使う

// シンボルをエクスポート(デフォルト可視)
__attribute__((visibility("default")))
int public_api(void);

// シンボルを非公開(.so内部専用)
__attribute__((visibility("hidden")))
static int internal_helper(void);

その他

// main() より前に実行される
__attribute__((constructor))
static void lib_init(void) {
    // ライブラリの初期化処理
}

// main() 終了後に実行される
__attribute__((destructor))
static void lib_fini(void) {
    // ライブラリの後処理
}

// 優先度指定(101〜65535、小さいほど先に実行)
__attribute__((constructor(200)))
static void init_phase2(void) { ... }

参考情報

gcc.gnu.org


過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。