Ukryte cechy C

Wiem, że za wszystkimi implementacjami kompilatora C stoi standard, więc nie powinno być żadnych ukrytych funkcji. Mimo to, jestem pewien, że wszyscy programiści C mają ukryte / tajne sztuczki, których używają cały czas.

Author: bernardn, 2008-09-25

30 answers

Wskaźniki funkcji. Można użyć tabeli wskaźników funkcji do zaimplementowania np. szybkich interpreterów kodu pośredniego (FORTH) lub dyspozytorów kodu bajtowego lub do symulacji metod wirtualnych podobnych do OO.

W bibliotece standardowej znajdują się ukryte klejnoty, takie jak qsort (), bsearch (), strpbrk (), strcspn () [te dwa ostatnie są przydatne do implementacji zamiennika strtok ()].

Błędem w C jest to, że podpisane arytmetyczne przepełnienie jest niezdefiniowanym zachowaniem (ub). Więc kiedy tylko zobaczysz wyrażenie takie jak x + y, oba są podpisane ints, może potencjalnie przepełnić i spowodować UB.

 62
Author: zvrba,
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
2008-09-25 10:29:18

Bardziej sztuczka kompilatora GCC, ale możesz dać podpowiedzi wskazujące gałąź kompilatorowi (powszechne w jądrze Linuksa)

#define likely(x)       __builtin_expect((x),1)
#define unlikely(x)     __builtin_expect((x),0)

Zobacz: http://kerneltrap.org/node/4705

Podoba mi się to, że dodaje ona również wyrazistości do niektórych funkcji.

void foo(int arg)
{
     if (unlikely(arg == 0)) {
           do_this();
           return;
     }
     do_that();
     ...
}
 117
Author: tonylo,
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
2008-09-25 14:08:29
int8_t
int16_t
int32_t
uint8_t
uint16_t
uint32_t

Są to elementy opcjonalne w standardzie, ale musi to być ukryta funkcja, ponieważ ludzie stale je redefiniują. Jedna baza kodu, nad którą pracowałem (i nadal pracuję) ma wiele redefinicji, wszystkie z różnymi identyfikatorami. W większości przypadków jest to z makrami preprocesora:

#define INT16 short
#define INT32  long

I tak dalej. Aż chce mi się wyrywać włosy. wystarczy użyć standardowego typu integer!

 78
Author: Ben Collins,
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
2008-09-25 14:31:30

Operator przecinka nie jest powszechnie używany. Z pewnością może być nadużywany, ale może być również bardzo przydatny. To użycie jest najczęstsze:

for (int i=0; i<10; i++, doSomethingElse())
{
  /* whatever */
}
Ale możesz użyć tego operatora wszędzie. Obserwować:
int j = (printf("Assigning variable j\n"), getValueFromSomewhere());

Każda instrukcja jest oceniana, ale wartość wyrażenia będzie wartością ostatniej ocenianej instrukcji.

 73
Author: Ben Collins,
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
2008-09-25 14:37:22

Inicjalizacja struktury do zera

struct mystruct a = {0};

Spowoduje to zerowanie wszystkich elementów konstrukcji.

 63
Author: mike511,
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
2008-10-01 00:34:59

Stałe Wieloznakowe:

int x = 'ABCD';

Ustawia x na 0x41424344 (lub 0x44434241, w zależności od architektury).

EDIT: ta technika nie jest przenośna, zwłaszcza jeśli serializujesz int. Jednak niezwykle przydatne może być tworzenie samo-dokumentujących się enum. np.

enum state {
    stopped = 'STOP',
    running = 'RUN!',
    waiting = 'WAIT',
};

To znacznie ułatwia, jeśli patrzysz na zrzut pamięci surowej i musisz określić wartość enum bez konieczności sprawdzania go.

 52
Author: Ferruccio,
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-09-10 20:27:28

Nigdy nie używałem pól bitowych , ale brzmią fajnie w przypadku ultra-niskich poziomów.

struct cat {
    unsigned int legs:3;  // 3 bits for legs (0-4 fit in 3 bits)
    unsigned int lives:4; // 4 bits for lives (0-9 fit in 4 bits)
    // ...
};

cat make_cat()
{
    cat kitty;
    kitty.legs = 4;
    kitty.lives = 9;
    return kitty;
}

Oznacza to, że sizeof(cat) może być tak małe jak sizeof(char).


Włączone komentarze przez Aaron i leppie , Dzięki chłopaki.

 44
Author: Motti,
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:17:47

C ma standard, ale nie wszystkie kompilatory C są w pełni zgodne (nie widziałem jeszcze żadnego w pełni zgodnego kompilatora C99!).

