C ön işlemcisi - C preprocessor

C önişlemci olduğu makro önişlemci için C , Objective-C ve C ++ bilgisayar programlama dilleri . Önişlemci, başlık dosyalarının , makro genişletmelerin, koşullu derlemenin ve satır kontrolünün dahil edilmesi yeteneği sağlar .

Birçok C uygulamasında, çevirinin ilk bölümü olarak derleyici tarafından çağrılan ayrı bir programdır .

Önişlemci yönergelerinin dili C'nin dilbilgisi ile yalnızca zayıf bir şekilde ilişkilidir ve bu nedenle bazen başka tür metin dosyalarını işlemek için kullanılır .

Tarih

Önişlemci, 1973 civarında Alan Snyder'ın ısrarıyla ve ayrıca BCPL ve PL / I'de bulunan dosya dahil etme mekanizmalarının kullanışlılığının tanınmasıyla C'ye tanıtıldı . Orijinal sürümü yalnızca dosyaları içermesine ve basit dize değiştirmeleri gerçekleştirmesine izin verdi: #include ve #define parametresiz makrolar. Bundan kısa bir süre sonra, makroları argümanlar ve koşullu derleme ile birleştirmek için çoğunlukla Mike Lesk ve ardından John Reiser tarafından genişletildi .

C ön işlemcisi , 1959'da Douglas Eastwood ve Douglas McIlroy tarafından başlatılan Bell Laboratuvarlarında uzun bir makro dil geleneğinin parçasıydı .

Aşamalar

Ön işleme, C Standardında belirtilen çevirinin ilk dört (sekiz) aşamasıyla tanımlanır.

  1. Trigraf değişimi: Ön işlemci, trigraf dizilerini temsil ettikleri karakterlerle değiştirir .
  2. Satır ekleme: Kaçan satırsonu dizileriyle devam eden fiziksel kaynak satırları, mantıksal satırlar oluşturmak için birleştirilir .
  3. Tokenizasyon: Önişlemci, sonucu ön işleme jetonları ve boşluklara böler . Yorumları boşlukla değiştirir.
  4. Makro genişletme ve yönerge işleme: Dosya dahil etme ve koşullu derleme dahil olmak üzere ön işleme yönerge satırları yürütülür. Ön işlemci, makroları eşzamanlı olarak genişletiyor ve C standardının 1999 versiyonundan bu yana _Pragma operatörleri yönetiyor .

Dosyaları dahil etme

Ön işlemcinin en yaygın kullanımlarından biri, başka bir dosya eklemektir:

#include <stdio.h>

int main(void)
{
    printf("Hello, world!\n");
    return 0;
}

Önişlemci, satırı #include <stdio.h> 'stdio.h' dosyasının metin içeriğiyle değiştirir ve diğer şeylerin yanı sıra printf() işlevi de açıklar .

Bu, çift tırnak kullanılarak da yazılabilir, örn #include "stdio.h" . Dosya adı açılı parantez içine alınmışsa, dosya standart derleyicide aranır. Dosya adı çift tırnak içine alınmışsa, arama yolu geçerli kaynak dosya dizinini içerecek şekilde genişletilir. C derleyicileri ve programlama ortamlarının tümü, programcının içerme dosyalarının nerede bulunabileceğini tanımlamasına izin veren bir özelliğe sahiptir. Bu, bir makefile kullanılarak parametrelendirilebilen bir komut satırı bayrağı aracılığıyla tanıtılabilir , böylece örneğin farklı işletim sistemleri için farklı bir içerme dosyası kümesi takas edilebilir.

Kural olarak, içerme dosyaları bir .h veya .hpp uzantısıyla adlandırılır. Ancak, bunun gözetilmesine gerek yoktur. Bir sahip dosyalar def uzantısı birden çok kez yer alması tasarlanan dosyaları belirtebilir, aynı içeriği tekrar tekrar genişleyen her seferinde; #include "icon.xbm" bir XBM görüntü dosyasına (aynı zamanda bir C kaynak dosyası olan) gönderme olasılığı yüksektir .

#include genellikle #include korumaların kullanılmasını zorunlu kılar veya #pragma once çift ​​katılımı önler.

Koşullu derleme

