[C++] Dyrektywa #pragma, nazwy predefiniowane oraz #include

Witam!

Mam kilka pytań dot. dyrektyw preprocesora, bo właśnie skończyłem ten dział w Symfonii C++. Szukałem odpowiedzi na te pytanie w Google, ale po prostu nic nie znalazłem. Może to dlatego, że są zbyt szczegółowe - nie wiem. Przepraszam, za to, że tak się o coś pytam ostatnio często, ale po prostu chciałbym to zrozumieć, a czasem też autor nie opisał czegoś dokładniej.

Pytanie 1.

W nazwach predefiniowanych jest nazwa __NAME__ - jest pod nią zapamiętana nazwa aktualnie kompilowanego pliku (tak mniej więcej jest napisane w książce). Jednak we wszystkich kodach jest zamiast niej użyta nazwa __FILE__. Kiedy zamienię ją na wcześniejszą, czyli na __NAME__ , program się po prostu nie skompiluje… Czy ktoś mógłby opisać tę sprawę dokładniej :?:

1.1

W C++ jest zdefiniowana domyślnie nazwa __cplusplus - czyli kompilator kompiluje w C++. Jest też __STDC__ - jeżeli np. zastosujemy coś takiego:

#if 10 == 10

__STDC__

// instrukcje...

#endif

To w ramach tego bloku, wszystkie instrukcje jakie napotka kompilator będą interpretowane jako instrukcje w C :?: 1.2 W ćwiczeniach pod koniec działu w.w. książce jest takie polecenie aby w programie została wyświetlona instrukcja czy użyto kompilatora C++. Można to zrealizować na kilka sposobów wg mnie, ale już przy jednym dziej się coś dziwnego:

#ifndef __STDC__

cout << "W tym programie uzyto kompilatora C++";

#endif

Ta instrukcja się nie wyświetli… Czyli wychodzi na to, że taka nazwa też jest domyślnie zdefiniowana :o (Jeżeli dam:

#ifdef __cplusplus

cout << "W tym programie uzyto kompilatora C++";

#endif

to oczywiście, się to wyświetli). Pytanie 2. Jest tzw. dyrektywa #pragma. Opisana jest dokładniej tutaj na str. 10: :arrow: http://www.ita.wat.edu.pl/~lgrad/pi/wyklad_c5.pdfZałóżmy, że mamy Borland C++ i wpiszemy coś takiego:

#pragma exit

To czy ktoś mógłby podać przykład momentu w programu, w którym opłaca się zastosowanie takiej dyrektywy :?: Bez przykładu trudno mi sobie to wyobrazić… Pytanie 3.

O jaki sposób “upewnienia się ile wynosi DLUGOSC_BUF” chodziło autorowi. Bo np. na pewno nie o ten:

cout << DLUGOSC_BUF;

Tu bez problemu, możemy dowiedzieć się ile “jakaś rzecz” wynosi…

Istnieje kilka przedefiniowanych makr:

__DATE__ - data w momencie kompilacji

__TIME__ - godzina w momencie kompilacji

__FILE__ - łańcuch, który zawiera nazwę pliku, który aktualnie jest kompilowany przez kompilator

__LINE__ - definiuje numer linijki

__STDC__ - w kompilatorach zgodnych ze standardem ANSI lub nowszym makro to przyjmuje wartość 1

__STDC_VERSION__ - zależnie od poziomu zgodności kompilatora makro przyjmuje różne wartości

Możliwe że autorowi chodziło o uogólnienie, wszystko to wygląda jak __NAME__

Użycie __STDC__ / __cplusplus

#ifdef __STDC__

void err(const char *msg) { printf("%s\n",msg); }

#else

#ifdef __cplusplus

