アラサーからのプログラマー生活

アラサーの新米PGが必死に頑張る成長記録です。あとは旅行とか。

【C言語】現場で実際に使われている初心者殺しのコードの話

こんにちは、堀井です。

今日はC言語を使った開発現場で見た不思議なコードの話をします。
不思議と言ってもC言語初心者の私が全然理解できなかったと言うだけなんですけれど、備忘録的に。

ポインタやdefineマクロについて書かれています。

※:本記事で記載されているコードはpaiza.ioで正常に動作することを確認しています。



defineの話

ところで皆さんはdefine定義はご存知でしょうか。
簡単な使い方だと、とある文字列をとある文字列に置き換える機能として使えて何かと便利です。
実例を見てましょう。

#include <stdio.h>
#define TAX 1.08    //2019年2月現在の税率

int main ( void ) {
    int price = 1280;
    int value = ( int )( ( double )price * TAX );
    printf("支払い価格 = %d円", value);
    return 0;
}

と書くことで、出力結果は

支払い価格 = 1382

となります。
コンパイル時にTAXという文字列を1.08に置き換える機能です。

明示的なキャストを何度かしていますが、C言語には自動型変換の機能があるので以下のように書いても同じ動きをします。

#include <stdio.h>
#define TAX 1.08    //2019年2月現在の税率

int main ( void ) {
    int price = 1280;
    int value = price * TAX ;    // ここを書き換えた
    printf("支払い価格 = %d円", value);
    return 0;
}

まあ、チームで組むならC言語に限らず明示的なキャストを入れたほうがいいかなと思います。
こんな簡単な動きばかりではないので。

define、どう便利なんだ

さて、このdefine定義、何が便利かと言いますと
2行目
#define TAX 1.08
の部分を
#define TAX 1.10
に変更すると税率が上がった時のメンテナンスが非常に楽なんです。
これが全て1.08と書いた場合、税率が上がった際にどの1.08を修正すればいいのか分からなくなりますし、読む側も非常に苦労します。

だからと言って

#define HOGE_EX HOGE
#define HOGE ( 65535 )

みたいな多重定義はやめてほしいなって思いました。
なんの意図があるのかと考えましたが、

#define HOGE_EX HOGE
#define FOO_EX HOGE
#define BAR_EX HOGE
#define HOGE ( 65535 )

みたいに同じ用途で複数の文字列を使いたい、くらいしか考えつきませんでした。
本当にこう言う目的なのでしょうか。よくわかりません。
良い用途をご存知の方、教えていただけると嬉しいです。


defineマクロの使い方

さて簡単な使い方が分かったところで、少し難しいですが便利な使い方を書きます。

#include <stdio.h>
#define TAX 1.08    //2019年2月現在の税率
#define calc( a ) ( a * TAX )

int main ( void ) {
    int price = 1280;
    int value = calc( price );
    printf("支払い価格 = %d円 ", value);
    return 0;
}

出力結果は上記と同じです。

支払い価格 = 1382

別にxじゃなくてもaでもbでもなんでも動きます。
(a * TAX)みたいな短いものでなく、2行以上に渡るものも書けるそうです。

#define calc(a, b) ( a * ( 1 - 0.3 ) < b  \
? a : b )

こんな感じに書きます。
行の最後にバックスラッシュを入れ、その後ろにはスペースも何も入れてはいけないとのこと。
こんな冗長なコードを書くなら別途関数を用意してほしい。

ちなみにVBAだとこうなります。

Dim a as long, _ 
    b as long, _ 
    c as long

_の前後に半角スペースが必要なんですね。

現場で見たdefineマクロとポインタを併用した不思議な使い方

まあ、まずは見てください。

#define foo *(const bool*)&bar

なんだこれ。

他の人に聞いて見たところ、
「恐らくだけれど、読み込まれたファイルのみで定義したい定数なんじゃないか」
とのこと。
とりあえず細かく見てみましょうか・・・。

*(bool*)&bar

まず、定数定義であるconstは見なかったことにしてみます。
そうすると、

  1. barが格納されているアドレスにアクセス
  2. 1の値をbool型にキャスト
  3. 2のアドレスをfooとしている

感じかな・・・。

constを元に戻すと最終的に
fooは、bool型でキャストしたbarの値を定数として定義した物のアドレスを指す
と言うことになるのかな。

ちょっとこれに関しては本当に自信がないです。
詳しい方是非教えてください。



こんな感じで毎日しどろもどろになりながら仕事をしていますがまだまだ元気です。
ただ、規模が大きいだけに変更や仕様追加を加えた時の影響調査が膨大になりすぎていて全然分からない。
いい加減にしてほしい。

そんな想いは抱きつつも生きています。
PG初心者の皆様、一緒に頑張りましょう。
それではまた次回。