İf-else direktifleri #if , #ifdef , #ifndef , #else , #elif ve #endif kullanılabilir koşullu derleme . #ifdef ve #ifndef basit kestirme vardır #if defined(...) ve #if !defined(...) .

#if VERBOSE >= 2
  printf("trace message");
#endif

Microsoft Windows'u hedefleyen çoğu derleyici örtük olarak tanımlar _WIN32 . Bu, önişlemci komutları dahil olmak üzere kodun yalnızca Windows sistemlerini hedeflerken derlenmesini sağlar. Bunun WIN32 yerine birkaç derleyici tanımlamaktadır . _WIN32 Makroyu dolaylı olarak tanımlamayan bu tür derleyiciler için, makroyu kullanarak derleyicinin komut satırında belirtilebilir -D_WIN32 .

#ifdef __unix__ /* __unix__ is usually defined by compilers targeting Unix systems */
# include <unistd.h>
#elif defined _WIN32 /* _WIN32 is usually defined by compilers targeting 32 or 64 bit Windows systems */
# include <windows.h>
#endif

Örnek kod, bir makronun __unix__ tanımlanıp tanımlanmadığını test eder . Öyleyse, dosya <unistd.h> daha sonra eklenir. Aksi takdirde, bunun _WIN32 yerine bir makronun tanımlanıp tanımlanmadığını test eder . Öyleyse, dosya <windows.h> daha sonra eklenir.

Daha karmaşık bir #if örnek, operatörler kullanabilir, örneğin:

#if !(defined __LP64__ || defined __LLP64__) || defined _WIN32 && !defined _WIN64
	// we are compiling for a 32-bit system
#else
	// we are compiling for a 64-bit system
#endif

#error Yönerge kullanılarak çevirinin başarısız olmasına da neden olabilir :

#if RUBY_VERSION == 190
# error 1.9.0 not supported
#endif

Makro tanımlama ve genişletme

İki tür makro vardır, nesne benzeri ve işlev benzeri . Nesne benzeri makrolar parametre almaz; işlev benzeri makrolar yapar (parametreler listesi boş olsa da). Bir tanımlayıcıyı her türden bir makro olarak bildirmek için genel sözdizimi sırasıyla şöyledir:

#define <identifier> <replacement token list>                    // object-like macro
#define <identifier>(<parameter list>) <replacement token list>  // function-like macro, note parameters

Fonksiyon benzeri makro bölüm tanımlayıcı ve birinci, açma, parantez arasında herhangi boşluk olmamalıdır. Boşluk varsa, makro, simge listesine eklenen ilk parantezden başlayarak her şeyle nesne benzeri olarak yorumlanacaktır.

Bir makro tanımı şu şekilde kaldırılabilir #undef :

#undef <identifier>                                              // delete the macro

Tanımlayıcı kaynak kodda her göründüğünde, boş olabilen ikame belirteç listesiyle değiştirilir. İşlev benzeri bir makro olduğu bildirilen bir tanımlayıcı için, yalnızca aşağıdaki simge aynı zamanda makro çağrısının bağımsız değişken listesini başlatan bir sol parantez olduğunda değiştirilir. İşlev benzeri makroların bağımsız değişkenlerle genişletilmesi için izlenen kesin prosedür ince.

Nesne benzeri makrolar, sabitler için sembolik adlar oluşturmak için iyi programlama uygulamasının bir parçası olarak geleneksel olarak kullanılmıştır, örneğin,

#define PI 3.14159

kod boyunca sabit kodlama numaraları yerine . Hem C hem de C ++ 'da, özellikle sayıya bir göstericinin gerekli olduğu durumlarda bir alternatif, const niteleyiciyi global bir değişkene uygulamaktır. Bu, değerin önişlemci tarafından değiştirilmek yerine bellekte depolanmasına neden olur.

İşleve benzer bir makro örneği:

#define RADTODEG(x) ((x) * 57.29578)

Bu, gerektiğinde koda eklenebilen bir radyan -derece dönüşümü tanımlar , yani RADTODEG(34) . Bu, yerinde genişletilir, böylece sabitle tekrarlanan çarpma işlemi kod boyunca gösterilmez. Buradaki makro, derlenmiş bir işlev değil, bir makro olduğunu vurgulamak için tamamen büyük harfle yazılmıştır.

