Do czego przydatne są makra C?

Napisałem trochę C , i mogę go przeczytać wystarczająco dobrze, aby uzyskać ogólne pojęcie o tym, co robi, ale za każdym razem, gdy spotkałem się z makrem, to rzuca mnie całkowicie. W końcu muszę pamiętać, co to jest makro i zastąpić go w głowie, jak czytam. Te, które napotkałem, które były intuicyjne i łatwe do zrozumienia, zawsze były jak małe mini funkcje, więc zawsze zastanawiałem się, dlaczego nie są tylko funkcjami.

Potrafię zrozumieć potrzebę definiowanie różnych typów kompilacji dla debugowania lub wieloplatformowych kompilacji w preprocesorze, ale możliwość definiowania dowolnych podstawień wydaje się być przydatna tylko w celu uczynienia trudnego języka jeszcze trudniejszym do zrozumienia.

Dlaczego wprowadzono tak złożony preprocesor dla C ? A czy ktoś ma Przykład użycia go, który pozwoli mi zrozumieć, dlaczego nadal wydaje się być używany do celów innych niż proste, jeśli # debug style warunkowe Kompilacje?

Edit:

Po przeczytaniu wielu odpowiedzi nadal tego nie rozumiem. Najczęstszą odpowiedzią jest kod inline. Jeśli słowo kluczowe inline tego nie robi, to albo ma dobry powód, aby tego nie robić, albo implementacja wymaga naprawy. Nie rozumiem, dlaczego potrzebny jest zupełnie inny mechanizm, który oznacza " naprawdę inline ten kod "(poza tym, że kod został napisany przed inline był wokół). Nie rozumiem też pomysłu, o którym wspomniano, że " jeśli jest zbyt głupi do umieszczenia w funkcji". Z pewnością każdy fragment kodu, który pobiera dane wejściowe i wytwarza dane wyjściowe, najlepiej umieścić w funkcji. Myślę, że może tego nie rozumiem, ponieważ nie jestem przyzwyczajony do mikro optymalizacji pisania C , ale preprocesor wydaje się być złożonym rozwiązaniem kilku prostych problemów.

Author: PointerToConstantChar, 2009-03-17

18 answers

W końcu muszę pamiętać, co to jest makro i zastąpić go w mojej głowie, jak czytam.

Wydaje się to źle odbijać się na nazewnictwie makr. Zakładam, że nie musiałbyś emulować preprocesora, gdyby był to makro log_function_entry().

Te, które napotkałem, które były intuicyjne i łatwe do zrozumienia, zawsze były jak małe mini funkcje, więc zawsze zastanawiałem się, dlaczego nie są tylko funkcjami.

Zazwyczaj powinny być, chyba, że muszą działać na ogólnych parametrach.

#define max(a,b) ((a)<(b)?(b):(a))

Będzie działać na dowolnym typie z operatorem <.

Więcej niż tylko funkcje, makra umożliwiają wykonywanie operacji za pomocą symboli w pliku źródłowym. Oznacza to, że możesz utworzyć nową nazwę zmiennej lub odwołać się do pliku źródłowego i numeru linii, na którym znajduje się Makro.

W C99 makra pozwalają również na wywołanie różnych funkcji, takich jak printf

#define log_message(guard,format,...) \
   if (guard) printf("%s:%d: " format "\n", __FILE__, __LINE__,__VA_ARGS_);

log_message( foo == 7, "x %d", x)

W którym format działa jak printf. Jeśli strażnik jest prawdziwy, wysyła wiadomość wraz z plikiem i numerem linii, z którego została wydrukowana wiadomość. Gdyby było to wywołanie funkcji, nie wiedziałby z jakiego pliku i linii go wywołałeś, a użycie vaprintf byłoby nieco bardziej pracochłonne.

 52
Author: Pete Kirkham,
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
2015-03-24 10:30:20

Ten fragment w zasadzie podsumowuje moje zdanie na ten temat, porównując kilka sposobów użycia makr C i jak je zaimplementować w D.

Skopiowane z DigitalMars.com

