ラベル c の投稿を表示しています。 すべての投稿を表示
ラベル c の投稿を表示しています。 すべての投稿を表示

2019年6月2日日曜日

C言語で仮引数の配列サイズは無視される

結論はタイトルの通りなんだけど、久々にサイズ付き配列を仮引数にしたコードを見かけてギョッとしたことがあったので、改めて調べてみた。

まず、以下のような仮引数自体は、文法的には一応NGではない。

void test(const char arg[32]) {
    printf("sizeof(arg) : %lu, arg : %s\n", sizeof(arg), arg);
}
ただ、決して配列を値渡ししているわけではない。ポインタや参照渡しを理解していない学生レベルの人が書きがちなコードなので、稀に仕事で見かけるとギョッとするのだろう。

さて、では、上記の例の32はどんな意味を持つのかというと、改めてタイトルの通り無視される。手元のApple LLVM version 10.0.1 (clang-1001.0.46.4)でサクッとコンパイルしてみると、実行するまでもなくwarningを吐いてくれた。

test_arg.c:4:51: warning: sizeof on array function parameter will return size of
      'const char *' instead of 'const char [32]' [-Wsizeof-array-argument]
    printf("sizeof(arg) : %lu, arg : %s\n", sizeof(arg), arg);
要するに、32だろうが256だろうがconst char *と書いたのと同様に解釈される。配列っぽく記述していてもコンパイラはポインタとして扱うので、配列の要素数という情報は捨てられる。sizeof(arg)は32ではなく、アドレスを表現するのに必要なサイズ(環境によるけど、8とか4)になるのだ。

やっぱり、サイズ情報を渡しているかのように誤解させる記述は、やめたほうがいいだろうね。…と言うわけで、ただ見慣れないという理由で頭ごなしに否定するのではなく、誤解の元になるから修正しろと言うのがスマートだろう。

2015年12月8日火曜日

C言語の__LINE__を文字列リテラル化したい

C言語の__LINE__は、そのファイルの中での行数を表す整数リテラルに展開される組み込みマクロ。それを文字列として扱うにはどうするかという話。

文字列に変換しようとしたらsprintf, snprintfを使うのが、まあ普通だろう。

sprintf(buf, "%d", __LINE__);
ただ、これは別途バッファを用意する必要があるし、デカくて重たいprintf系の関数は使いたくない(特に組み込みでは)。それに、コンパイル時に結果が一意に定まるはずの変換を動的にやらされるのは癪だ。

そこで使えるのが、プリプロセッサの文字列化演算子#。#stringを"string"に置換してくれるナイスガイ。ただ、__LINE__との併用は一筋縄ではいかず、

#__LINE__
#(__LINE__)
とか記述してもエラーになってしまう。
#define TO_STRING(arg) #arg

TO_STRING(__LINE__)
とマクロ化しても、これでは"__LINE__"になってしまう。正解は、マクロの引数として__LINE__だけ展開させてから、文字列化マクロに食わせるという、非常にまどろっこしい方法。
#define TO_STRING(arg) #arg
#define NUM2STR(num) TO_STRING(num)

NUM2STR(__LINE__)
これなら"10"とか"666"のような、""で括って文字列リテラル化した行数になる。あくまで文字列リテラルなので、
__FILE__ ":" NUM2STR(__LINE__)
みたいに記述して、コンパイル時に静的に文字列の結合もしてもらえる。

2015年10月21日水曜日

GCCは同名ヘッダのどれをインクルードするのか?

GCCに標準のディレクトリ以外からincludeするヘッダファイルを探してもらいたい場合、-Idir オプションで指定する。-Idir1 -Idir2のように複数指定すれば、指定した順に探してもらえる。…と思っていたら、困ってしまった。

簡単な例をアップした。この中には、以下のようにfoo.hが3つある。

test_include/
  +- inc1/
  |    +- foo.h
  +- inc2/
  |    +- foo.h
  |    +- bar.h : #include "foo.h"
  +- inc3/
       +- foo.h
       +- baz.h : #include "bar.h"
この状態で-Iinc1 -Iinc2 -Iinc3と指定してコンパイルすると、どのディレクトリのfoo.hがインクルードされるのか?

まず#include "foo.h"した場合。大方の予想通り、インクルードされるのはinc1/foo.h。これはいい。