İkincisi x , tek bir değer yerine bir ifade olduğunda hatalı işlem sırası olasılığını önlemek için kendi parantez çifti içine alınır . Örneğin, ifade şu şekilde doğru şekilde genişler ; parantez olmadan çarpma işlemine öncelik verir. RADTODEG(r + 1)((r + 1) * 57.29578)(r + 1 * 57.29578)

Benzer şekilde, dış parantez çifti doğru işlem sırasını korur. Örneğin, şekilde genişler ; parantez olmadan , bölüme öncelik verir. 1 / RADTODEG(r)1 / ((r) * 57.29578)1 / (r) * 57.29578

Genişleme sırası

işlev benzeri makro genişletme aşağıdaki aşamalarda gerçekleşir:

  1. Dizgileştirme işlemleri, bağımsız değişkeninin ikame listesinin metinsel gösterimi ile değiştirilir (genişletme yapılmadan).
  2. Parametreler, değiştirme listesiyle değiştirilir (genişletme yapılmadan).
  3. Birleştirme işlemleri, iki işlenenin birleştirilmiş sonucu ile değiştirilir (sonuçta elde edilen belirteci genişletmeden).
  4. Parametrelerden kaynaklanan belirteçler genişletilir.
  5. Ortaya çıkan belirteçler normal şekilde genişletilir.

Bu şaşırtıcı sonuçlar doğurabilir:

#define HE HI
#define LLO _THERE
#define HELLO "HI THERE"
#define CAT(a,b) a##b
#define XCAT(a,b) CAT(a,b)
#define CALL(fn) fn(HE,LLO)
CAT(HE, LLO) // "HI THERE", because concatenation occurs before normal expansion
XCAT(HE, LLO) // HI_THERE, because the tokens originating from parameters ("HE" and "LLO") are expanded first
CALL(CAT) // "HI THERE", because parameters are expanded first

Özel makrolar ve yönergeler

Ön işleme sırasında bir uygulama tarafından belirli sembollerin tanımlanması gerekir. Bunlar __FILE__ ve __LINE__ geçerli dosya ve satır sayıya genişletmek ön işlemci kendisi tarafından önceden tanımlanmış. Örneğin aşağıdakiler:

// debugging macros so we can pin down message origin at a glance
// is bad
#define WHERESTR  "[file %s, line %d]: "
#define WHEREARG  __FILE__, __LINE__
#define DEBUGPRINT2(...)       fprintf(stderr, __VA_ARGS__)
#define DEBUGPRINT(_fmt, ...)  DEBUGPRINT2(WHERESTR _fmt, WHEREARG, __VA_ARGS__)
// OR
// is good
#define DEBUGPRINT(_fmt, ...)  fprintf(stderr, "[file %s, line %d]: " _fmt, __FILE__, __LINE__, __VA_ARGS__)

  DEBUGPRINT("hey, x=%d\n", x);

x dosya ve satır numarası ile birlikte hata akışının değerini yazdırır ve mesajın hangi satırda üretildiğine hızlı erişim sağlar. Bağımsız WHERESTR değişkenin, onu izleyen dizeyle birleştirildiğine dikkat edin. Değerleri __FILE__ ve __LINE__ manipüle edilebileceğini #line direktifi. #line Yönerge satır numarası ve aşağıdaki hattın dosya adını belirler. Örneğin:

#line 314 "pi.c"
printf("line=%d file=%s\n", __LINE__, __FILE__);

printf işlevini oluşturur:

printf("line=%d file=%s\n", 314, "pi.c");

Kaynak kod debugger'lar ile tanımlanmış kaynak pozisyonuna da atıfta __FILE__ ve __LINE__ . Bu, C tamamen farklı bir dil için bir derleyicinin hedef dili olarak kullanıldığında kaynak kodu hata ayıklamasına izin verir. İlk C Standardı __STDC__ , uygulama ISO Standardına uygunsa makronun 1 ve aksi takdirde 0 __STDC_VERSION__ olarak tanımlanacağını ve makronun , uygulama tarafından desteklenen Standardın sürümünü belirten sayısal bir değişmez bilgi olarak tanımlandığını belirtti. Standart C ++ derleyicileri __cplusplus makroyu destekler . Standart olmayan modda çalışan derleyiciler bu makroları ayarlamamalı veya farklılıkları belirtmek için başkalarını tanımlamalıdır.