Kiedy wynaleziono C, kompilator technologia była prymitywna. Instalowanie a tekstowy preprocesor makra na przodzie koniec był prosty i łatwy sposób aby dodać wiele zaawansowanych funkcji. Na zwiększenie rozmiaru i złożoności programy zilustrowały, że te funkcje pochodzą z wielu nieodłącznych problemy. D nie ma preprocesora; ale D zapewnia bardziej skalowalny oznacza rozwiązanie tego samego problemy.

Makra

Makra preprocesora dodają potężne funkcje i elastyczność C. Ale mają minusy:

  • makra nie mają pojęcia zakresu; są ważne od punktu definicji do końca źródła. Przecięli pokrój .pliki h, zagnieżdżony kod itp. Gdy #include ' ING tysiące linii definicji makr, staje się problematyczne, aby uniknąć przypadkowych rozszerzeń makr.
  • makra nie są znane debuggerowi. Próba debugowania programu z danymi symbolicznymi jest podważana przez debuggera, który wie tylko o rozszerzeniach makr, a nie o samych makrach.
  • makra uniemożliwiają tokenizację kodu źródłowego, ponieważ wcześniejsza zmiana makra może dowolnie ponowić tokeny.
  • czysto tekstowa podstawa makr prowadzi do arbitralności i niespójności użycie, tworzenie kodu za pomocą makr podatnych na błędy. (Niektóre próby rozwiązania tego problemu wprowadzono szablonami w C++.)
  • makra są nadal używane w celu uzupełnienia braków w ekspresji języka, takich jak" wrappery " wokół plików nagłówkowych.
Poniżej znajduje się wyliczenie typowych zastosowań makr i odpowiadająca im Funkcja W D:]}
  1. Definiowanie stałych literalnych:

    • Preprocesor C Sposób

      #define VALUE 5
      
    • The D Way

      const int VALUE = 5;
      
  2. Tworzenie listy wartości lub FLAG:

    • Sposób C Preprocesora

      int flags:
      #define FLAG_X  0x1
      #define FLAG_Y  0x2
      #define FLAG_Z  0x4
      ...
      flags |= FLAG_X;
      
    • The D Way

      enum FLAGS { X = 0x1, Y = 0x2, Z = 0x4 };
      FLAGS flags;
      ...
      flags |= FLAGS.X;
      
  3. Ustawianie funkcji wywołujących konwencje:

    • Sposób C Preprocesora

      #ifndef _CRTAPI1
      #define _CRTAPI1 __cdecl
      #endif
      #ifndef _CRTAPI2
      #define _CRTAPI2 __cdecl
      #endif
      
      int _CRTAPI2 func();
      
    • The D Way

      Wywołujące konwencje mogą być określone w blokach, więc nie ma trzeba ją zmienić dla każdej funkcji:

      extern (Windows)
      {
          int onefunc();
          int anotherfunc();
      }
      
  4. Proste programowanie ogólne:

    • Sposób C Preprocesora

      Wybór funkcji do użycia na podstawie podstawiania tekstu:

      #ifdef UNICODE
      int getValueW(wchar_t *p);
      #define getValue getValueW
      #else
      int getValueA(char *p);
      #define getValue getValueA
      #endif
      
    • The D Way

      D włącza deklaracje symboli, które są aliasami innych symboli:

      version (UNICODE)
      {
          int getValueW(wchar[] p);
          alias getValueW getValue;
      }
      else
      {
          int getValueA(char[] p);
          alias getValueA getValue;
      }
      
Więcej przykładów można znaleźć na stronie internetowej DigitalMars .
 16
Author: Brad Gilbert,
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
2009-03-18 00:26:56

Są językiem programowania (prostszym) na szczycie C, więc są przydatne do wykonywania metaprogramowania w czasie kompilacji... innymi słowy, możesz napisać kod makra, który generuje kod C w mniejszej liczbie wierszy i czasu, który zajmie napisanie go bezpośrednio w C.

Są również bardzo przydatne do zapisu wyrażeń "funkcyjnych", które są "polimorficzne" lub "przeciążone"; np. makro max zdefiniowane jako:

