Mengapa hanya menentukan makro jika belum ditentukan?

93

Di seluruh basis kode C kami, saya melihat setiap makro didefinisikan dengan cara berikut:

#ifndef BEEPTRIM_PITCH_RATE_DEGPS
#define BEEPTRIM_PITCH_RATE_DEGPS                   0.2f
#endif

#ifndef BEEPTRIM_ROLL_RATE_DEGPS
#define BEEPTRIM_ROLL_RATE_DEGPS                    0.2f
#endif

#ifndef FORCETRIMRELEASE_HOLD_TIME_MS
#define FORCETRIMRELEASE_HOLD_TIME_MS               1000.0f
#endif

#ifndef TRIMSYSTEM_SHEARPIN_BREAKINGFORCE_LBS
#define TRIMSYSTEM_SHEARPIN_BREAKINGFORCE_LBS       50.0f
#endif

Apa alasan melakukan pemeriksaan definisikan ini daripada hanya menentukan makro?

#define BEEPTRIM_PITCH_RATE_DEGPS                   0.2f
#define BEEPTRIM_ROLL_RATE_DEGPS                    0.2f
#define FORCETRIMRELEASE_HOLD_TIME_MS               1000.0f
#define TRIMSYSTEM_SHEARPIN_BREAKINGFORCE_LBS       50.0f

Saya tidak dapat menemukan praktik ini dijelaskan di mana pun di web.

Trevor Hickey
sumber
6
Mengubah konstanta di tempat lain dalam kode dijamin akan berhasil dengan cara ini. Jika di tempat lain seseorang mendefinisikan salah satu dari makro tersebut, mereka tidak akan ditimpa oleh preprocessor saat mengurai file ini.
Enzo Ferber
8
Ini adalah contoh dari prinsip desain WET.
mencolok
Memposting jawaban dengan contoh, coba kompilasi.
Enzo Ferber

Jawaban:

141

Ini memungkinkan Anda untuk mengganti makro saat Anda sedang menyusun:

gcc -DMACRONAME=value

Definisi di file header digunakan sebagai default.

Barmar
sumber
51

Seperti yang saya katakan di komentar, bayangkan situasi ini:

foo.h

#define FOO  4

defs.h

#ifndef FOO
#define FOO 6
#endif

#ifndef BAR
#define BAR 4
#endif

bar.c

#include "foo.h"
#include "defs.h"

#include <stdio.h>

int main(void)
{
    printf("%d%d", FOO, BAR);
    return 0;
}

Akan mencetak 44.

Namun, jika kondisional ifndeftidak ada, hasilnya adalah peringatan kompilasi redefinisi MACRO dan akan dicetak 64.

$ gcc -o bar bar.c
In file included from bar.c:2:0:
defs.h:1:0: warning: "FOO" redefined [enabled by default]
 #define FOO 6
 ^
In file included from bar.c:1:0:
foo.h:1:0: note: this is the location of the previous definition
 #define FOO 4
 ^
Enzo Ferber
sumber
1
Ini khusus untuk kompiler. Mendefinisikan ulang makro yang mirip objek adalah ilegal kecuali jika definisi ulangnya "sama" (ada spesifikasi yang lebih teknis untuk itu, tetapi tidak penting di sini). Kode ilegal memerlukan diagnostik dan, setelah mengeluarkan diagnostik (di sini peringatan), compiler bebas untuk melakukan apa saja, termasuk mengkompilasi kode dengan hasil khusus implementasi.
Pete Becker
7
Jika Anda memiliki def yang bertentangan untuk makro yang sama, bukankah Anda lebih suka mendapatkan peringatan dalam banyak kasus? Daripada menggunakan definisi pertama secara diam-diam (karena definisi kedua menggunakan ifdefuntuk menghindari definisi ulang).
Peter Cordes
@PeterCordes Sering kali, definisi di bawah #infdefs digunakan sebagai nilai "fallback" atau "default". Pada dasarnya, "jika pengguna mengkonfigurasinya, baiklah. Jika tidak, mari gunakan nilai default."
Angew tidak lagi bangga dengan SO
@Angew: Oke, jadi jika Anda memiliki beberapa #definesdi header perpustakaan yang merupakan bagian dari ABI perpustakaan, Anda tidak boleh menggabungkannya #ifndef. (Atau lebih baik, gunakan enum). Saya hanya ingin memperjelas bahwa #ifndefhanya pantas jika memiliki definisi khusus untuk sesuatu dalam satu unit kompilasi tetapi tidak yang lain tidak apa-apa. Jika a.cmenyertakan header dalam urutan yang berbeda dari b.c, mereka mungkin mendapatkan definisi yang berbeda dari max(a,b), dan salah satu dari definisi tersebut mungkin putus dengan max(i++, x), tetapi yang lain mungkin menggunakan temporaries dalam ekspresi-pernyataan GNU. Setidaknya masih membingungkan!
Peter Cordes
@PeterCordes Apa yang ingin saya lakukan dalam kasus itu adalah#ifdef FOO #error FOO already defined! #endif #define FOO x
Cole Johnson
17

Saya tidak tahu konteksnya tetapi ini dapat digunakan untuk memberikan ketersediaan kepada pengguna untuk menimpa nilai yang ditetapkan oleh definisi makro tersebut. Jika pengguna secara eksplisit menentukan nilai yang berbeda untuk salah satu makro tersebut, itu akan digunakan sebagai ganti nilai yang digunakan di sini.

Misalnya di g ++ Anda dapat menggunakan -Dtanda tersebut selama kompilasi untuk meneruskan nilai ke makro.

Ivaylo Strandjev
sumber
14

Ini dilakukan agar pengguna file header dapat mengganti definisi dari kodenya atau dari flag -D compiler.


sumber
7

Setiap proyek C berada di beberapa file sumber. Saat mengerjakan satu file sumber, pemeriksaan tampaknya (dan sebenarnya) tidak ada gunanya, tetapi saat mengerjakan proyek C besar, sebaiknya periksa definisi yang ada sebelum menentukan konstanta. Idenya sederhana: Anda memerlukan konstanta dalam file sumber spesifik tersebut, tetapi mungkin sudah ditentukan di file lain.

George
sumber
2

Anda bisa memikirkan tentang kerangka kerja / pustaka yang memberi pengguna preset default yang memungkinkan pengguna untuk mengkompilasi dan mengerjakannya. Definisi tersebut tersebar di file yang berbeda dan pengguna akhir disarankan untuk menyertakan file config.h itu di mana dia dapat mengkonfigurasi nilainya. Jika pengguna lupa beberapa mendefinisikan sistem dapat terus bekerja karena preset.

LP
sumber
1

Menggunakan

#ifndef BEEPTRIM_PITCH_RATE_DEGPS
#define BEEPTRIM_PITCH_RATE_DEGPS                   0.2f
#endif

memungkinkan pengguna untuk menentukan nilai makro menggunakan argumen baris perintah (dalam gcc / clang / VS) -DBEEPTRIM_PITCH_RATE_DEGPS=0.3f.

Ada alasan penting lainnya. Merupakan kesalahan untuk mendefinisikan ulang makro preprocessor secara berbeda. Lihat jawaban ini untuk pertanyaan SO lainnya . Tanpa #ifndefpemeriksaan, kompilator akan menghasilkan kesalahan jika -DBEEPTRIM_PITCH_RATE_DEGPS=0.3fdigunakan sebagai argumen baris perintah dalam pemanggilan kompilator.

R Sahu
sumber