第25問

以下は、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だから……
y0x22221111になります!
正解!それじゃあ、実際の処理をしている部分はどうなってるかしら?
関数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つありますね。
うんうん。pAs32pAs16の2つのポインタが、同じ32ビットのバッファを指しているわね。
あ、2つ目のポインタはuint16_t *ですね。え?これは16ビットでは……?
1つ目のポインタが指している32ビットの値を、2つ目のポインタでは16ビットの値が2つ格納された配列とみなしているわけ。
なるほど!配列だと思えば値を入れ替えられると。
そういうことよ。
そんなテクニックがあるんですねー!
そのテクニックがどうなの?っていうのが今回の問題よ。
えっと、いまいち問題の意味が理解できないんですけど……
そうね。まず今回のプログラムは、ひとまず期待どおりの動作をしているの。
あ、そうか。リリースすると問題が発生するっていう話でしたね。同じプログラムなのに?
そう。開発中とリリース時とでは、コンパイラの設定が違うでしょう?
そっか!ソースファイルが同じでも、コンパイル結果が変わるんですね。
そういうこと!
でも、だからって動作が変わったりします?
変わってしまうかもしれない、危険な書き方があるってことなのよ。
バッファへのアクセス方法に問題がないか考えてみましょう!