#define max(a,b) ((a)>(b)?(a):(b))

Jest przydatna dla dowolnego typu liczbowego, a w C nie można napisz:

int max(int a, int b) {return a>b?a:b;}
float max(float a, float b) {return a>b?a:b;}
double max(double a, double b) {return a>b?a:b;}
...

Nawet jeśli chcesz, ponieważ nie możesz przeciążać funkcji.

I nie wspominając o kompilacji warunkowej i Pliku zawierającym (które są również częścią języka makr)...

 13
Author: fortran,
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
2009-05-20 10:22:20

Makra pozwalają komuś modyfikować zachowanie programu w czasie kompilacji. Rozważ to:

  • stałe C umożliwiają ustalanie zachowania programu w czasie tworzenia
  • zmienne C pozwalają na modyfikację zachowania programu w czasie wykonywania
  • makra C pozwalają modyfikować zachowanie programu w czasie kompilacji

W czasie kompilacji oznacza, że NIEUŻYWANY KOD nie trafi nawet do pliku binarnego i że proces budowania może modyfikować wartości, o ile jest zintegrowany z makro preprocesor. Przykład: make ARCH = arm (zakłada przekierowanie definicji makra jako CC-DARCH=arm)

Proste przykłady: (z limitów glibc.h, zdefiniuj największą wartość long)

#if __WORDSIZE == 64
#define LONG_MAX 9223372036854775807L
#else
#define LONG_MAX 2147483647L
#endif