次に#include "bar.h"した場合。inc2/bar.hがインクルードされるのは分かるのだが、そこから更にinc2/foo.hがインクルードされるのだ。bar.hから間接的に#includeする際には、inc1/より先にinc2/から探されてしまうようだ。

最後に#include "baz.h"した場合。inc3/baz.hからinc2/bar.hがインクルードされるのは良いとして、そこからインクルードされるのはinc2/foo.h。

プリプロセッサは、#includeディレクティブに遭遇するたびに検索ディレクトリリストの先頭から探すのだとばかり思っていたら、#includeが入れ子になっている場合は、直前にヘッダを見つけたディレクトリから検索を始めている? どうも、+librescanを付けないVCSのライブラリ検索みたいな感じの振る舞いに見える。なお、GCCではなくClangを使ってみても結果は同じだった。このあたりの仕様は規格で定められているのだろうか?

同名のヘッダをあちこちに置くのが悪いと言われたらその通りなのだけれど、一部のヘッダだけテスト用のものに差し替えたりしたいとき、ちょっと困る。

2014年7月24日木曜日

C言語で構造体の中のメンバの順序

ずっと勘違いしていた。C言語の構造体の中でのメンバの並びはコンパイラがいくらでも最適化しうるから、ソースコートで記述した順序が保たれる保証は全くないと思っていたのだが、そんなことはなかった。

リンク元から引用の引用をすると、

構造体オブジェクト内では, 非ビットフィールドメンバ及びビットフィールドが置かれる単位は, 宣言された順に増加するアドレスをもつ。構造体オブジェクトへのポインタは, 適切に変換すれば, その先頭メンバ(又はビットフィールドならば, それが置かれた単位)を指す。さらに, その逆も成り立つ。構造体オブジェクトの中に名前のない詰め物があってもよいが, 先頭には名前のない詰め物があってはならない。
だそうな。構造体の先頭以外にはパディングされうるけど、メンバの並び順までは変えられない。例えばこの構造体
struct s {
  char a;
  int b;
  char c;
};
に対しては、必ず以下の関係が満たされるわけだ。
offsetof(struct s, a) < offsetof(struct s, b) < offsetof(struct s, c)
上の例だと、a, b, cという順序に特に意味がなければ、大抵の場合はb, a, cと並び替えた方がメモリ使用効率が良いだろう。

パディングする/しないはC言語の標準機能で選べないので、具体的なオフセット値までは標準機能だけでは保証されない。

2014年7月3日木曜日

C99のlog2()

Cの標準ライブラリで用意されているのは自然対数log()と常用対数log10()のみで、2を底とする対数が欲しければ自分で計算しなければならない。そんなふうに考えていた時期が俺にもありました。

それがC99で、そのものずばりlog2()が追加されていた。

#include <math.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
    double xs[] = {1, 2, 3, 4, 6, 8, 11, 16, 100, 128, 200, 256};
    int i;
    for (i = 0; i < sizeof(xs) / sizeof(double); i++) {
        double y = log2(xs[i]);
        printf("log2(%lf) = %lf\n", xs[i], y);
    }
    return 0;
}
こいつを実行すると
log2(1.000000) = 0.000000
log2(2.000000) = 1.000000
log2(3.000000) = 1.584963
log2(4.000000) = 2.000000
log2(6.000000) = 2.584963
log2(8.000000) = 3.000000
log2(11.000000) = 3.459432
log2(16.000000) = 4.000000
log2(100.000000) = 6.643856
log2(128.000000) = 7.000000
log2(200.000000) = 7.643856
log2(256.000000) = 8.000000
こうなる。

もっともlog2が欲しい場面の大半は、ある整数を表現するのに何ビット必要かを計算するときなので、シフトしながら桁数を数えれば済む。double log2(double)はオーバースペックかな。

…と思ったけど、最近(?)のCPUは整数から浮動小数点数への変換をハードウェアでやってくれるので、doubleにキャストして指数部を直接見た方が速いかもしれない。

2014年5月28日水曜日

C99のstdint.hで幅指定した定数リテラル

C99のstdint.hで幅を明示した型は使えるようになったのだが、例えば1Lという定数リテラルの幅は、結局処理系のlongの幅に依存してしまう。なので幅を明示した定数を使いたいときは