Diğer Standart makrolar __DATE__ , güncel tarihi ve __TIME__ güncel saati içerir.

C Standardının ikinci baskısı olan C99 , __func__ içinde bulunduğu fonksiyon tanımının adını içeren destek ekledi , ancak önişlemci C'nin gramerinden bağımsız olduğu için, bu derleyicinin kendisinde bir işleve yerel değişken.

Değişken sayıda bağımsız değişken ( değişken makrolar ) alabilen makrolara C89'da izin verilmez, ancak bir dizi derleyici tarafından tanıtıldı ve C99'da standartlaştırıldı . Değişken makrolar, printf örneğin uyarıları ve hataları günlüğe kaydederken olduğu gibi , değişken sayıda parametre alan işlevlere sarmalayıcılar yazarken özellikle yararlıdır .

C ön işlemcisinin az bilinen bir kullanım modeli X-Makroları olarak bilinir . Bir X-Macro, bir başlık dosyasıdır . Genellikle bunlar geleneksel ".h" yerine ".def" uzantısını kullanır. Bu dosya, "bileşen makroları" olarak adlandırılabilecek benzer makro çağrılarının bir listesini içerir. Dahil etme dosyasına daha sonra tekrar tekrar başvurulur.

Çoğu derleyici, standart olmayan ek makrolar tanımlar, ancak bunlar genellikle yetersiz şekilde belgelenir. Bu makrolar için ortak bir referans, standartları, derleyicileri, işletim sistemlerini, donanım mimarilerini ve hatta temel çalışma zamanı kitaplıklarını tanımlamak için kullanılabilecek "önceden tanımlanmış çeşitli derleyici makrolarını listeleyen Önceden Tanımlanmış C / C ++ Derleyici Makroları projesidir. derleme zamanında ".

Jeton dizilimi

# Operatörü ("Dizgelendirme İşleci" olarak bilinir), bir belirteci , herhangi bir tırnak veya ters eğik çizgiden uygun şekilde kaçarak bir C dizgesine dönüştürür .

Misal:

#define str(s) #s

str(p = "foo\n";) // outputs "p = \"foo\\n\";"
str(\n)           // outputs "\n"

Bir makro bağımsız değişkeninin genişlemesini dizgiselleştirmek istiyorsanız, iki düzeyde makro kullanmanız gerekir:

#define xstr(s) str(s)
#define str(s) #s
#define foo 4

str (foo)  // outputs "foo"
xstr (foo) // outputs "4"

Bir makro bağımsız değişkenini ek metinle birleştiremez ve hepsini bir arada dizilemezsiniz. Bununla birlikte, bir dizi bitişik dize sabiti ve dizgeli argüman yazabilirsiniz: C derleyicisi daha sonra tüm bitişik dize sabitlerini tek bir uzun dizede birleştirir.

Jeton birleştirme

## operatörü ("Token Yapıştırma Operatörü" olarak bilinir) iki jetonu tek bir jetonda birleştirir.

Misal:

#define DECLARE_STRUCT_TYPE(name) typedef struct name##_s name##_t

DECLARE_STRUCT_TYPE(g_object); // Outputs: typedef struct g_object_s g_object_t;

Kullanıcı tanımlı derleme hataları

#error Yönerge hata akımına aracılığıyla bir ileti oluşturur.

#error "error message"

Uygulamalar

Tüm C, C ++ ve Objective-C uygulamaları bir önişlemci sağlar, çünkü ön işleme bu diller için gerekli bir adımdır ve davranışı bu diller için ISO C standardı gibi resmi standartlar tarafından açıklanır.

Uygulamalar kendi uzantılarını ve sapmalarını sağlayabilir ve yazılı standartlara uygunluk derecelerinde değişiklik gösterebilir. Tam davranışları, çağrı sırasında sağlanan komut satırı bayraklarına bağlı olabilir. Örneğin, GNU C ön işlemcisi, belirli bayraklar sağlayarak daha fazla standart uyumlu hale getirilebilir.

Derleyiciye özgü önişlemci özellikleri