void err(const char *msg) { cout<
#else

#error "Nie wiadomo co to za kod"

#endif

#endif

Stosuje w różnego rodzaju bibliotekach dla inicializacji #pragma startup i finalizacji #pragma exit, chodzi przeważnie o przydzielenie i zwolnienie pamięci.

Upewnić się można na wiele sposobów, między innymi w taki sposób jak podałeś lub policzyć ręcznie lub też jest kilka innych, raczej chodziło o upewnienie się czy DLUGOSC_BUF przypadkiem nie ma wartości 0 lub przekracza wartość MAX_INT

Ta strona powinna Cię zainteresować: http://predef.sourceforge.net/index.php

Nie wiem czy to dobrze rozumiem, ale można to przetłumaczyć na:

Ale jakiego pliku dokładniej :?:

Tego w którym jest to #pragma once

Nie wdawaj się zbytnio w szczegóły jak napisano że coś jest tylko w jednym kompilatorze to pomijaj.

I lepiej wywal tą książkę do śmietnika, znajdź sobie porządną.

#pragma once to strażnik nagłówka. Dodany w Visual C++(resztą w G++ i kompilatorze Intela też jest) po to by nie stosować “standardowego” strażnika(jest o nim w Symfonii, poszukaj).

Tak wiem, o którego chodzi - pewnie o tym mowa:

http://forum.4programmers.net/viewtopic … 5#id520045

zresztą może to nie jest najlepszy przykład, bo on nie działa (działa wtedy, kiedy funkcje2 napiszemy jako inline - jednak dlaczego tak akurat jest to nikt nie może uzasadnić na żadnym forum…)

A wracając do #pragma to gdzie by go wstawić w takim kodzie co w linku jest :?:

Jedyne z czym mi się to kojarzy to taki kod, jednak on jest błędny:

#pragma once


void funkcja1();


void funkcja2()

        {

                cout << "!UWAGA!\t Jestem w 'funkcja2'\t !UWAGA!\n\n";

        }

#pragma once wstawiasz w nagłówku w tym samym miejscu co strażnika.

Co do wielokrotnej definicji funkcja2: doczytaj o inline w Symfonii :wink: Tak się dzieje ponieważ wielokrotnie kompilujesz tą samą funkcję(funkcję o tym samy prototypie) i linker tego nie przepuści ponieważ nie wie której ma użyć. Jeśli zmienisz funkcję na inline wtedy kompilator rozwinie tę funkcje w miejscu wystąpienia i linker nic o niej wiedział nie będzie.

A jak dokładniej - bo mi się to kojarzy z moim (błędnym) przykładem…

EDIT:

OK - już jest. Zapomniałem o inline. Ale niczym się to nie różni od tego standardowego strażnika :?:

Aha - no nareszcie rozumiem, dzięki wielkie :slight_smile: Chodzi mniej więcej o to, że linker też jest “uczulony” na dwie definicje, ale w tym przypadku to “jakby zignoruje” bo kompilacja jest pierwsza i kompilator “usunie” te definicje i wsadzi ich treść do instrukcji, gdzie została wywołana ta właśnie funkcja.

EDIT: Jeszcze chciałbym się zapytać odnośnie tej nazwy __STDC__. Czy można to jakoś inaczej zobrazować z innym kodem niż podał [alex] bo o wskaźnikach się jeszcze nie uczyłem :?:

Przy stosowaniu #pragma once masz pewność, że nie będziesz miał redefinicji makra oznaczającego dołączenie pliku. Tzn. nie będzie czegoś takiego:

//gdzieś w dołączanych plikach...

#define _TST_H_


//dołączany plik tst.h

#ifndef _TST_H_

//tutaj kod się nie skompiluje, ponieważ _TST_H_ jest już zdefiniowane.

#endif

Lecz to się zbyt często nie zdarza :wink:

Ad. EDIT: __STDC__ mówi nam, czy kompilator jest zgodny ze standardem C i niczym więcej :wink:

Nie musisz rozumieć całego, chodziło o to że jeżeli kompilujemy w czystym C to w funkcji err użyj printf(), jeżeli nie ale kompilujemy w C++ to w funkcji err użyj cout, w pozostałych przypadkach wyświetl błąd kompilacji.

@[alex]: __STDC__ jest definiowane jeśli kompilator jest zgodny z ANSI C, a więc równie dobrze może być zdefiniowane również w kompilatorze C++ :wink:

Teraz dopiero przyjrzałem się kodowi podanemu przez [alexa], jest on źle napisany, po co używać “#else #ifdef” jeśli można użyć #elif? Lepszy kod(działający tak jak powinien):

#ifdef __cplusplus //Kompilowane jako C++

void err(const char* msg)

{

   std::cout << msg << std::endl;

}

#else //Jako C

void err(const char* msg)

{

   printf("%s\n", msg);

}

#endif

A nie powinno:

struct T { int x; }; T x;

Nie jest zgodny z ANSI C, powinno być: struct T x; Jednak dla C++ ten kod jest jak najbardziej poprawny. Więc jeżeli kompilator to obsługuje i przy tym ma ustawione __STDC__ to jest to właściwie przekłamaniem.

Po to aby jakoś wyeliminować przypadki kiedy to nie jest C++ ani też ANSI C, no i przy okazji zwrócić uwagę autora tematu na #error

Poczytaj uważnie pytanie na które odpowiedziałem podając ten przykład.

Ad. 1: kompilator to może obsługiwać. VC++ obsługuje, g++ też. Więc to nie ma nic do rzeczy.

Ad. 2: nie widzę związku pomiędzy #error a #elif, oprócz tego, że obie to instrukcje preprocesora.