Jaka jest różnica między makrem a const w C++?

Zadano mi to pytanie w wywiadzie technicznym:

Jaka jest różnica między const a makrem w C++?

Moja odpowiedź była taka, że makro jest dyrektywą preprocesora i może być trudno debugować aplikację, jeśli używasz makra, ponieważ jest zastępowane wyrażeniem stałym przed kompilacją, podczas gdy const może mieć identyfikator typu i jest łatwe do debugowania.

Czy ktoś mógłby wskazać jakąś inną różnicę i która powinna być preferujesz?

EDIT:

Z dokumentacji IBM dla C++:

Poniżej przedstawiono pewne różnice między #define a kwalifikatorem typu const:

  1. Dyrektywa #define może być użyta do utworzenia nazwy stałej numerycznej, znakowej lub łańcuchowej, podczas gdy obiekt const dowolnego typu może być zadeklarowany.

  2. Obiekt const podlega regułom zakresowym dla zmiennych, podczas gdy stała utworzona za pomocą #define jest nie. W przeciwieństwie do obiektu const, wartość makra nie pojawia się w pośrednim kodzie źródłowym używanym przez kompilator, ponieważ są one rozwijane w wierszu. Rozszerzenie inline sprawia, że wartość makra jest niedostępna dla debuggera.

  3. Makro może być używane w wyrażeniu ciągłym, takim jak Wiązanie tablicy, podczas gdy obiekt const nie może. (Myślę, że na pewno musimy użyć makra do zdefiniowania array_size.

  4. Kompilator nie wpisuje-sprawdza makro, w tym makro argumenty.

Author: Jamal, 2011-06-18

8 answers

Makra i stałe nie są zdalnie tym samym, każda jest czasami odpowiednia dla okoliczności, a Twoja odpowiedź tylko zarysowuje powierzchnię różnicy. Ponadto, C++ ma dwa różne rodzaje stałych.

Stała zdefiniowana za pomocą kwalifikatora const jest najlepiej postrzegana jako zmienna niezmodyfikowana . Ma wszystkie właściwości zmiennej: ma typ, ma rozmiar, ma powiązanie, możesz wziąć jej adres. (Kompilator może zoptymalizować usuwa niektóre z tych właściwości, jeśli może to ujść na sucho: na przykład stałe, których adres nigdy nie jest używany, mogą nie zostać wyemitowane do pliku wykonywalnego. Ale jest to tylko dzięki łasce Zasady as-if.) Jedyną rzeczą, której nie można zrobić z const datum, jest zmiana jego wartości. Stała zdefiniowana przez enum jest nieco inna. Ma typ i rozmiar, ale nie ma powiązania, nie można wziąć jego adresu, a jego typ jest unikalny. Oba są przetwarzane w fazie 7 tłumaczenia, więc to nie może być nic innego jak lvalue lub rvalue. (Przepraszam za żargon w poprzednim zdaniu, ale inaczej musiałbym napisać kilka akapitów.)

Makro ma znacznie mniej ograniczeń: może rozwinąć się do dowolnej sekwencji tokenów, o ile cały program pozostaje dobrze uformowanym programem. Nie ma żadnych właściwości zmiennej. Zastosowanie sizeof lub & do makra może, ale nie musi, zrobić coś użytecznego, w zależności od tego, do czego makro się rozszerza. Makra są czasami makra takie są czasami traktowane jako stałe, ale nie są: "właściwy kompilator" (tj. Faza translacji 7) widzi je jako literały numeryczne.

Ogólnie uważa się za dobrą praktykę, w dzisiejszych czasach nie używać makra, gdy zrobi to stała. Makra nie są zgodne z tymi samymi regułami zakresów, co wszystkie inne identyfikatory, co może być mylące. tak więc również do debuggera. Jednak makra pozwalają ci robić rzeczy, których nie można zrobić w inny sposób, a jeśli musisz zrobić jedną z tych rzeczy, nie wahaj się ich użyć. (Makra, które ciągną swoją wagę, w tym sensie, zazwyczaj nie po prostu rozszerzają się na literały liczbowe, choć nie powiem nigdy.)

EDIT: Oto przykład makra robiącego coś ciekawego. W żaden sposób nie kształtuje ani nie tworzy stałej. Może być sposób na ten sam efekt bez makra (jeśli znasz taki, który nie wymaga stringstreams, byłbym ciekaw, aby usłyszeć o tym!), ale myślę, że jest to dobra ilustracja zarówno mocy, jak i niebezpieczeństwa makr (dla tych ostatnich, zastanów się, co by zrobił, gdyby był używany poza jednym bardzo specyficznym kontekstem...)

static double elapsed()
{ ... }
#define ELAPSED '[' << std::fixed << std::setprecision(2) << elapsed() << "] "

// usage:
for (vector<string>::iterator f = files.begin(); f != files.end(); f++) {
    cout << ELAPSED << "reading file: " << *f << '\n';
    process_file(*f);
}
 24
Author: zwol,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-22 20:29:29

Należy preferować const int sum = 1; nad #define sum 1 z wielu powodów:

Mechanizm Oparty Na Zakresie:

#defines nie respektują zakresów, więc nie ma sposobu na utworzenie przestrzeni nazw z klasą. Podczas gdy zmienne const mogą być skalowane w klasach.

unikanie dziwnych liczb magicznych podczas błędów kompilacji:

Jeśli używasz {[2] } są one zastępowane przez pre-procesor w czasie prekompilacji, więc Jeśli pojawi się błąd podczas kompilacji, będzie to mylące, ponieważ komunikat o błędzie nie będzie odwoływać się do nazwy makra, ale wartość i pojawi się nagła wartość, a tracimy dużo czasu na śledzenie go w kodzie.

łatwość debugowania:

Również z tych samych powodów, podczas gdy debugowanie #define nie pomogłoby naprawdę.
Aby uniknąć obu powyższych sytuacji const będzie lepszym wyborem.

 12
Author: Alok Save,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-18 04:13:47

Inną różnicą jest to, że zmienna const ma pamięć i może być odwołana za pomocą wskaźnika. Makro jest tylko autouzupełnianie , które nastąpi przed kompilacją, stąd nazwa jest tracona podczas kompilacji.

Również makro może być czymś więcej niż stałą. Może to być wyrażenie am lub cokolwiek, co jest poprawne składniowo, nawet cała definicja funkcji.

Makra są używane do przedstawiania wyborów programistycznych, np. rozmiaru stosu; natomiast cosnt służy do przedstawiania rzeczywistych stałe światowe, takie jak wartość Pi lub e.

 2
Author: Xolve,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-18 04:39:34

(Originally posted for static const vs #define - odtwarzanie tutaj, ponieważ to pytanie wydaje się mieć więcej "rozmachu"... daj mi znać, jeśli to niestosowne... )

Plusy i minusy do wszystkiego, w zależności od użycia:

  • consts
    • poprawnie skonfigurowane / identifier clash issues handle nice
    • silny, pojedynczy, określony przez użytkownika Typ
      • możesz spróbować "wpisać" A #define ala #define S std::string("abc"), ale stała pozwala uniknąć powtarzania konstrukcji osobne miejsca pracy w każdym punkcie użycia
    • jedna zasada definicji
    • może pobierać adresy, tworzyć odniesienia do nich const itp.
  • definiuje
    • "globalny" zakres / bardziej podatny na sprzeczne zastosowania, które mogą powodować trudne do rozwiązania problemy z kompilacją i nieoczekiwane wyniki w czasie wykonywania, a nie rozsądne komunikaty o błędach; łagodzenie tego wymaga:
      • długie, niejasne i / lub centralnie skoordynowane identyfikatory oraz dostęp do nie mogą korzystać z domyślnego dopasowania używanej / current/Koenig-looked-up przestrzeni nazw, aliasów przestrzeni nazw itp.
      • użycie wszystkich wielkich liter jest zwykle wymagane i Zarezerwowane dla preprocesora definiuje (ważna wskazówka dla użycia preprocesora w skali przedsiębiorstwa, aby pozostać w zarządzaniu, a których bibliotek stron trzecich można oczekiwać, aby śledzić), których obserwacja implikuje migrację istniejących const lub enum do definicji wiąże się ze zmianą kapitalizacji (a tym samym wpływa na klienta). kod). (Osobiście pierwsza litera enum, ale nie const, więc i tak bym tu trafił - Może Czas To przemyśleć.)
    • możliwe są operacje w czasie kompilacji: konkatenacja ciągów literalnych, stringification (przyjmując ich rozmiar)
      • minusem jest to, że biorąc pod uwagę #define X "x" i pewne użycie klienta ala "pre" X "post", Masz kłopoty, jeśli chcesz lub musisz uczynić x zmienną zmienną runtime, a nie stałą, podczas gdy przejście jest łatwiejsze od const char* lub const std::string ponieważ już zmuszają użytkownika do włączenia operacji konkatenacji.
    • nie można użyć sizeof bezpośrednio na zdefiniowanej stałej liczbowej
    • untyped (GCC nie ostrzega w porównaniu do unsigned)
    • niektóre łańcuchy kompilatorów / linkerów/debugerów mogą nie prezentować identyfikatora, więc zostaniesz zredukowany do patrzenia na "magiczne liczby" (ciągi, cokolwiek...)
    • nie mogę przyjąć adresu
    • podstawiona wartość nie musi być legalna (lub dyskretna) w kontekście gdzie tworzony jest #define, ponieważ jest oceniany w każdym punkcie użycia, dzięki czemu można odwoływać się do jeszcze nie zadeklarowanych obiektów, zależeć od "implementacji", która nie musi być wstępnie dołączona, tworzyć" stałe", takie jak { 1, 2 }, które mogą być użyte do inicjalizacji tablic, lub #define MICROSECONDS *1E-6 itd. ( zdecydowanie Nie polecam tego!)
    • niektóre specjalne rzeczy, takie jak __FILE__ i __LINE__ mogą być włączone do podstawienia makra
  • enums
    • możliwe tylko dla liczby całkowitej wartości
    • poprawnie skonfigurowane / identifier clash issues handle nice
    • mocno wpisana, ale do wystarczająco dużej liczby całkowitej podpisanej lub niepodpisanej, nad którą nie masz kontroli (w C++03)
    • nie mogę przyjąć adresu
    • mocniejsze ograniczenia użycia (np. inkrementacja - template <typename T> void f(T t) { cout << ++t; } nie skompiluje się)
    • typ każdej stałej pobrany z enum enum, więc template <typename T> void f(T) otrzymuje osobną instancję, gdy przekazuje tę samą wartość liczbową z różnych enum, z których wszystkie są różne z dowolnej rzeczywistej instancji f (int).
    • nawet w przypadku typeof, nie można oczekiwać, że numeric_limits dostarczy użytecznego wglądu
    • Nazwa typowa enum może pojawiać się w różnych miejscach w RTTI, komunikatach kompilatora itp. - prawdopodobnie przydatne, możliwe zaciemnienie

Generalnie używam const i uważam je za najbardziej profesjonalną opcję do ogólnego użytku (chociaż inne mają prostotę atrakcyjną dla tego starego leniwego programisty).

 2
Author: Tony Delroy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:33

Makra nie respektują zakresu, a nazwa makra może nie być dostępna dla symbolicznego debuggera. Dan Saks ma dość kompletny artykuł na temat względnych zalet makr (brak), stałych obiektów i stałych wyliczeń. Podobnie jak Stephen Dewhurst, Saks preferuje stałe wyliczania dla wartości całkowitych, ponieważ nie zajmują one pamięci (dokładniej, stałe wyliczania nie mają czasu przechowywania ani powiązania).

 0
Author: Gnawme,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-18 05:19:10

Define może być przedefiniowane, ale const spowoduje błąd kompilatora:

Próbka: źródło: main.cpp

#define int_constance 4
#define int_constance 8 // ok, compiler will warning ( redefine macro)

const int a = 2;
const int a = 4; // redefine -> error

int main(int argc, char** argv)
{
   std::cout << int_constance ; // if remove second #define line, output will be 8

   return 0;
}
 0
Author: Phạm Mạnh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-29 16:56:11

Makra takie jak # define i const są używane w języku programowania C do definiowania stałych.

Zobaczmy różnicę między # define i const:

#define

  1. #define dyrektywa jest dyrektywą preprocesora.
  2. #define może definiować tylko stałą prostą.

Const

  1. const zmienna jest jak normalna zmienna w języku C.
  2. const może definiować stałe złożone też.

Dla różnicy całkowitej: różnica między # define i const w C

 0
Author: Shadab Kazi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-20 06:43:24

Makro zawsze ma typ, na przykład {[0] } jest typu int.

Zaletą zmiennej const nad makrem może być użycie pamięci : w przypadku makra wartość może być duplikowana wszędzie tam, gdzie jest używana, zmienna const nie będzie duplikowana w pamięci. (ale nie jestem pewien tej różnicy)

 -1
Author: BenjaminB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-18 04:16:58