yohhoyさんのコメント
ここで挙げられているプログラムは、いわゆる「未定義動作」となります。
http://www.jpcert.or.jp/sc-rules/c-exp05-c.html環境(コンパイラやOS等)によっては出力結果が20になったり18になったり、はたまた実行時クラッシュを引き起こしたりしますが、いずれもこのプログラムとしては「あり得る」実行結果です。
とあったので、逃げ道がないか探しました。
結論は、構造体の実体そのものにはconstをつけず、メンバにconstつければ逃げられるようです
#探索
EXP05-C. const 修飾をキャストではずさない
には、以下のように記述されています。
変数の型にたいするconst修飾をキャストしてはずさないこと。const 修飾をキャストしてはずすと、プログラムが定数値を変更できることになり、未定義の動作につながるおそれがある。
C99 の脚注には以下のように記載されている。
処理系は、volatileでないconstオブジェクトを、読み取り専用記憶域に置いてもよい。さらに、処理系はそのアドレスが使われないならば、そのようなオブジェクトに記憶域を割り付けなくてもよい。
(略)
##自動検出
- LDRA tool suite V 7.6.0 はこのレコメンデーションの違反を検出できる。
- GCC は -Wcast-qual フラグを使用することでこのレコメンデーションの違反を検出できる
- Compass/ROSE はこのレコメンデーションの違反を検出できる。
要は・・・
- 読み取り専用記憶域に置かれない
- GCC の -Wcast-qual フラグを使用して検出されない
であれば、OKであろうと考えた。
#テスト方法
- 対象の変数(st_dest)が読み取り専用記憶域に置かれていないことをprintfによるアドレス
出力とobjdumpにより確認する。 - GCC の-Wcast-qual フラグを使用して検出されない
objdumpの確認結果は、適当になってしまうところがあったので、省略(たぶんOK)。
#逃げ道
#include <stdio.h>
#include <stdlib.h>
/* add - start */
#include <string.h>
#define PRINT_VALUE(a) printf(#a" = %d\n", a);
struct {
const int age;
} st_src = {20}, st_dest = {18};
/* add - end */
int
main (
int argc,
char *argv[ ]
)
{
const int age = 18;
printf ( "%d\n", age );
*( ( int* )( &age ) ) = 20; /* -Wcast-qualにより、ここは検出可能 */
printf ( "%d\n", age );
/* add - start */
PRINT_VALUE(st_dest.age);
/*
st_dest.age = 20; という代入は、
エラー: 読み取り専用メンバ 'age' への代入です
となる。(constの期待動作をする。)
*/
/* -Wcast-qualでも↓は、検出されない・・・ st_src と st_dest自体は「constオブジェクト」ではないので、読み取り専用領域におかれない。 */
memcpy(&st_dest, &st_src, sizeof(st_dest));
PRINT_VALUE(st_dest.age);
/* add - end */
return ( EXIT_SUCCESS );
}
/*
MinGW + gcc- 4.7.2でコンパイル
$ gcc -pedantic -Wall -Wcast-qual test.c
test.c: 関数 'main' 内:
test.c:22:6: 警告: cast discards '__attribute__((const))' qualifier from pointer target type [-Wcast-qual]
実行結果
$ ./a.exe
18
20
st_dest.age = 18
st_dest.age = 20
*/