To powiedziawszy, triki, które preferuję, to te, które są nieoczywiste i przenośne na różnych platformach, ponieważ opierają się na semantyce C. Zazwyczaj są to makra lub arytmetyka bitowa.

Na przykład: Zamiana dwóch niepodpisanych liczb całkowitych bez użycia zmiennej tymczasowej:

...
a ^= b ; b ^= a; a ^=b;
...

Lub "rozszerzenie C" do reprezentowania skończonych maszyn stanowych, takich jak:

FSM {
  STATE(x) {
    ...
    NEXTSTATE(y);
  }

  STATE(y) {
    ...
    if (x == 0) 
      NEXTSTATE(y);
    else 
      NEXTSTATE(x);
  }
}

Że można uzyskać za pomocą następujących makr:

#define FSM
#define STATE(x)      s_##x :
#define NEXTSTATE(x)  goto s_##x

Ogólnie rzecz biorąc, nie lubię sztuczek, które są sprytne, ale sprawiają, że kod niepotrzebnie komplikuje się w czytaniu (jako przykład wymiany) i uwielbiam te, które sprawiają, że kod jest jaśniejszy i bezpośrednio przekazuje zamiar (jak przykład FSM).

 37
Author: Remo.D,
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
2008-09-25 09:20:57

Struktury przeplatające jak Urządzenie Duffa :

strncpy(to, from, count)
char *to, *from;
int count;
{
    int n = (count + 7) / 8;
    switch (count % 8) {
    case 0: do { *to = *from++;
    case 7:      *to = *from++;
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n > 0);
    }
}
 37
Author: ComSubVie,
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
2010-10-14 07:28:10

Bardzo lubię wyznaczone inicjalizatory, dodane w C99 (i obsługiwane w gcc przez długi czas):

#define FOO 16
#define BAR 3

