質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.49%

  • C

    3800questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

  • MacOS(OSX)

    1955questions

    MacOSとは、Appleの開発していたGUI(グラフィカルユーザーインターフェース)を採用したオペレーションシステム(OS)です。Macintoshと共に、市場に出てGUIの普及に大きく貢献しました。

  • GCC

    143questions

    GCCはGNU Compiler Collectionの略です。LinuxのC言語コンパイラのデファクトスタンダードであり、数多くの他言語やプラットフォームサポートもします。

  • Debian

    121questions

    Debianは、Debian GNU/Linux などのOS(オペレーティングシステム)です。

ポインタのキャストを介したconst外しの環境依存について

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 321

tachikoma

score 2458

低レベルプログラミングの書籍の中で、const int型の変数xの中身を書きかえるコードをMac環境で再現できずに。やろうとしている内容は、次の通りです。

変数をconstで修飾しても、絶対安全ではない。それでも変更する方法はあるのだ。const int xという変数について、それを示そう(リスト9-21)。
* それを指すポインタを作る。その型は、const int*となる。
* このポインタを、int*にキャストする。
* 新しいポインタをデリファレンスする。これで新しい値をxに代入できる。

(低レベルプログラミングより抜粋)

リスト9-21のソースコードはこちらのリポジトリにあります。const_cast.c

これをMacの環境で試したところ、変更されるはずの変数xが変更されませんでした。何が原因なのかを調べるために、上記のコードをステップごろに分解したコードを作成しました。

main.c

#include <stdio.h>

int main() {
  const int x = 10;  // ここは同じ
  const int *px = &x;  // xのポインタを作る
  int *py = (int *)px;  // const int*型をint*型にキャストする
  *py = 30;  // 新しいポインタをデリファレンスする。

  printf("  x=%d, &x=%p\n", x, &x);
  printf("*px=%d, px=%p\n", *px, px);
  printf("*py=%d, py=%p\n", *py, py);

  return 0;
}

これをMacのtermianlでコンパイル(gcc main.c)して実行したところ、同じアドレスを示しているにもかかわらず、xの数値のみが変更前の10という値を返しました

  x=10, &x=0x7ffee6feaa38
*px=30, px=0x7ffee6feaa38
*py=30, py=0x7ffee6feaa38

上記のコードをDebian上でコンパイル/実行を行ったところ、書籍の内容の通りconst int型の中身が変更されました。

  x=30, &x=0x7ffde77e3d3c
*px=30, px=0x7ffde77e3d3c
*py=30, py=0x7ffde77e3d3c

この振る舞いの違いは何に起因するのかよく分からずにおります。gccのバージョンやOSの違いで説明がつくものなのでしょうか・・・何かご存知の方がいらっしゃいましたらお教え願えますと幸いです。


OSとgccのバージョンは下記の通りでした。

Mac OSX 10.13.4

$ gcc --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 9.1.0 (clang-902.0.39.1)
Target: x86_64-apple-darwin17.5.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

Debian 9.4

# gcc --version
gcc (Debian 6.3.0-18+deb9u1) 6.3.0 20170516
Copyright (C) 2016 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

長くなりましたが、最後まで目を通していただきありがとうございました。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

+4

アセンブラを見てみるとはっきりすると思いますが、おそらく、
コンパイラの最適化によるものと思われます。

規格ではキャストで const を外して値を変更した際の挙動は未定義です(たしか)。
そこでコンパイラは const 指定された変数を見たとき、
変数の定義を見つけてそのまま値を置き換える、という最適化が可能です。
「未定義なんだから何をしたっていい」→「そんなことは起こらないと仮定して最適化したっていい」、という考え方ですね。
なので

printf("  x=%d, &x=%p\n", x, &x);


printf("  x=%d, &x=%p\n", 10, &x);


に最適化されているのではないかと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/05/20 01:23

    なるほど、ありそうですね。探ってみます。

    キャンセル

checkベストアンサー

+2

せっかくなので自分で確認してみました。

 結果概要

  • Win+msys2 環境だと gcc で 30、clang で 10 と表示された
  • gcc では -O0 のとき実行時にスタックから値を読み込んでいる。-O2 のとき即値で 30 を埋め込んでいる。
  • clang では -O0 でも即値が埋め込まれている
  • つまり、↑で回答したとおりコンパイラの最適化により異なる結果が得られているということ

 環境

$ uname -a
MINGW64_NT-6.3 *** 2.10.0(0.325/5/3) 2018-02-09 15:25 x86_64 Msys

$ gcc --version
gcc.exe (Rev2, Built by MSYS2 project) 7.3.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ clang --version
clang version 5.0.1 (tags/RELEASE_501/final)
Target: x86_64-w64-windows-gnu
Thread model: posix
InstalledDir: C:\msys64\mingw64\bin

 プログラム

質問にある main.c をコピペ

 コンパイル

$ gcc -std=c99 -O2 -masm=intel -S a.c -o gcc-a.S
$ clang -std=c99 -O2 -masm=intel -S a.c -o clang-a.S

 結果

該当部分のみ

# gcc-a.S
lea    rcx, .LC0[rip]  # "  x=%d, &x=%p\12\0"
mov    r8, rbx
mov    edx, 30         # ← 即値を第二引数として渡している
mov    DWORD PTR 44[rsp], 30
call    printf

# clang-a.S
lea    rcx, [rip + .L.str]  # "  x=%d, &x=%p\12\0"
lea    rsi, [rbp + 4]
mov    edx, 10              # ← 即値を第二引数として渡している
mov    r8, rsi
call    printf

gcc では即値 30 を、clang では即値 10 を printf に渡しています。
gcc はポインタの使われ方を解析して、値 10 は一度も使われないということを見抜いて出力コードから 10 の代入を省いていました。
clang は上述の通り、const の書きかえが未定義であることを利用した最適化を行っていました。こちらでは逆に 30 を代入するコードが省かれていました。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/05/20 20:32

    回答の内容を自分の環境でも再現できました。clangとgccの違い、こんなところにでてくるんですね。勉強になりました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.49%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • C

    3800questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

  • MacOS(OSX)

    1955questions

    MacOSとは、Appleの開発していたGUI(グラフィカルユーザーインターフェース)を採用したオペレーションシステム(OS)です。Macintoshと共に、市場に出てGUIの普及に大きく貢献しました。

  • GCC

    143questions

    GCCはGNU Compiler Collectionの略です。LinuxのC言語コンパイラのデファクトスタンダードであり、数多くの他言語やプラットフォームサポートもします。

  • Debian

    121questions

    Debianは、Debian GNU/Linux などのOS(オペレーティングシステム)です。