#pragma Yönergesi ise derleyici özgü yönergesi derleyici satıcıları kendi amaçları için kullanabilir. Örneğin #pragma , genellikle belirli hata mesajlarının bastırılmasına, yığın ve yığın hata ayıklamasının yönetilmesine vb. İzin vermek için kullanılır. OpenMP paralelleştirme kitaplığı desteğine sahip bir derleyici, bir for döngüyü #pragma omp parallel for .

C99 , kayan nokta uygulamasını kontrol etmek için kullanılan #pragma biçimi alan birkaç standart direktif getirmiştir #pragma STDC ... . Alternatif, makro benzeri form _Pragma(...) da eklendi.

  • Çoğu uygulama trigrafları desteklemez veya varsayılan olarak bunların yerini almaz.
  • Birçok uygulama (örneğin, GNU, Intel, Microsoft ve IBM'in C derleyicileri dahil) çıktıda bir uyarı mesajı yazdırmak için standart olmayan bir yönerge sağlar, ancak derleme sürecini durdurmaz. Tipik bir kullanım, artık kullanımdan kaldırılan ve yalnızca uyumluluk nedenleriyle dahil edilen bazı eski kodların kullanımı hakkında uyarmaktır , örneğin:
    // GNU, Intel and IBM
    #warning "Do not use ABC, which is deprecated. Use XYZ instead."
    
    // Microsoft
    #pragma message("Do not use ABC, which is deprecated. Use XYZ instead.")
    
  • Bazı Unix ön işlemcileri geleneksel olarak , programlamada kullanılan iddialara çok az benzerlik gösteren "iddialar" sağladı .
  • GCC #include_next , aynı ada sahip zincirleme üstbilgileri sağlar .
  • Objective-C önişlemcileri #import , gibidir #include ancak dosyayı yalnızca bir kez içerir. C'deki benzer işlevselliğe sahip yaygın bir satıcı pragması bir kereliğine #pragma'dır .

Diğer kullanımlar

C ön işlemcisi, birlikte verildiği derleyiciden ayrı olarak çağrılabildiğinden, farklı dillerde ayrı olarak kullanılabilir. Dikkate değer örnekler arasında, artık kullanımdan kaldırılan imake sisteminde ve Fortran'ın ön işlenmesi için kullanılması yer alıyor . Bununla birlikte, genel amaçlı bir ön işlemci olarak bu tür kullanım sınırlıdır: giriş dili yeterince C benzeri olmalıdır. GNU Fortran derleyici otomatik olarak belirli dosya uzantıları kullanılırsa Fortran kodu derlemeden önce "geleneksel modu" (aşağıya bakınız) cpp'nin çağırır. Intel , benzer yeteneklere sahip ifort derleyicisiyle kullanılmak üzere bir Fortran ön işlemcisi (fpp) sunar .

CPP ayrıca çoğu montaj dili ve Algol benzeri dillerle de kabul edilebilir şekilde çalışır . Bu, dil sözdiziminin CPP sözdizimiyle çelişmemesini gerektirir; bu # , cpp'nin dize değişmezleri olarak yorumladığı ve bu nedenle yok saydığı satırların olmadığı ve çift tırnakların bundan başka sözdizimsel anlamı olmadığı anlamına gelir. "Geleneksel mod" (ISO C öncesi ön işlemcisi gibi davranır) genellikle daha fazla izin verir ve bu tür bir kullanım için daha uygundur. Daha karmaşık durumlar için GPP olarak adlandırılan C ön işlemcisinin daha esnek bir çeşidi tercih edilir.

C ön işlemcisi Turing-complete değildir , ancak çok yakındır: özyinelemeli hesaplamalar belirtilebilir, ancak gerçekleştirilen özyineleme miktarına sabit bir üst sınır ile. Ancak, C ön işlemcisi genel amaçlı bir programlama dili olarak tasarlanmamıştır ve bu kadar iyi performans göstermez. C ön işlemcisi, yinelemeli makrolar, alıntıya göre seçici genişletme ve koşullu dizge değerlendirmesi gibi bazı diğer önişlemcilerin özelliklerine sahip olmadığından, m4 gibi daha genel bir makro işlemciye kıyasla çok sınırlıdır .

Ayrıca bakınız

Referanslar

Kaynaklar

Dış bağlantılar