myStructType_t myStuff[] = {
    [FOO] = { foo1, foo2, foo3 },
    [BAR] = { bar1, bar2, bar3 },
    ...

Inicjalizacja tablicy nie jest już zależna od pozycji. Jeśli zmienisz wartości FOO lub BAR, inicjalizacja tablicy będzie automatycznie odpowiadała ich nowej wartości.

 33
Author: DGentry,
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
2008-09-25 14:15:22

C99 ma niesamowitą inicjalizację struktury dowolnego rzędu.

struct foo{
  int x;
  int y;
  char* name;
};

void main(){
  struct foo f = { .y = 23, .name = "awesome", .x = -38 };
}

 28
Author: 2 revs, 2 users 83%Jason,
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
2012-01-19 15:20:06

Anonymous structures and arrays to mój ulubiony. (por. http://www.run.montefiore.ulg.ac.be / ~martin/resources/kung-f00.html )

setsockopt(yourSocket, SOL_SOCKET, SO_REUSEADDR, (int[]){1}, sizeof(int));

Lub

void myFunction(type* values) {
    while(*values) x=*values++;
}
myFunction((type[]){val1,val2,val3,val4,0});

Może być nawet używany do instancjowania połączonych list...

 27
Author: PypeBros,
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
2008-09-25 10:16:48

Gcc ma wiele rozszerzeń do języka C, które lubię, które można znaleźć Tutaj . Niektóre z moich ulubionych To atrybuty funkcji . Bardzo przydatnym przykładem jest atrybut format. Można tego użyć, jeśli zdefiniujesz funkcję niestandardową, która przyjmuje ciąg znaków w formacie printf. Jeśli włączysz ten atrybut funkcji, gcc sprawdzi twoje argumenty, aby upewnić się, że łańcuch formatowania i argumenty pasują do siebie i wygeneruje ostrzeżenia lub błędy jako odpowiednie.

int my_printf (void *my_object, const char *my_format, ...)
            __attribute__ ((format (printf, 2, 3)));
 24
Author: Russell Bryant,
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
2008-09-29 00:27:06

(ukryta) funkcja, która "zaszokowała" mnie, gdy po raz pierwszy zobaczyłem, dotyczy printf. ta funkcja pozwala na użycie zmiennych do samego formatowania specyfikacji formatu. poszukaj kodu, zobaczysz lepiej:

#include <stdio.h>

int main() {
    int a = 3;
    float b = 6.412355;
    printf("%.*f\n",a,b);
    return 0;
}

Znak * osiąga ten efekt.

 24
Author: kolistivra,
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-06-22 11:10:14

No cóż... Myślę, że jedną z mocnych stron języka C jest jego przenośność i standardowość, więc kiedy znajduję jakąś "ukrytą sztuczkę" w implementacji, której obecnie używam, staram się jej nie używać, ponieważ staram się zachować mój kod C jako standardowy i przenośny, jak to możliwe.

 24
Author: Giacomo Degli Esposti,
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-11-03 15:00:04

Twierdzenia o czasie kompilacji, jak już tutaj omówione .

//--- size of static_assertion array is negative if condition is not met
#define STATIC_ASSERT(condition) \
    typedef struct { \
        char static_assertion[condition ? 1 : -1]; \
    } static_assertion_t

//--- ensure structure fits in 
STATIC_ASSERT(sizeof(mystruct_t) <= 4096);
 19
Author: philant,
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:09:44

Ciąg stały konkatenacja

Byłem dość zaskoczony, że nie widzę go już w odpowiedziach, ponieważ wszystkie Kompilatory, które znam, wspierają go, ale wielu programistów zdaje się go ignorować. Czasami jest to bardzo przydatne i nie tylko podczas pisania makr.

Przypadek użycia, który mam w obecnym kodzie: Mam #define PATH "/some/path/" w pliku konfiguracyjnym (tak naprawdę jest on ustawiony przez makefile). Teraz chcę zbudować pełną ścieżkę, w tym nazwy plików, aby otworzyć ressources. It just goes do:

fd = open(PATH "/file", flags);

Zamiast okropnych, ale bardzo powszechnych:

char buffer[256];
snprintf(buffer, 256, "%s/file", PATH);
fd = open(buffer, flags);

Zauważ, że powszechnym rozwiązaniem jest:

  • trzy razy dłużej
  • znacznie mniej czytelny
  • znacznie wolniej
  • less powerfull at it set to a arbitrary buffer size limit (but you would have to use even longer code to avoid that without constant strings contatenation).
  • użyj więcej przestrzeni stosu
 16
Author: kriss,
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
2010-05-08 08:22:58

Cóż, nigdy go nie używałem i nie jestem pewien, czy kiedykolwiek poleciłbym go komukolwiek, ale czuję, że to pytanie byłoby niekompletne bez wzmianki o sztuczce Simona Tathama.

 15
Author: Mark Baker,
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
2008-10-17 08:54:17

Podczas inicjalizacji tablic lub enum, możesz umieścić przecinek po ostatniej pozycji na liście inicjalizacji. e. g:

int x[] = { 1, 2, 3, };

enum foo { bar, baz, boom, };

Zostało to zrobione tak, że jeśli generujesz Kod automatycznie, nie musisz się martwić o wyeliminowanie ostatniego przecinka.

 12
Author: Ferruccio,
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-06-11 11:14:49

Przypisanie struktury jest fajne. Wiele osób nie zdaje sobie sprawy, że struktury też są wartościami i można je przypisać, nie ma potrzeby używania memcpy(), gdy proste przypisanie wystarczy.

Na przykład, rozważmy jakąś wyimaginowaną bibliotekę grafiki 2D, może ona zdefiniować typ reprezentujący (całkowitą) współrzędną ekranu:

typedef struct {
   int x;
   int y;
} Point;

Teraz robisz rzeczy, które mogą wyglądać "źle", na przykład piszesz funkcję, która tworzy punkt zainicjowany z argumentów funkcji i zwraca go, jak więc:

Point point_new(int x, int y)
{
  Point p;
  p.x = x;
  p.y = y;
  return p;
}

Jest to bezpieczne, o ile (oczywiście) wartość zwracana jest kopiowana przez wartość przy użyciu przypisania struct:

Point origin;
origin = point_new(0, 0);

W ten sposób można pisać dość czysty i obiektowo zorientowany Kod, wszystko w zwykłym standardowym C.

 12
Author: unwind,
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
2010-10-25 08:32:49

Dziwne indeksowanie wektorów:

int v[100]; int index = 10; 
/* v[index] it's the same thing as index[v] */
 10
Author: INS,
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
2008-09-25 09:30:28

Kompilatory C implementują jeden z kilku standardów. Posiadanie standardu nie oznacza jednak, że wszystkie aspekty języka są zdefiniowane. urządzenie Duffa, na przykład, jest ulubioną "ukrytą" funkcją, która stała się tak popularna, że nowoczesne Kompilatory mają specjalny kod rozpoznawczy, aby zapewnić, że techniki optymalizacyjne nie zatkają pożądanego efektu tego często używanego wzorca.

Ogólnie rzecz biorąc ukryte funkcje lub sztuczki językowe są zniechęcane, gdy biegasz na krawędź brzytwy dowolnego standardu C używanego przez kompilator. Wiele takich sztuczek nie działa z jednego kompilatora do drugiego i często tego rodzaju funkcje zawodzą z jednej wersji pakietu kompilatorów danego producenta do innej wersji.

Różne triki, które złamały kod C to:

  1. poleganie na tym, jak kompilator rozkłada struktury w pamięci.
  2. założenia na endianness liczb całkowitych/pływaków.
  3. założenia dotyczące funkcji ABIs.
  4. założenia dotyczące kierunku wzrostu ramek stosu.
  5. założenia dotyczące kolejności wykonania w oświadczeniach.
  6. założenia dotyczące kolejności wykonywania poleceń w argumentach funkcji.
  7. założenia dotyczące wielkości bitów lub precyzji typów short, int, long, float i double.

Inne Problemy i problemy, które pojawiają się, gdy programiści przyjmują założenia dotyczące modeli wykonawczych, które są określone w większości standardów C jako " kompilator zachowanie zależne.

 9
Author: Kevin S.,
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
2008-09-25 09:26:19

Podczas używania sscanf możesz użyć %n, aby dowiedzieć się, gdzie powinieneś dalej czytać:

sscanf ( string, "%d%n", &number, &length );
string += length;

Najwyraźniej nie możesz dodać kolejnej odpowiedzi, więc dołączę tu drugą, możesz użyć "& & " i "| | " jako warunku:

#include <stdio.h>
#include <stdlib.h>

int main()
{
   1 || puts("Hello\n");
   0 || puts("Hi\n");
   1 && puts("ROFL\n");
   0 && puts("LOL\n");

   exit( 0 );
}

Ten kod wyświetli:

Hi
ROFL
 9
Author: onemasse,
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-05-26 02:20:38

Użycie INT(3) do ustawienia punktu przerwania w kodzie jest moim ulubionym czasem

 8
Author: Dror Helper,
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
2008-09-25 09:06:39

Moją ulubioną" ukrytą " cechą C, jest użycie %n w printf do odpisywania na stosie. Zwykle printf wyświetla wartości parametrów ze stosu na podstawie formatu, ale %n może je zapisać.

Sprawdź sekcję 3.4.2 TUTAJ . Może prowadzić do wielu paskudnych luk.

 8
Author: Sridhar Iyer,
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
2008-09-26 20:18:15

Założenie w czasie kompilacji-sprawdzanie za pomocą enums: Głupi przykład, ale może być naprawdę przydatny dla bibliotek z konfigurowalnymi stałymi w czasie kompilacji.

#define D 1
#define DD 2

enum CompileTimeCheck
{
    MAKE_SURE_DD_IS_TWICE_D = 1/(2*(D) == (DD)),
    MAKE_SURE_DD_IS_POW2    = 1/((((DD) - 1) & (DD)) == 0)
};
 8
Author: S.C. Madsen,
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-11-11 13:42:11

Gcc (c) ma kilka ciekawych funkcji, które możesz włączyć, takich jak zagnieżdżone deklaracje funkcji i a?: B forma ?: operator, który zwraca a jeśli a nie jest false.

 8
Author: Alex Brown,
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-05-26 02:32:16

Odkryłem ostatnio 0 bitfieldów.

struct {
  int    a:3;
  int    b:2;
  int     :0;
  int    c:4;
  int    d:3;
};

Który da układ

000aaabb 0ccccddd

Zamiast Bez :0;

0000aaab bccccddd

Pole o szerokości 0 mówi, że następujące pola bitowe powinny być ustawione na następnej jednostce atomowej(char)

 8
Author: tristopia,
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-09-10 20:58:31

Makra zmiennych w stylu C99, aka

#define ERR(name, fmt, ...)   fprintf(stderr, "ERROR " #name ": " fmt "\n", \
                                  __VAR_ARGS__)

Które byłyby używane jak

ERR(errCantOpen, "File %s cannot be opened", filename);

Tutaj również używam operatora stringize i ciągów stałych konkatentacji, inne funkcje, które naprawdę lubię.

 7
Author: Ben Combee,
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
2008-10-22 18:00:24

Zmienna rozmiar automatyczne zmienne są również przydatne w niektórych przypadkach. Zostały one dodane i nC99 i były obsługiwane w gcc przez długi czas.

void foo(uint32_t extraPadding) {
    uint8_t commBuffer[sizeof(myProtocol_t) + extraPadding];

Kończy się buforem na stosie z miejscem na nagłówek protokołu o stałym rozmiarze oraz danymi o zmiennej wielkości. Taki sam efekt można uzyskać za pomocą alloca(), ale składnia ta jest bardziej zwarta.

Musisz upewnić się, że extraPadding jest rozsądną wartością przed wywołaniem tej procedury, albo skończysz wysadzając stos. Musiałbyś poczytać sprawdź argumenty przed wywołaniem malloc lub innej techniki alokacji pamięci, więc nie jest to naprawdę niezwykłe.

 6
Author: DGentry,
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
2008-09-25 14:23:20