Makrolar tıpkı diğer araçlar gibidir - cinayette kullanılan çekiç kötü değildir çünkü çekiçtir. Kişinin onu bu şekilde kullanması kötüdür. Çivi çakmak istiyorsanız, çekiç mükemmel bir araçtır.
Makroları "kötü" yapan birkaç yön vardır (her birini daha sonra genişleteceğim ve alternatifler önereceğim):
- Makrolarda hata ayıklayamazsınız.
- Makro genişleme garip yan etkilere neden olabilir.
- Makroların "ad alanı" yoktur, bu nedenle başka bir yerde kullanılan bir adla çakışan bir makronuz varsa, istemediğiniz yerde makro değiştirmeleri alırsınız ve bu genellikle garip hata mesajlarına yol açar.
- Makrolar, farkında olmadığınız şeyleri etkileyebilir.
Öyleyse burada biraz genişletelim:
1) Makrolarda hata ayıklanamaz.
Bir sayıya veya dizeye çeviren bir makronuz olduğunda, kaynak kodda makro adı olur ve birçok hata ayıklayıcı, makronun neye dönüştüğünü "göremezsiniz". Yani gerçekte neler olduğunu bilmiyorsunuz.
Değiştirme : Kullanım enum
veyaconst T
"İşlev benzeri" makrolar için, hata ayıklayıcı "bulunduğunuz her kaynak satırı" düzeyinde çalıştığından, makronuz bir ifade veya yüz olursa olsun tek bir ifade gibi hareket edecektir. Neler olduğunu anlamayı zorlaştırıyor.
Değiştirme : İşlevleri kullanın - "hızlı" olması gerekiyorsa satır içi (ancak çok fazla satır içi işlemin iyi bir şey olmadığına dikkat edin)
2) Makro genişletmelerin garip yan etkileri olabilir.
Ünlü olan #define SQUARE(x) ((x) * (x))
ve kullanımıdır x2 = SQUARE(x++)
. Bu x2 = (x++) * (x++);
, geçerli bir kod [1] olsa bile, programcının istediği neredeyse kesinlikle olmayacağı sonucuna götürür . Bir işlev olsaydı, x ++ yapmak iyi olurdu ve x yalnızca bir kez artar.
Başka bir örnek, makrolardaki "if else" dir, diyelim ki bizde:
#define safe_divide(res, x, y) if (y != 0) res = x/y;
ve sonra
if (something) safe_divide(b, a, x);
else printf("Something is not set...");
Aslında tamamen yanlış bir şey oluyor ...
Değiştirme : gerçek işlevler.
3) Makroların ad alanı yoktur
Bir makromuz varsa:
#define begin() x = 0
ve C ++ 'da begin kullanan bazı kodlarımız var:
std::vector<int> v;
... stuff is loaded into v ...
for (std::vector<int>::iterator it = myvector.begin() ; it != myvector.end(); ++it)
std::cout << ' ' << *it;
Şimdi, hangi hata mesajını aldığınızı düşünüyorsunuz ve bir hatayı nerede ararsınız [başka birinin yazdığı bir başlık dosyasında yaşayan başlangıç makrosunu tamamen unuttuğunuzu - ya da bilmediğinizi varsayarsak -? [ve bu makroyu dahil etmeden önce eklerseniz daha da eğlencelidir - kodun kendisine baktığınızda kesinlikle hiçbir anlam ifade etmeyen garip hatalarda boğulursunuz.
Değiştirme : Bir "kural" kadar bir ikame yoktur - makrolar için yalnızca büyük harfli adlar kullanın ve diğer şeyler için hiçbir zaman büyük harfli adlar kullanmayın.
4) Makroların fark etmediğiniz etkileri vardır
Bu işlevi alın:
#define begin() x = 0
#define end() x = 17
... a few thousand lines of stuff here ...
void dostuff()
{
int x = 7;
begin();
... more code using x ...
printf("x=%d\n", x);
end();
}
Şimdi, makroya bakmadan, startın x'i etkilememesi gereken bir fonksiyon olduğunu düşünürsünüz.
Bu tür şeyler ve çok daha karmaşık örnekler gördüm, GERÇEKTEN gününüzü alt üst edebilir!
Değiştirme : x'i ayarlamak için bir makro kullanmayın veya x'i bir bağımsız değişken olarak iletin.
Makro kullanmanın kesinlikle faydalı olduğu zamanlar vardır. Bir örnek, dosya / satır bilgilerini iletmek için bir işlevi makrolarla sarmalamaktır:
#define malloc(x) my_debug_malloc(x, __FILE__, __LINE__)
#define free(x) my_debug_free(x, __FILE__, __LINE__)
Artık my_debug_malloc
kodda normal malloc olarak kullanabiliriz , ancak fazladan argümanları var, bu yüzden sona gelince ve "hangi bellek öğelerinin serbest bırakılmadığını" taradığımızda, tahsisin nerede yapıldığını yazdırabiliriz, böylece programcı sızıntıyı takip edebilir.
[1] Bir değişkeni birden fazla "sıra noktasında" güncellemek tanımsız bir davranış. Bir sıra noktası, bir ifade ile tam olarak aynı değildir, ancak çoğu niyet ve amaç için, onu bu şekilde değerlendirmeliyiz. Bunu yapmak iki kez x++ * x++
güncellenecektir x
, bu tanımsızdır ve muhtemelen farklı sistemlerde farklı değerlere ve farklı sonuç değerlerine yol açacaktır x
.
#pragma
makro değildir.