((uint64_t) 1)
のようにキャストしていたのだが、stdint.hには定数リテラルを記述するためのマクロがあることを知った。

  • INT8_C(c)
  • INT16_C(c)
  • INT32_C(c)
  • INT64_C(c)
  • INTMAX_C(c)
  • UINT8_C(c)
  • UINT16_C(c)
  • UINT32_C(c)
  • UINT64_C(c)
  • UINTMAX_C(c)

LとかUとかサフィックスを付けたり付けなかったりしているだけのマクロなので、過剰な期待をしてはいけない。

UINT16_C(65536)
とかやっても引数の65536がそのままスルーされるだけで、下位16ビットだけ残して0になったりはしない。

2014年5月9日金曜日

C言語のrand()の落とし穴

C言語のrand()は、0以上RAND_MAX以下のint型の値を返す。なので、そもそも負の値を返さないrand()の戻り値は、MSBが絶対に立たない。rand()が質の悪い乱数であることは承知の上で、お手軽に32ビットの乱数を生成してテストケースをカバーするつもりが、実は31ビットの乱数だった…。

C++11のrandomを使おう。

2014年4月29日火曜日

GCCでローカル変数のアラインメント指定

GCCでアラインメントを指定するときは__attribute__((aligned(N)));を使うが、今までstaticな変数に対してしか使ったことがなかった。そこで、これはローカル変数でも有効なのか試してみた。以下サンプルコード。

#include <stdio.h>
int main(int argc, char *argv[]) {
    char buf_na_0[1];
    char buf_na_1[1];
    char buf_na_2[1];
    char buf_na_3[1];
    char buf_na_4[1];
    char buf_na_5[1];
    char buf_na_6[1];
    char buf_na_7[1];
    char buf_a8_0[1] __attribute__((aligned(8)));
    char buf_a8_1[1] __attribute__((aligned(8)));
    char buf_a16_0[1] __attribute__((aligned(16)));
    char buf_a16_1[1] __attribute__((aligned(16)));
    char buf_a32_0[1] __attribute__((aligned(32)));
    char buf_a32_1[1] __attribute__((aligned(32)));
    char buf_a64_0[1] __attribute__((aligned(64)));
    char buf_a64_1[1] __attribute__((aligned(64)));
    char buf_a128_0[1] __attribute__((aligned(128)));
    char buf_a128_1[1] __attribute__((aligned(128)));
    char buf_a256_0[1] __attribute__((aligned(256)));
    char buf_a256_1[1] __attribute__((aligned(256)));
    printf("&buf_na_0   = %p\n", buf_na_0);
    printf("&buf_na_1   = %p\n", buf_na_1);
    printf("&buf_na_2   = %p\n", buf_na_2);
    printf("&buf_na_3   = %p\n", buf_na_3);
    printf("&buf_na_4   = %p\n", buf_na_4);
    printf("&buf_na_5   = %p\n", buf_na_5);
    printf("&buf_na_6   = %p\n", buf_na_6);
    printf("&buf_na_7   = %p\n", buf_na_7);
    printf("&buf_a8_0   = %p\n", buf_a8_0);
    printf("&buf_a8_1   = %p\n", buf_a8_1);
    printf("&buf_a16_0  = %p\n", buf_a16_0);
    printf("&buf_a16_1  = %p\n", buf_a16_1);
    printf("&buf_a32_0  = %p\n", buf_a32_0);
    printf("&buf_a32_1  = %p\n", buf_a32_1);
    printf("&buf_a64_0  = %p\n", buf_a64_0);
    printf("&buf_a64_1  = %p\n", buf_a64_1);
    printf("&buf_a128_0 = %p\n", buf_a128_0);
    printf("&buf_a128_1 = %p\n", buf_a128_1);
    printf("&buf_a256_0 = %p\n", buf_a256_0);
    printf("&buf_a256_1 = %p\n", buf_a256_1);
    return 0;
}

そして実行結果。

&buf_na_0   = 0x7fff532159df
&buf_na_1   = 0x7fff532159de
&buf_na_2   = 0x7fff532159dd
&buf_na_3   = 0x7fff532159dc
&buf_na_4   = 0x7fff532159db
&buf_na_5   = 0x7fff532159da
&buf_na_6   = 0x7fff532159d9
&buf_na_7   = 0x7fff532159d8
&buf_a8_0   = 0x7fff532159d0
&buf_a8_1   = 0x7fff532159c8
&buf_a16_0  = 0x7fff532159c0
&buf_a16_1  = 0x7fff532159b0
&buf_a32_0  = 0x7fff532159a0
&buf_a32_1  = 0x7fff53215980
&buf_a64_0  = 0x7fff53215940
&buf_a64_1  = 0x7fff53215900
&buf_a128_0 = 0x7fff53215880
&buf_a128_1 = 0x7fff53215800
&buf_a256_0 = 0x7fff53215700
&buf_a256_1 = 0x7fff53215600

