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

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

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

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

Q&A

解決済

2回答

696閲覧

#defineの動作

handymatsu

総合スコア31

C

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

0グッド

0クリップ

投稿2021/09/10 05:40

前提・実現したいこと

教師をしていますが,学生が変なプログラムを書いてきたので,シンプルにして実験してみたのですが,プリプロセッサの挙動が自分が考えている結果と異なるのですが,これは仕様でしょうか?

発生している問題・エラーメッセージ

"#define A 10" ですが,Aが変数のように扱われる。そういうものなのでしょうか?

該当のソースコード

C言語

1#include <stdio.h> 2#define TEST A+1 3int main(void){ 4 #define A 10 5 printf("%u\n",TEST); 6 #define A 30 7 printf("%u\n",TEST); 8} 9

試したこと

実行結果ですが,
"#define TEST A+1"
が先に全体に処理されて,二つのprintf文のTESTは,A+1 に置き換わると考えました。
次に,main関数の#define A 10 により,A+1 はすべて10+1に置き換わり,
"#define A 30" では,もうAがないので,何も置き換えないと思ったのですが,
実行結果は,
11
31
となりました。まるでAが変数になっているようです。

補足情報(FW/ツールのバージョンなど)

Arduinoのコンパイラとpaiza.ioで試しました。

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

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

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

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

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

dodox86

2021/09/10 06:12

> Arduinoのコンパイラとpaiza.ioで試しました。 プリプロセスの結果だけを知りたい場合、gccであれば"-E"オプションを使って確認できます。 $ cat t1.c #include <stdio.h> #define TEST A+1 int main(void){ #define A 10 printf("%u\n",TEST); #define A 30 printf("%u\n",TEST); } $ gcc t1.c t1.c: In function ‘main’: t1.c:6: warning: "A" redefined 6 | #define A 30 | t1.c:4: note: this is the location of the previous definition 4 | #define A 10 | $ gcc -E t1.c > t1.c2 でたくさん出力されるので、t1.c2を抜粋すると # 3 "t1.c" int main(void){ printf("%u\n",10 +1); printf("%u\n",30 +1); } ご質問は「この動きは仕様でしょうか?」なので、以上コメントのみです。
thkana

2021/09/10 06:18

本題ではないけれど、#defineで同じシンボルを違う値で再定義すると少なくとも警告になるはず。 ぜひ「きちんと説明できない限り、(たとえ「うまく動いた」としても)警告を無視してはいけない」と教えてやってくださいな。 警告を出さないようにすると、 int main(void){ #define A 10 printf("%u\n",TEST); #undef A #define A 30 printf("%u\n",TEST); } となるはずで、そうすると「A+1 はすべて10+1に置き換わ」るようには見えなくなりませんか。
handymatsu

2021/09/13 00:38

dodox86 様, 大変参考になるレス,ありがとうございました。
handymatsu

2021/09/13 00:41

thkana 様,それが,Warningが出れば良いのですが,Arduinoではでないので戸惑っていました。今調べてみたところ,コンパイラのエラーを出力するオプションがありました。早速試してみたところ,無事にエラーが出ました。デフォルトでこのオプションをオンにしておくようにします。ありがとうございました。 C:\Users\matsu\Documents\Arduino\zzz\zzz.ino:8:0: warning: "A" redefined #define A 30 ^ C:\Users\matsu\Documents\Arduino\zzz\zzz.ino:6:0: note: this is the location of the previous definition #define A 10 ^
thkana

2021/09/13 03:58

とりあえず、教える立場の方のようなので 「エラー」と「警告」は出る意味も対応も異なるかと思いますので、生徒にはしっかり区別して説明してあげてください。
handymatsu

2021/09/13 09:38 編集

Arduinoの開発環境の設定変更は,次回の電算室のアプリアップデート時に全台更新して,出てきたWarningはつぶすように学生さんには指導します。出てきたエラーは(理解した上でどうしようもない場合を除いて)Warningも含めて,きちんと対処させる方針で教育しています。きっかけとなったプログラムを書いた学生さんは,別の問題もあり,補講等で対応します。アドバイス,ありがとうございました。
guest

回答2

0

「仕様でしょうか?」という質問に対しては

  • 言語仕様としては保証されない
  • GCC としては保証している

というのが端的な答えです。

言語仕様 (ISO/IEC 9899:1999) ではマクロの置換要素の並びが同じ場合に限って再定義が許される旨が 6.10.3 にあり、質問の例ではそれに該当しないので結果は (言語仕様上は) 未定義と解釈できます。

Arduino のコンパイラの実体は GCC なわけですが、 GCC としては警告を出した上で新しい定義に置き換えるという挙動は保証しているようです。

投稿2021/09/10 08:47

SaitoAtsushi

総合スコア5466

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

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

otn

2021/09/10 09:45

補足ですが、この回答は、Aの再定義である #define A 30 の前に、 #undef A を入れるべきという指摘でしょうね。
handymatsu

2021/09/10 11:27

GCCですと,Warningが出るようです。そもそもこの学生さん,#defineの意味分かっているのかな・・・。補講でもしてあげないといけないかも・・・。 ありがとうございました。
guest

0

ベストアンサー

プリプロセスが済むと次のようになります。

C

1int main(void){ 2 printf("%u\n",10+1); 3 printf("%u\n",30+1); 4}

コードの前から順に処理されるため、#define が二回行われた場合、二回目とそれ以降では別のものになります。
変数のような実行順ではなく、あくまで出現順です。

投稿2021/09/10 05:47

Zuishin

総合スコア28662

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

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

Zuishin

2021/09/10 05:53

> "#define TEST A+1" が先に全体に処理されて,二つのprintf文のTESTは,A+1 に置き換わると考えました。 置き換わりますが、その A が更に展開されます。一回目は A が 10 に、二回目は 30 になります。
Zuishin

2021/09/10 06:01

ソースコードは前から順に処理されるので、次のような順番になります。 まず TEST が A+1 を表すようになります。 次に A が 10 を指すようになります。 printf("%u\n",TEST); が展開されて printf("%u\n",A+1); になります。 printf("%u\n",A+1); が更に展開されて printf("%u\n",10+1); になります。 次に A が 30 を指すようになります。 printf("%u\n",TEST); が展開されて printf("%u\n",A+1); になります。 printf("%u\n",A+1); が更に展開されて printf("%u\n",30+1); になります。
handymatsu

2021/09/10 11:25

ありがとうございます。置き換えテーブルを常に更新しながら作業しているって事ですね。ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.46%

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

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

質問する

関連した質問