

マクロ関数について勉強しています。

以下のコードを実行すると、エラーが出てしまいます。  
解決法教えてください。

**エラーメッセージ**

[code]

    error: lvalue required as left operand of assignment
     #define ABS(x)   x >= 0 ? x = x * 1 : x = x * -1;
                                             ^
    .\5.c:9:5: note: in expansion of macro 'ABS'
         ABS(x);
         ^~~
    
[/code]

**ソースコード**

[code]

    #include <stdio.h>
    
    #define ABS(x)   x >= 0 ? x = x * 1 : x = x * -1;
    
    int main(){
        int x, y;
        scanf("%d %d", x, y);
        x += y;
        ABS(x);
        printf("%d\n", x);
        return 0;
    }
    
[/code]


CC BY-SA 4.0
2019-12-26T03:26:32.563
61785
2019-12-27T11:26:15.107
2019-12-27T11:26:15.107
3060
35091
question
c
マクロ関数について

  * 勉強中とのことですので直接の回答ではありませんが、三項演算`(condition ? expr1 : expr2)`に書けるのは式です。代入文を書きたいならば`if`文にしましょう。※ 訂正です。代入も式でした。他の人の回答でわかりました。

  * 引数付きのマクロは関数に似ているので、ABS(x)でxの値が書き換わるのは違和感があります。  
マクロは値を表現するようにして、利用するときに代入した方がよいです。

[code]

    x = ABC(x);
    
[/code]

コンパイルしてみたら致命的な警告メッセージも出ていました。※q3.cはソースファイル名です

[code]

    q3.c:8:17: warning: format specifies type 'int *' but the argument has type 'int' [-Wformat]
            scanf("%d %d", x, y);
                   ~~      ^
    q3.c:8:20: warning: format specifies type 'int *' but the argument has type 'int' [-Wformat]
            scanf("%d %d", x, y);
                      ~~      ^
    
[/code]


CC BY-SA 4.0
2019-12-26T04:08:26.903
61792
2019-12-26T05:01:16.403
2019-12-26T05:01:16.403
35558
35558
61785
answer


実はこの `ABS()` は [c++](/questions/tagged/c%2b%2b "'c++' のタグが付いた質問を表示")
ではコンパイルに通ってしまいます。 [c](/questions/tagged/c "'c' のタグが付いた質問を表示") と
[c++](/questions/tagged/c%2b%2b "'c++' のタグが付いた質問を表示")
で仕様の違うところをピンポイントで踏み抜いているという。

[c](/questions/tagged/c "'c' のタグが付いた質問を表示")
において演算子の結合規則（コンパイラが見るとき、どっちを先に処理するか）は  
<https://ja.cppreference.com/w/c/language/operator_precedence>  
これによると三項演算子 `? :` は代入より先に解釈されるので当該 `ABS(x);` は

[code]

    (x >= 0 ? x = x * 1 : x) = x * -1;;
    
[/code]

と解釈されます。代入演算子の左辺が変数でないので怒られています。

[c++](/questions/tagged/c%2b%2b "'c++' のタグが付いた質問を表示") では
<https://ja.cppreference.com/w/cpp/language/operator_precedence> の注２から

[code]

    x >= 0 ? (x = x * 1) : x = x * -1;;
    
[/code]

となるのでコンパイルでき、期待通りに動きます。

で、この質問の回答からずれるので詳細は省略しますが、出来る限り `#define` マクロは避けてください。この簡単そうな `ABS()`
マクロですが、実用的に使おうとすると複数個の問題を抱えています。どれだけ問題（になりそうな）点があるかを読者の皆様も数えてみてください。最低３個は指摘できるはず。

* * *

どう修正すればよいかは、オイラ的には `#define` を使わないでください、になりそうです。ほかの方の回答も「実用レベルで安全に使う」には不合格です（
QAC や PgRelief 等分析ツールがビシバシ指摘してきます）。


CC BY-SA 4.0
2019-12-26T04:20:18.437
61793
2019-12-26T04:32:13.860
2019-12-26T04:32:13.860
8589
8589
61785
answer


マクロ関数を使っていることはエラーとは直接関係ありません。マクロを使わずに書くと`ABS(x);`の部分は次のようになりますが、これでも同じエラーが出るはずです。

[code]

    x >= 0 ? x = x * 1 : x = x * -1;
    ;
    
[/code]

ここで三項演算子の優先順位は代入演算子のそれより高いので

[code]

    (x >= 0 ? (x = x * 1) : x) = x * -1;
    
[/code]

とカッコを付けたかのように解釈されます。`(x >= 0 ? (x = x * 1) :
x)`は代入先として不適当なため、質問にあるようなエラーメッセージが表示されます。

直接的な修正としては、次のようにカッコをつけてやれば良いです。

[code]

    x >= 0 ? (x = x * 1) : (x = x * -1);
    
[/code]

参考: [C の演算子の優先順位 -
cppreference.com](https://ja.cppreference.com/w/c/language/operator_precedence)

あと今回の質問とは無関係ですが

  * `scanf`の引数
  * `ABS`マクロに余分なセミコロンがついている
  * `ABS`という名を使うのであれば、絶対値を返すことだけに集中し、代入は外でやるべき

などいくつか気になる点があります。


CC BY-SA 4.0
2019-12-26T04:22:54.387
61794
2019-12-26T04:22:54.387
13199
61785
answer