結果としてはきちんとアラインメントされた。普通のGCCだけでなく、MavericksのLLVM GCCでもこのattributeは有効なのね。

些末なことだけど、alignmentのカタカナ表記はアラインメントよりアライメントの方がずっと優勢なのか。音的にはアラインメントの方が近い気がするんだが。

2014年2月13日木曜日

GCCでアトミックな変数操作

ARMでアトミックな変数操作をしたくてググっていたら、こちらに辿り着いた。

ほうほう。LDREX, STREXなんて命令があって、アトミックに操作できるよう自分以外を止めるのではなく、アトミックに操作できなかったらリトライするのか。普通はアトミックに操作できない方が確率的にレアだろうから、失敗した時しかやり直さないこの方法は良い性能が出そうだな。

…なんて思いながら読んでいたら、GCCには__sync_fetch_and_add()みたいな組み込み関数があるとのこと。ちょっと使ってみたら

.L3:
        ldrex   r1, [r3]
        add     r1, r1, #1
        strex   r2, r1, [r3]
        cmp     r2, #0
        bne     .L3
しっかりLDREX, STREXを吐いてくれた。ついでにx86でも試してみたら、lockプレフィックスを付けてくれていた。GCC限定だけど、こりゃいいわ。

2014年2月7日金曜日

C99のuintptr_t, intptr_t

C99のuintptr_tとintptr_t。一言で説明すると「ポインタと同じビット幅の整数型」。要は

sizeof(uintptr_t) == sizeof(void *)
sizeof(intptr_t) == sizeof(void *)
が成り立つことを保証している整数型なわけだ。符号の有無は名前の通り。

アドレスを整数に、整数をアドレスにキャストするようなコードって、大抵ガチガチにプラットフォーム依存しているので、移植性なんて考えずにuint32_tとか使っていたYO。

2014年1月30日木曜日

Eclipseでデバッグ中に表示する変数の値のデフォルト基数

EclipseでHW寄りのコードをデバッグする際、Variablesで値を10進表示されると分かりづらい。

で、一々右クリックでFormatをHexadecimalに変更していたのだが、いい加減、面倒になってきた。そこでデフォルトの表示フォーマットの設定項目を探したところ、

Preference>C/C++>Debug
にDefault number formatという設定があった。ここでVariablesをHexadecimalに変更したら、しっかり表示が変わってくれた。ああ、良かった。

ところで、デフォルトの表示設定はDefaultとなっているのだが、このDefaultの挙動を定めている設定項目はどこ?

2013年11月16日土曜日

C言語のプリプロセッサで文字列化、トークンの連結

たまに使おうとすると忘れてググるシリーズ。

C言語のプリプロセッサでマクロの引数を""で括って文字列化したり、トークンを連結して新たなトークンを作るときには、#や##演算子を使う。

例えば

#define TO_S(a) #a
#define CAT(a1, a2) a1 ## _ ## a2
こんな定義をしておいて、
TO_S(foo)
CAT(bar, baz)
こんな入力をすると
"foo"
bar_baz
こうなる。

2013年2月10日日曜日

C言語で浮動小数点数の丸め、特に負の値

小数点以下を切り上げたり切り下げたりする話は、意外と疎かにされがちな(コードのお守りを押し付けられたりする)ので、今更ながら改めて整理しよう。

floor(x)
切り下げ。小数点以下がゼロでなければ、xより小さくxにもっとも近い整数を返す。
ceil(x)
切り上げ。小数点以下がゼロでなければ、xより大きくxにもっとも近い整数を返す。
round(x)
四捨五入。xにもっとも近い整数を返す。ちょうど2つの整数の中間だった場合(3.5とか)は絶対値が大きいほうを選ぶ。
(int) x
切り捨て。小数点以下がゼロでなければ、xより絶対値が小さくxにもっとも近い整数を返す。
手っ取り早く実例。

