• ベストアンサー

const で宣言してるのに、値が変更できてしまう

constを使って、配列のアドレス&値の変更を禁止させたのですが 以下のコードを書くと変更できてしまいます。(テスト環境は、Visual C++ .NET 2003 です。) void CtestDlg::myAAA() { char hoge[] = {1,2,3}; const char const *kari = hoge; //配列kariからは変更不可 myBBB(kari); //配列kariのアドレスを渡す } void CtestDlg::myBBB(const char const *pBuffer) { //pBuffer[1] = 99; //これをするとエラー *(int*)pBuffer = 6;; //キャスト //元のhoge配列の中身も変わっている } const の用途は、「変更できない」じゃなくて「変更しちゃだめ」って注意を促す だけなのでしょうか?? そもそも何がやりたいかと言いますと、 例えば、myAAA関数とmyBBB関数をそれぞれ違う人が記述する場合、 myBBB関数 の作成者がどんなコードを書いても配列hogeが上書きされないように myAAA関数で変更禁止処理をかけたいのですが・・・アドレスを渡す時点でそれは不可能なのでしょうか?

質問者が選んだベストアンサー

  • ベストアンサー
  • MrBan
  • ベストアンサー率53% (331/615)
回答No.2

どちらかといえば、「変更しちゃだめ」です。 そして、通常はそれで十分に変更禁止の役に立ちます。 例示の「強引なキャスト」はルールを破る数少ない手段です。 > *(int*)pBuffer = 6;; //キャスト 変更できるのは、(int*)で*無理やり* constでない型にキャストしているためです。 (int*)のようなキャストはC言語との互換性のために残っている代物で、 constを無理やりはずすことができますので、*そんなコードを書く奴が悪い*ということになります。 # C++用に用意されたキャストでは、const_castという専用のキャストが必要です。 # そして、const外しは未定義動作になる可能性も含め通常やるべきものではありません。 基本的にC++はプログラマは全能である、 コンパイラはありがちなミスを避けるための規制をかけるが、 プログラマは必要に応じて*明示的に*その制限を回避する手段がある、 ということが多いです。 言語はプログラマの意図を表現するための道具であり、 プログラマの意図を正しく表現できない言語は実用的でない、 という思想だと理解しています。

hiroki_xxx
質問者

お礼

詳しい解説ありがとうございます。 偶然見つけたのですが、既知の事実だったのですね・・。 「const外し」で検索したら一杯引っ掛かりました。 const外しはバグではなく、(思想的観点から)敢えて残しているのですね。

全文を見る
すると、全ての回答が全文表示されます。

その他の回答 (4)

  • koko_u_
  • ベストアンサー率18% (459/2509)
回答No.5

>早速の回答ありがとうございます。 >このやり方では保証できないのですね。 どうも勘違いしているような気がしてならない。 保証する責任を負っているのは myBBB() の実装担当者です。 myAAA() 実装担当者ができることは、CtestDlg::myBBB(const char* pBuffer) が「そのインターフェイス設計に反して」pBuffer の指す内容を変更してしまうと知った時点で、 そのバグを myBBB() の実装担当者に修正するよう要求することだけです。

全文を見る
すると、全ての回答が全文表示されます。
  • tokichim
  • ベストアンサー率42% (88/205)
回答No.4

constについては他の回答者さんが回答されているので。 > myBBB関数 の作成者がどんなコードを書いても配列hogeが上書きされないように > myAAA関数で変更禁止処理をかけたいのですが・・・アドレスを渡す時点でそれは不可能なのでしょうか? そうですね、むしろmyAAAの作者はhogeへのポインタをそのまま渡さないようにする。 配列hogeに対応するクラスをつくり、hogeを操作するメソッドを実装してmyBBBの作者にはそれを使ってhogeにアクセスしてもらう。 そうすればmyAAAの作者はメソッドを実装する手間がかかる代わり、自分が公開した処理だけをそのクラスを使う者に許すことが出来ます。 安全性でいえばこれがスマートだと思うのですが、いかがでしょう?

hiroki_xxx
質問者

お礼

間接的に操作させるイメージでいいのでしょうか? 例では、簡単のため同クラスの関数で書いたのですが、それぞれ別クラスの場合 アクセス方向が myAAA⇔myBBB, myAAA→myBBB, myAAA←myBBB の場合でいろんな方法がありそうですね。 参考にしてみたら?というページがありましたら是非教えて下さい。 回答頂けた方どうもありがとうございました。

全文を見る
すると、全ての回答が全文表示されます。
  • tadys
  • ベストアンサー率40% (856/2135)
回答No.3

>myAAA関数で変更禁止処理をかけたいのですが・・・アドレスを渡す時点でそれは不可能なのでしょうか? それはシステムに依存します。 組み込み系では const を ROM 上に配置するするものがありますがその場合は物理的に書き込み出来ません。 また、MMUで書込み禁止に設定できる場合は書き込みをしたときに例外が発生するように出来ます。 そうでない場合には禁止できませんね。 どうしても禁止したいのであればポインタで渡すのをやめるしかないでしょう。

hiroki_xxx
質問者

お礼

組み込み系だと、const宣言時にROMに割り当てられるので上書不可なんですね。 ただ、組み込み開発の環境があったので試しにコンパイルしてみたら 通りました(汗)。一応、警告はでましたが・・・

全文を見る
すると、全ての回答が全文表示されます。
  • koko_u_
  • ベストアンサー率18% (459/2509)
回答No.1

>myBBB関数 の作成者がどんなコードを書いても配列hogeが上書きされないように >myAAA関数で変更禁止処理をかけたい インターフェイスを void CtestDlg::myBBB(const char* pBuffer) と宣言して、myBBB() を実装する以上、 *pBuffer の内容が変更されないようにするのは、「myBBB の作成者の責任」だと思いますよ。

hiroki_xxx
質問者

お礼

早速の回答ありがとうございます。 このやり方では保証できないのですね。

全文を見る
すると、全ての回答が全文表示されます。

関連するQ&A

  • constの位置

    申し訳ありませんが、続けて質問させていただきます。 char const と const char は同じ意味でしょうか。 次のプログラムは配列をconst charと宣言しています。 ary[1]='N'; の行で、意図した通りにコンパイルエラーになります。 これはchar constと宣言しても、同じ意味なんでしょうか。 #include <stdio.h> int main(void) { const char ary[ ] = { 'A', 'B', 'C', '\0' }; ary[1] = 'N'; printf("aryは%s\n", ary ); return 0; } また、 const int a=1; int const a=1; この2つは同じ意味でしょうか。

  • メンバ関数にconstをつけた際の問題

    C++を勉強中の学生です。 以下のようなコードで問題が起こりました。 class TEST{ private: int mArray[10]; //(int a;) public: int* get_mArray(void) const; //(int get_a(void) const;) }; int* TEST::get_mArray(void) const{ return mArray;    //配列の先頭要素の値を受け取りたい。 } //(関係のありそうな部分のみを抜き出しました) このコードにおいて、関数は値を変えない事が明白なので、constをつける必要性はないのですが(癖でつけた際に発生した問題です)、 なぜconstをつけると動作しなくなるのか知りたいです。 //()で囲った部分を有効にし、 int TEST::get_a(void) const{return a;}という関数はエラーにならなかったのですが、どのような違いがあるのでしょうか? 出たエラーは(visual C++ 2010) 'const int [10]' から 'int *' に変換できません。 というものでした。 ポインタとconst関連の部分を手持ちの本で勉強しなおしたのですが、解答を得られなかったのでこちらで質問させてもらいます。

  • static constメンバ変数(配列)の初期化について

    C++初心者です。 constメンバ変数の初期化について教えてください。 クラスの中に、static constメンバ変数(配列)を持ちたいのですが、 <コード1> class hoge { public : hoge(){}; virtual ~hoge(){}; static const int fuga[2] = {1, 2}; }; とすると、VC++ 2005では、 error C2059: 構文エラー : '{' error C2334: '{' の前に予期しないトークンがありました。関数の本体は無視されます というエラーが発生します。 何がいけないのでしょうか? また、下の様にするとOKでした。 <コード2> class hoge { public : hoge(){}; virtual ~hoge(){}; static const int fuga[2]; }; const int hoge::fuga[2] = {1,2}; こうすればコンパイルが通る事は分かったのですが、なぜこんな面倒な事をしないといけないのかが分かりません。 コード1では何がいけないのでしょうか? 以上、よろしくお願いします。

  • 引数付きコンストラクタでコンパイルエラー

    MFCダイアログベースアプリを作成しています。 開発環境はVC++2005です。 CtestDlgクラスのOnBnClickedOk()関数内での処理、 CtestDlg2 dlg( &hoge ); にてCtestDlg2ダイアログ起動し、CString型変数hogeを、 CtestDlg2クラスのコンストラクタに引数として渡したいのですが、 コンパイルエラーが出てしまい、何が原因か分かりません。 解決法をご存知の方おられましたら、お手数ですがご教授お願い致します。 【エラー内容】 error C2664: 'CtestDlg2 ::CtestDlg2(const CtestDlg2 &)' : 1 番目の引数を 'CString *__w64 ' から 'const CtestDlg2 &' に 変換できません。(新しい機能 ; ヘルプを参照) 理由: 'CString *__w64 ' から 'const CtestDlg2' へは 変換できません。 コンストラクタはソース型を持てません、またはコンストラクタの オーバーロードの解決があいまいです。 【CtestDlgクラス内宣言】 void CtestDlg::OnBnClickedOk(){ CString hoge = "aaa"; CtestDlg2 dlg( &hoge ); dlg.DoModal(); } 【CtestDlg2クラスコンストラクタ】 //CtestDlg2::CtestDlg2(CWnd* pParent /*=NULL*/) CtestDlg2::CtestDlg2(CString* st, CWnd* pParent /*=NULL*/) : CDialog(CtestDlg2::IDD, pParent), m_st(st) { } 【CtestDlg2クラスヘッダ内宣言】 public: CtestDlg2::CtestDlg2(CString* st, CWnd* pParent /*=NULL*/); CString* m_st;

  • 不透明なデータのポインタ宣言

    ライブラリ内で使用し、ライブラリの外には、 中身を公開したくない構造体があります。 ライブラリ関数には必ず、ポインタとしてやりとりされます。 これを外部向けに不透明なデータのポインタとして宣言したいのですが・・ typedef const void *PRIVATE_DATA ; このように宣言してみたのですが、 これでは、否応にもvoidポインタとなってしまい、 char *等と暗黙のキャストが行われてしまいます。 たとえば・・ int lib_func( PRIVATE_DATA priv ) ; こんなライブラリ関数に対して、 lib_func( "HELLO" ) ; こんなおかしな呼び出し方が書けてしまいます。 不透明で、他の型へ暗黙のキャストが行われないポインタの宣言の仕方って、 ありませんでしょうか。。? ちなみに、コンパイラは gcc version 3.4.4 です。

  • main の引数には const 付けた方が

    C言語での質問です。 引数を取るような main 関数は int main( int argc, char *argv[]){~} とされていますが、argvの指す文字列を変更する、というのはいくら何でもまずいので、 int main( int argc, const char *argv[]){~} あるいは int main( int argc, const char const * const * argv){~} の方がいいのではないでしょうか? 何故、constを付けない形が出回っているのでしょうか?

  • メンバ変数が変更されそうなconstメンバ関数

    c++のconstメンバ関数についての質問です。 以下のコードの様に、constメンバ関数で メンバ変数を変更しているように見える場合、 本来はどのように記述すべきか? class foo { public: foo(){} foo(int i){ d = i; } void hoge(foo *dest0, foo *dest1 ) const { dest0->d = d*2; dest1->d = d*4; } int d; }; int main() { foo f(1); foo p,q; f.hoge(&p,&q); std::cout << f.d << "\n"; std::cout << p.d << "\n"; std::cout << q.d << "\n"; f.hoge(&f,&q); std::cout << f.d << "\n"; std::cout << q.d << "\n"; return 0; } ちなみに出力期待値は 1 2 4 2 4 とします。 上のコードは 1 2 3 2 8 と出力されますが、このようなことが無いように実装するには どうすべきかという質問です。 dest0->d = d*2; dest1->d = d*4; の所を int i=d; dest0->d = i*2; dest1->d = i*4; とすべき? それともhogeの最初に if( (this == dest0) || (this == dest1) || (dest0== dest1) ){ throw "同じオブジェクトじゃだめ"; } とすべき? どんな書き方が安定でしょうか?

  • qsortの引数について

    以下のプログラムがあります。 int compare( const char **name1, const char **name2 ) { return strcmp( *name1, *name2 ); } int main( void ) { char *names[] = { "rand", "calloc", "malloc" }; int num = sizeof names / sizeof names[0]; qsort( names, num, sizeof( names[0] ), (int (*)(const void *, const void * ))compare ); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~この部分 return 0; } 上の「~~~」の上の部分のqsort関数の第4引数のキャストの意味が 分かりません。なぜ関数の前に引数が書かれるのでしょうか? またintの後にある(*)は「int型のポインタ」と言う意味なのか、 compare関数のポインタなのかも分かりません。 ご回答よろしくおねがいします。

  • 配列のディスプレイという使い方について

    先程は、ありがとうございました もうひとつ、お願い致します 「配列へのポインタ配列」 これは、配列のディスプレイという使い方についての説明です 以下がコードです int hoge1[10]; int hoge2[10]; int hoge3[10]; int *chobi[3]={hoge1,hoge2,hoge3}; まず、コードを見る限り、これは「配列のアドレスへの配列」と言えばよいのではないでしょうか 「ポインタ配列」とは、 *foo() こういうものだと認識しています (「配列へのアドレスの配列」が正しいかもしれません。容赦してください) 「配列へのポインタ配列」という表現が、全く理解できません どなたか説明お願い致します 次に、配列のアドレスを格納するのであれば char 型にするべきではないでしゅうか(アドレスは16進数なので) 最後に、使い方がわかりません 配列のアドレスを配列にして、どのように使うのでしょうか 以上、質問が多いですが、よろしくお願い致します

  • const参照をポインタ引数として渡すには?

    Aというクラスがあって、BはAを継承しているとします。 そこで、Bのconst参照を返却する以下の関数定義があったとします。 const B& getB() { return b; //bはB型 } さらに次の関数があります。 void C(A* a) { //適当な処理 } ここでCを以下のように呼ぼうとするとコンパイルエラーになります。 C(getB()); Cは引数として型Aを求めていますが、BはAを継承しているので、 そのまま渡しても問題ないと思います。 次に、Cは引数としてポインタを求めているのにgetBの戻り値の参照をそのまま 渡しているからまずいのだと思い、以下のようにしました。 const B& hoge = getB(); C(*hoge); //参照をポインタに変換 しかし、さらに型が違うとエラーになります。 どこがまずいのでしょうか? それと、上では参照をポインタに変換するために変数hogeを宣言していますが、 それを省略して一気にやる方法はないでしょうか? C(*getB()); のようなやり方がしたいのですが。