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

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

新規登録して質問してみよう
ただいま回答率
85.50%
C

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

GCC

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

MacOS(OSX)

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

Debian

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

Q&A

解決済

2回答

3729閲覧

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

tachikoma

総合スコア3601

C

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

GCC

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

MacOS(OSX)

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

Debian

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

1グッド

0クリップ

投稿2018/05/19 15:53

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

変数をconstで修飾しても、絶対安全ではない。それでも変更する方法はあるのだ。const int xという変数について、それを示そう(リスト9-21)。

  • それを指すポインタを作る。その型は、const int*となる。
  • このポインタを、int*にキャストする。
  • 新しいポインタをデリファレンスする。これで新しい値をxに代入できる。

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

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

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

main.c

c

1#include <stdio.h> 2 3int main() { 4 const int x = 10; // ここは同じ 5 const int *px = &x; // xのポインタを作る 6 int *py = (int *)px; // const int*型をint*型にキャストする 7 *py = 30; // 新しいポインタをデリファレンスする。 8 9 printf(" x=%d, &x=%p\n", x, &x); 10 printf("*px=%d, px=%p\n", *px, px); 11 printf("*py=%d, py=%p\n", *py, py); 12 13 return 0; 14}

これを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.

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

TaroToyotomi👍を押しています

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

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

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

C

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

C

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

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

投稿2018/05/19 16:18

pute

総合スコア134

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tachikoma

2018/05/19 16:23

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

0

ベストアンサー

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

結果概要

  • 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

結果

該当部分のみ

nasm

1# gcc-a.S 2lea rcx, .LC0[rip] # " x=%d, &x=%p\12\0" 3mov r8, rbx 4mov edx, 30 # ← 即値を第二引数として渡している 5mov DWORD PTR 44[rsp], 30 6call printf 7 8# clang-a.S 9lea rcx, [rip + .L.str] # " x=%d, &x=%p\12\0" 10lea rsi, [rbp + 4] 11mov edx, 10 # ← 即値を第二引数として渡している 12mov r8, rsi 13call printf

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

投稿2018/05/19 17:17

pute

総合スコア134

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tachikoma

2018/05/20 11:32

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問