Sprawdza (używając # define _ _ WORDSIZE) podczas kompilacji, jeśli kompilujemy dla 32 lub 64 bitów. W przypadku multilib toolchain, użycie parametrów-m32 i - M64 może automatycznie zmienić rozmiar bitów.

(żądanie wersji POSIX)

#define _POSIX_C_SOURCE 200809L

Żądania podczas kompilacji obsługa POSIX 2008. Na biblioteka standardowa może obsługiwać wiele (niekompatybilnych) standardów, ale z tą definicją dostarczy poprawne prototypy funkcji (przykład: getline (), no gets (), itd.). Jeśli biblioteka nie obsługuje standardu, może dać błąd #podczas kompilacji, zamiast zawieszać się podczas wykonywania, na przykład.

(hardcoded path)

#ifndef LIBRARY_PATH
#define LIBRARY_PATH "/usr/lib"
#endif

Definiuje, w czasie kompilacji, katalog hardcode. Można zmienić na przykład-DLIBRARY_PATH= / home/user / lib. Gdyby to był const char *, jak skonfigurować go podczas kompilacji ?

(pthread.h, złożone definicje w czasie kompilacji)

# define PTHREAD_MUTEX_INITIALIZER \
  { { 0, 0, 0, 0, 0, 0, { 0, 0 } } }

Można zadeklarować duże fragmenty tekstu, które w przeciwnym razie nie byłyby uproszczone (zawsze w czasie kompilacji). Nie można tego zrobić z funkcjami lub stałymi(w czasie kompilacji).

Aby uniknąć naprawdę komplikowania rzeczy i aby uniknąć sugerowania słabych stylów kodowania, Nie będę podawał przykładu kodu, który kompiluje się w różnych, niekompatybilnych, działających systemy. Użyj do tego swojego systemu cross build, ale powinno być jasne, że preprocesor pozwala na to bez pomocy systemu build, bez przerywania kompilacji z powodu braku interfejsów.

Na koniec pomyśl o znaczeniu kompilacji warunkowej w systemach wbudowanych, gdzie prędkość procesora i pamięć są ograniczone, a systemy są bardzo niejednorodne.

Teraz, jeśli zapytasz, czy możliwe jest zastąpienie wszystkich stałych makr definicjami i wywołaniami funkcji odpowiednimi definicje ? Odpowiedź brzmi tak, ale nie spowoduje to po prostu, że potrzeba zmiany zachowania programu podczas kompilacji zniknie. Preprocesor nadal byłby wymagany.

 12
Author: hdante,
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-11-09 07:00:33

Pamiętaj, że makra (i pre-procesor) pochodzą z najwcześniejszych dni C. kiedyś były jedynym sposobem wykonywania inline ' funkcji '(ponieważ, oczywiście, inline jest bardzo niedawnym słowem kluczowym), i nadal są jedynym sposobem, aby wymusić coś do inline.

Makra są również jedynym sposobem na wykonanie takich trików jak wstawianie pliku i linii do stałych łańcuchów podczas kompilacji.

W dzisiejszych czasach wiele rzeczy, które makra były jedynym sposobem na zrobienie, jest lepiej obsługiwanych poprzez nowsze mechanizmy. Ale wciąż mają swoje miejsce, od czasu do czasu.

 11
Author: Michael Kohne,
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
2009-03-17 11:39:07

Oprócz inliningu dla wydajności i kompilacji warunkowej, makra mogą być używane do podniesienia poziomu abstrakcji niskopoziomowego kodu C. C tak naprawdę nie izoluje Cię od szczegółów dotyczących zarządzania pamięcią i zasobami oraz dokładnego układu danych i obsługuje bardzo ograniczone formy ukrywania informacji i inne mechanizmy zarządzania dużymi systemami. Dzięki makrom nie jesteś już ograniczony do używania tylko konstrukcji bazowych w języku C: możesz definiować własne struktury danych i konstrukcji kodowania (w tym klas i szablonów!), pisząc jeszcze nominalnie C!

Makra preprocesora oferują język Turing-complete wykonywany w czasie kompilacji. Jednym z imponujących (i nieco przerażających) przykładów jest koniec po stronie C++: preprocesor Boost biblioteka używa C99/C++98 preprocesor do budowania (względnie) bezpiecznych konstrukcji programistycznych, które następnie są rozszerzane na dowolne podstawowe deklaracje i Kod wpisujesz, czy C czy c++.

W praktyce zalecałbym programowanie preprocesorowe jako ostateczność, gdy nie masz swobody korzystania z konstrukcji wysokiego poziomu w bezpieczniejszych językach. Ale czasami dobrze jest wiedzieć, co możesz zrobić, jeśli twoje plecy są przy ścianie, a łasice się zbliżają...!

 8
Author: Pontus Gagge,
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
2009-03-17 12:22:43

From Głupoty Komputerowe :

Widziałem ten fragment kodu w wielu darmowych programach do gier dla Uniksa:

/*
* Wartości bitowe.
*/
# define BIT_0 1
# define BIT_1 2
# define BIT_2 4
# define BIT_3 8
# define BIT_4 16
# define BIT_5 32
# define BIT_6 64
# define BIT_7 128
# define BIT_8 256
# define BIT_9 512
# define BIT_10 1024
# define BIT_11 2048
# define BIT_12 4096
# define BIT_13 8192
# define BIT_14 16384
# define BIT_15 32768
# define BIT_16 65536
# define BIT_17 131072
# define BIT_18 262144
# define BIT_19 524288
# define BIT_20 1048576
# define BIT_21 2097152
# define BIT_22 4194304
# define BIT_23 8388608
# define BIT_24 16777216
# define BIT_25 33554432
# define BIT_26 67108864
# define BIT_27 134217728
# define BIT_28 268435456
# define BIT_29 536870912
# define BIT_30 1073741824
# define BIT_31 2147483648

O wiele łatwiejszym sposobem osiągnięcia tego jest:

#define BIT_0 0x00000001
# define BIT_1 0X00000002
# define BIT_2 0X00000004
# define BIT_3 0x00000008
# define BIT_4 0X00000010
...
# define BIT_28 0x10000000
# define BIT_29 0x20000000
# define BIT_30 0X40000000
# define BIT_31 0x80000000

Łatwiejszym sposobem jest pozwolić kompilatorowi wykonać obliczenia:

#define BIT_0 (1)
# define BIT_1 (1 # define BIT_2 (1 # define BIT_3 (1 # define BIT_4 (1 ...
# define BIT_28 (1 # define BIT_29 (1 # define BIT_30 (1 # define BIT_31 (1

Ale po co zadawać sobie trud definiowania 32 stałych? Język C ma również sparametryzowane makra. Wszystko czego naprawdę potrzebujesz to:

#define BIT(x) (1

W każdym razie, zastanawiam się, czy facet, który napisał oryginalny kod użył kalkulatora, czy po prostu obliczył to wszystko na papierze.

To tylko jedno z możliwych zastosowań makr.

 7
Author: Niet the Dark Absol,
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-11-09 07:04:55

Jednym z przypadków, w których makra naprawdę świecą, jest generowanie kodu z nimi.

Pracowałem kiedyś na starym systemie C++, który używał systemu wtyczek z własnym sposobem przekazywania parametrów do wtyczki (używając niestandardowej struktury podobnej do mapy). Niektóre proste makra zostały użyte, aby poradzić sobie z tym dziwactwem i pozwoliły nam używać prawdziwych klas i funkcji C++ z normalnymi parametrami w wtyczkach bez większych problemów. Cały kod kleju jest generowany przez makra.

 5
Author: Julien Roncaglia,
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
2009-03-17 11:46:50

Dodam do tego, co już zostało powiedziane.

Ponieważ makra działają na podstawieniach tekstu, pozwalają robić bardzo przydatne rzeczy, których nie da się zrobić za pomocą funkcji.

Oto kilka przypadków, w których makra mogą być naprawdę przydatne:

/* Get the number of elements in array 'A'. */
#define ARRAY_LENGTH(A) (sizeof(A) / sizeof(A[0]))

Jest to bardzo popularne i często używane makro. Jest to bardzo przydatne, gdy na przykład trzeba iterację poprzez tablicę.

int main(void)
{
    int a[] = {1, 2, 3, 4, 5};
    int i;
    for (i = 0; i < ARRAY_LENGTH(a); ++i) {
        printf("a[%d] = %d\n", i, a[i]);
    }
    return 0;
}

Tutaj nie ma znaczenia czy inny programista doda jeszcze pięć elementów do a w deklecja. Pętla for - będzie zawsze iterować przez wszystkie elementy.

Funkcje biblioteki C do porównywania pamięci i ciągów znaków są dość brzydkie w użyciu.

Piszesz:

char *str = "Hello, world!";

if (strcmp(str, "Hello, world!") == 0) {
    /* ... */
}

Lub

char *str = "Hello, world!";

if (!strcmp(str, "Hello, world!")) {
    /* ... */
}

Aby sprawdzić, czy str wskazuje na "Hello, world". Osobiście uważam, że oba te rozwiązania wyglądają dość brzydko i myląco(szczególnie !strcmp(...)).

Oto dwa zgrabne makra, których niektórzy ludzie (w tym I) używają, gdy muszą porównać łańcuchy znaków lub pamięć za pomocą strcmp/memcmp:

/* Compare strings */
#define STRCMP(A, o, B) (strcmp((A), (B)) o 0)

/* Compare memory */
#define MEMCMP(A, o, B) (memcmp((A), (B)) o 0)

Teraz możesz napisać kod w następujący sposób:

char *str = "Hello, world!";

if (STRCMP(str, ==, "Hello, world!")) {
    /* ... */
}

Tutaj jest intencja dużo jaśniejsze!

Są to przypadki, w których makra są używane do rzeczy, których funkcje nie mogą osiągnąć. Makra nie powinny być używane do zastępowania funkcji, ale mają inne dobre zastosowania.

 5
Author: wefwefa3,
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
2015-01-10 22:20:37

Biorąc pod uwagę komentarze w twoim pytaniu, możesz nie do końca docenić to, że wywołanie funkcji może pociągnąć za sobą sporo kosztów ogólnych. Parametry i rejestry kluczy mogą być kopiowane do stosu po wejściu, a stos odwijany po wyjściu. Było to szczególnie prawdziwe w przypadku starszych układów Intel. Makra pozwalają programiście zachować abstrakcję funkcji (prawie), ale unikają kosztownych kosztów wywołania funkcji. Słowo kluczowe inline jest advisory, ale kompilator może nie zawsze zrób to dobrze. Chwałą i niebezpieczeństwem 'C' jest to, że zwykle można nagiąć kompilator do swojej woli.

W codziennym programowaniu aplikacji tego rodzaju mikro-optymalizacja (unikanie wywołań funkcji) jest na ogół gorsza niż bezużyteczna, ale jeśli piszesz krytyczną czasowo funkcję wywoływaną przez jądro systemu operacyjnego, to może to zrobić ogromną różnicę.

 4
Author: Charles E. Grant,
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
2009-03-18 01:07:23

W przeciwieństwie do zwykłych funkcji, można wykonywać przepływ sterowania (if, while, for,...) w makrach. Oto przykład:

#include <stdio.h>

#define Loop(i,x) for(i=0; i<x; i++)

int main(int argc, char *argv[])
{
    int i;
    int x = 5;
    Loop(i, x)
    {
        printf("%d", i); // Output: 01234
    } 
    return 0;
} 
 3
Author: alexpinho98,
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
2013-06-20 10:33:18

Wykorzystując manipulację tekstem preprocesora C można skonstruować odpowiednik C polimorficznej struktury danych. Używając tej techniki możemy zbudować niezawodny zestaw prymitywnych struktur danych, które mogą być użyte w dowolnym programie C, ponieważ wykorzystują składnię C, a nie specyfikę konkretnej implementacji.

Szczegółowe wyjaśnienie jak używać makr do zarządzania strukturą danych znajduje się tutaj - http://multi-core-dump.blogspot.com/2010/11/interesting-use-of-c-macros-polymorphic.html

 2
Author: coredump,
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-11-05 18:57:28

To jest dobre dla inlining kodu i unikanie wywołania funkcji overhead. Jak również używać go, jeśli chcesz zmienić zachowanie później bez edytowania wielu miejsc. Nie jest to przydatne dla złożonych rzeczy, ale dla prostych linii kodu, które chcesz wbudować, nie jest złe.

 2
Author: Arkaitz Jimenez,
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
2013-06-04 20:10:47

Makra pozwalają pozbyć się skopiowanych fragmentów, których nie można wyeliminować w żaden inny sposób.

Na przykład (prawdziwy kod, składnia kompilatora VS 2010):

for each (auto entry in entries)
{
        sciter::value item;
        item.set_item("DisplayName",    entry.DisplayName);
        item.set_item("IsFolder",       entry.IsFolder);
        item.set_item("IconPath",       entry.IconPath);
        item.set_item("FilePath",       entry.FilePath);
        item.set_item("LocalName",      entry.LocalName);
        items.append(item);
    }

Jest to miejsce, w którym przekazujesz wartość pola o tej samej nazwie do silnika skryptów. Czy to jest skopiowane? Tak. DisplayName jest używany jako ciąg znaków dla skryptu i jako nazwa pola dla kompilatora. Czy to źle? Tak. Jeśli zmienisz kod i zmienisz nazwę LocalName na RelativeFolderName (tak jak ja) i zapomnisz zrobić to samo z ciągiem znaków (tak jak ja) skrypt będzie działał w sposób, którego się nie spodziewasz (w rzeczywistości, w moim przykładzie zależy od tego, czy zapomniałeś zmienić nazwę pola w oddzielnym pliku skryptu, ale jeśli skrypt jest używany do serializacji, byłby to 100% błąd).

Jeśli użyjesz do tego makra, nie będzie miejsca na błąd:

for each (auto entry in entries)
{
#define STR_VALUE(arg) #arg
#define SET_ITEM(field) item.set_item(STR_VALUE(field), entry.field)
        sciter::value item;
        SET_ITEM(DisplayName);
        SET_ITEM(IsFolder);
        SET_ITEM(IconPath);
        SET_ITEM(FilePath);
        SET_ITEM(LocalName);
#undef SET_ITEM
#undef STR_VALUE
        items.append(item);
    }

Niestety, otwiera to drzwi dla innych typów błędów. Możesz zrobić literówkę pisząc makro i nigdy nie zobaczysz zepsutego kodu, ponieważ kompilator nie pokaż, jak to wygląda po całym wstępnym przetwarzaniu. Ktoś inny mógłby użyć tej samej nazwy (dlatego "wypuszczam" makra jak najszybciej z #undef). Więc używaj go mądrze. Jeśli widzisz inny sposób pozbycia się wklejonego kodu (np. funkcji), Użyj tego sposobu. Jeśli widzisz, że pozbycie się wklejonego kodu za pomocą makr nie jest warte rezultatu, zachowaj kod wklejony.

 2
Author: noober,
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-11-21 09:46:54

Jednym z oczywistych powodów jest to, że używając makra, kod zostanie rozszerzony w czasie kompilacji i otrzymasz pseudo-wywołanie funkcji bez narzutu wywołania.

W przeciwnym razie można go również użyć do symbolicznych stałych, dzięki czemu nie trzeba edytować tej samej wartości w kilku miejscach, aby zmienić jedną małą rzecz.

 1
Author: sykora,
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
2009-03-17 11:36:41

Makra .. gdy twój & # (*$&kompilator po prostu odmawia wbudowania czegoś.

To powinien być plakat motywacyjny, prawda?

Z całą powagą, Google nadużycie preprocesora (możesz zobaczyć podobne pytanie jako wynik #1). Jeśli piszę makro, które wykracza poza funkcjonalność assert (), zazwyczaj staram się sprawdzić, czy mój kompilator faktycznie wbudowałby podobną funkcję.

Inni sprzeciwiają się używaniu #if do kompilacji warunkowej .. oni raczej ty:

if (RUNNING_ON_VALGRIND)

Zamiast

#if RUNNING_ON_VALGRIND

.. do celów debugowania, ponieważ możesz zobaczyć if (), ale nie #if w debugerze. Następnie nurkujemy w #ifdef vs # if.

Jeśli jest poniżej 10 linii kodu, spróbuj go wstawić. Jeśli nie można go zainlinować, spróbuj go zoptymalizować. Jeśli jest zbyt głupi, aby być funkcją, zrób makro.

 0
Author: Tim Post,
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
2009-03-17 11:41:00

Chociaż nie jestem wielkim fanem makr i nie mam tendencji do pisania dużo C już, na podstawie mojego obecnego taskingu, coś takiego (co oczywiście może mieć pewne skutki uboczne) jest wygodne:

#define MIN(X, Y)  ((X) < (Y) ? (X) : (Y))

Teraz nie pisałem nic takiego od lat, ale 'funkcje' jak to były w całym kodzie, który utrzymywałem wcześniej w mojej karierze. Myślę, że rozbudowę można uznać za wygodną.

 0
Author: itsmatt,
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
2009-03-17 11:42:05

Nie widziałem, żeby ktoś o tym wspominał, jeśli chodzi o funkcje takie jak makra, np:

#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))

Ogólnie zaleca się unikanie używania makr, gdy nie jest to konieczne, z wielu powodów, czytelność jest głównym problemem. Więc:

Kiedy należy używać ich zamiast funkcji?

Prawie nigdy, ponieważ istnieje bardziej czytelna alternatywa, którą jest inline, Zobacz https://www.greenend.org.uk/rjk/tech/inline.html lub http://www.cplusplus.com/articles/2LywvCM9/ (drugi link to strona C++, ale punkt dotyczy kompilatorów c O ile mi wiadomo).

Niewielka różnica polega na tym, że makra są obsługiwane przez pre-procesor, a inline przez kompilator, ale obecnie nie ma praktycznej różnicy.

Kiedy należy je stosować?

Dla małych funkcji (maksymalnie dwie lub trzy linijki). Celem jest uzyskanie pewnej przewagi podczas biegu funkcje takie jak makra (i funkcje inline) są zamiennikami kodu wykonywanymi podczas wstępnego przetwarzania (lub kompilacji w przypadku inline) i nie są rzeczywistymi funkcjami żyjącymi w pamięci, więc nie ma narzutów wywołania funkcji (więcej szczegółów na połączonych stronach).

 0
Author: äli,
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-12-22 16:54:42