小ネタです。
今まで、C++で関数呼出の引数に配列を指定する際、予めポインタ変数として宣言してからでないとダメだと思っていました。
C++
void myFunc(uint8_t* arg){
// hogehoge
}
uint8_t* val = {0xAA, 0xBB, 0xCC};
myFunc(val);
やりたかったこと
以前からずっと、C++でもJavaScriptみたいに引数に配列を直接指定してスッキリさせたいなーと、空き時間に調べていたのですが、なかなか上手く行きませんでした。
JavaScript
function myFunc(arg){
// hogehoge
}
myFunc([0xAA, 0xBB, 0xCC]);
Arduinoでは古来から、文字列リテラルをポインタ型にキャストすることで配列のように扱えるハックがありますが、文字列ってそれ元の\0
はどこいったの?など精神衛生的にどうにも気持ち悪く、他の方法を探していました。
C++(Arduino)
void myFunc(uint8_t* arg){
// hogehoge
}
myFunc((uint8_t*)"\xAA\xBB\xCC");
試したこと
純粋に考えて、配列初期化子{}
をポインタ型にキャストすれば行けるんじゃね!?と思って試しましたが、そう簡単にはいきませんでした。
C++
void myFunc(uint8_t* arg){
// hogehoge
}
myFunc((uint8_t*){0xAA, 0xBB, 0xCC});
Error
too many initializers for 'uint8_t* {aka unsigned char*}'
invalid conversion from 'int' to 'uint8_t* {aka unsigned char*}' [-fpermissive]
解決したこと
試した方法では、ズバリ構文記法が問題でした。
キャスト構文に用いるポインタ記号は*
ではなく[]
を使用する必要がありました。
その2つは同じポインタ型を示す記号だと思っていたので、思わぬ落とし穴でした。
C++
void myFunc(uint8_t* arg){
// hogehoge
}
myFunc((uint8_t[]){0xAA, 0xBB, 0xCC});
なおmemcmp()
など、いくつかの特殊な関数の場合、ただポインタ型にキャストしただけだと "一時的な配列は駄目だよ" とエラーが出てしまうので、その場合はconst
を付けて、ポインタ型定数としてキャストします。
C++
uint8_t* buf = {0xAA, 0xBB, 0xCC};
memcmp(buf, (const uint8_t[]){0xAA, 0xBB, 0xCC}, sizeof(buf));