xfloor(x)ceil(x)round(x)(int) x
2.52.03.03.02
2.02.02.02.02
1.41.02.01.01
0.00.00.00.00
-1.4-2.0-1.0-1.0-1
-2.0-2.0-2.0-2.0-2
-2.5-3.0-2.0-3.0-2

floo()やceil()が符号を気にしないのに対して、round()や整数型へのキャストの挙動は正負対象となる。ハードウェアのCモデルを作る人は、ここらへんをきちんと考えながら書いてくれ。…って言うか、固定小数点のハードウェアモデルを書くのにdoubleとか使うなよ。

2012年7月17日火曜日

C言語で関数名取得

C99で追加された__func__を使えばOK。

void foo(void) { printf("%s()\n", __func__); }
void bar(void) { printf("%s()\n", __func__); }
int main(void) {
  printf("%s()\n", __func__);
  foo();
  bar();
  return 0;
}

これを実行すると

main
foo
bar

と表示される。 __func__がコンテキストに応じて値が変わる、文字列定数のように振る舞うわけ。

ちなみに__FILE__や__LINE__とは異なり、__func__はプリプロセッサが処理するマクロではない。 プロプロセッサが関数まで踏み込むはずが無いから、当然と言えば当然だけど。

2012年6月24日日曜日

C/C++の変数宣言とswitch

昔のC/C++言語では関数の頭で変数宣言しなくてはならなかったけど、最近はもうどこでもOKなのかと思ってたら、switch文で分岐した先でやったら怒られた。
まあ考えてみれば、breakせずに上のcaseから降りて来たりする中で、新たに変数を作られるのは嫌だよな〜、とは思う。

ちなみに、{}で括って明示的にブロック化してやれば、その中では変数宣言OKだった。

2012年6月17日日曜日

Valgrind

Valgrindという素敵なツールの存在を知った。
こいつを使えば、CやC++で不正なメモリアクセスをしても、動的に検知してくれる。それも、どの関数がどこを触ったかなんて情報まで出してくれる。
メモリのゴミを読んで動いたり動かなかったりするバグも、これを使えばゴミを読んだことをレポートしてくれるので、非常に助かる。

2012年6月13日水曜日

C++でtypedefのテンプレート化のようなもの

C99のstdint.hを使えばint8_tやuint16_t等のビット幅を指定した型を利用できるが、この8や16等のビット幅をC++のテンプレート引数にしたい。が、typedefはテンプレート化できないし、classはオーバーヘッドが大きくて嫌だ。さて、どうにかならないか考えてみた。
template <size_t width> union uint_u;
template<> union uint_u<8> {uint8_t v;};
template<> union uint_u<16> {uint16_t v;};
template<> union uint_u<32> {uint32_t v;};
template<> union uint_u<64> {uint64_t v;};
template <size_t width> union sint_u;
template<> union sint_u<8> {int8_t v;};
template<> union sint_u<16> {int16_t v;};
template<> union sint_u<32> {int32_t v;};
template<> union sint_u<64> {int64_t v;};

こんなのを定義しておいて
uint_u<16> u16;
sint_u<64> s64 = {-123456};
s64.v = u16.v * -77;
こんな感じで使うのはどうだろう?

…う〜ん、やっぱりダサいから没。これならマクロを使うか、がっつりclassを組んでコンパイラの最適化に期待する方がいいかな。

2012年6月7日木曜日

C/C++のポインタやリファレンスに関する悩みどころ

悩ましいのはポインタとリファレンスの使い分けとかではなく、記述の問題。

C/C++のポインタ変数の宣言には、大まかに分けて2つの流儀がある(と思う)。1つは
int *p;
もう1つは
int* p;
違いはスペースの位置。個人的には前者のスタイルなのだが、これは*が変数を修飾するものだから、という理由。後者のスタイルでは、*はintという型を修飾するものだという考え方なのだろう。
int *p, i;
という変数宣言では*は変数pのみを修飾しているので、この点からは前者が正しいように見える。ところが
(int *) xx
reinterpret_cast<int *>(xx)
のようなキャスト演算では、*は明らかにintを修飾している。なので、結局どちらとも言い切れないのが、この悩ましさの元凶なんだよなぁ。

C++リファレンスを表す&も同様なんだけど、こっちは
int& r = i;
のように型に付ける方が主流派な気がして、変数修飾派はなんとなく肩身が狭い。