以下は、32ビットの値の上位16ビット、下位16ビットを入れ替えるプログラムです。どうやら期待どおりに動作しているようなのですが、このままリリースしてしまうと問題が発生するかもしれません。
何が問題なのか分かりますか?
main.c
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
uint32_t ReversedHighLow32Helper(uint32_t n, uint32_t *pAs32, uint16_t *pAs16) {
*pAs32 = n;
uint16_t temp = pAs16[0];
pAs16[0] = pAs16[1];
pAs16[1] = temp;
return *pAs32;
}
uint32_t ReversedHighLow32(uint32_t n) {
uint32_t buffer;
return ReversedHighLow32Helper(n, &buffer, (uint16_t *)&buffer);
}
int main(void) {
uint32_t x = 0x11112222;
uint32_t y = ReversedHighLow32(x);
printf("x = 0x%08x\n", x);
printf("y = 0x%08x\n", y);
return EXIT_SUCCESS;
}
期待される実行結果
x = 0x11112222
y = 0x22221111
ちょっと複雑なプログラムだけど、解読できるかしら?
やってみます!
えっと、32ビットの値というのは、
main()
関数にあるx
のことかな。
uint32_t x = 0x11112222;
uint32_t y = ReversedHighLow32(x);
この
x
の値の上位16ビットと下位16ビットを入れ替えて、y
に代入してるんですね。
そうね。
x
の値は16進数で0x11112222
だから……
y
は0x22221111
になります!
正解!それじゃあ、実際の処理をしている部分はどうなってるかしら?
関数
ReversedHighLow32()
の中身ですね。
あれ?また別の関数を呼び出してる。この32ビットの変数
buffer
は……バッファ?
uint32_t ReversedHighLow32(uint32_t n) {
uint32_t buffer;
return ReversedHighLow32Helper(n, &buffer, (uint16_t *)&buffer);
}
で、この関数が呼ばれるのか。
uint32_t ReversedHighLow32Helper(uint32_t n, uint32_t *pAs32, uint16_t *pAs16) {
*pAs32 = n;
uint16_t temp = pAs16[0];
pAs16[0] = pAs16[1];
pAs16[1] = temp;
return *pAs32;
}
えっと、ここの引数は
n
が入れ替え前の値で、それにバッファへのポインタが2つありますね。
うんうん。
pAs32
とpAs16
の2つのポインタが、同じ32ビットのバッファを指しているわね。
あ、2つ目のポインタは
uint16_t *
ですね。え?これは16ビットでは……?
1つ目のポインタが指している32ビットの値を、2つ目のポインタでは16ビットの値が2つ格納された配列とみなしているわけ。
なるほど!配列だと思えば値を入れ替えられると。
そういうことよ。
そんなテクニックがあるんですねー!
そのテクニックがどうなの?っていうのが今回の問題よ。
えっと、いまいち問題の意味が理解できないんですけど……
そうね。まず今回のプログラムは、ひとまず期待どおりの動作をしているの。
あ、そうか。リリースすると問題が発生するっていう話でしたね。同じプログラムなのに?
そう。開発中とリリース時とでは、コンパイラの設定が違うでしょう?
そっか!ソースファイルが同じでも、コンパイル結果が変わるんですね。
そういうこと!
でも、だからって動作が変わったりします?
変わってしまうかもしれない、危険な書き方があるってことなのよ。
バッファへのアクセス方法に問題がないか考えてみましょう!