Zum Beispiel bin ich kürzlich im Linux-Kernel darauf gestoßen:
/ * Erzwinge einen Kompilierungsfehler, wenn die Bedingung erfüllt ist * / #define BUILD_BUG_ON (Bedingung) ((void) sizeof (char [1 - 2 * !! (Bedingung)]))
Wenn Sie also in Ihrem Code eine Struktur haben, die beispielsweise ein Vielfaches von 8 Bytes groß sein muss, möglicherweise aufgrund einiger Hardwareeinschränkungen, können Sie Folgendes tun:
BUILD_BUG_ON ((sizeof (struct mystruct)% 8)! = 0);
und es wird nicht kompiliert, es sei denn, die Größe von struct mystruct ist ein Vielfaches von 8, und wenn es ein Vielfaches von 8 ist, wird überhaupt kein Laufzeitcode generiert.
Ein weiterer Trick, den ich kenne, stammt aus dem Buch "Graphics Gems", mit dem eine einzelne Header-Datei Variablen in einem Modul deklarieren und initialisieren kann, während sie in anderen Modulen, die dieses Modul verwenden, lediglich als extern deklariert werden.
#ifdef DEFINE_MYHEADER_GLOBALS # GLOBAL definieren #define INIT (x, y) (x) = (y) #sonst #define GLOBAL extern #define INIT (x, y) #endif GLOBAL int INIT (x, 0); GLOBAL int somefunc (int a, int b);
Damit macht der Code, der x und somefunc definiert, Folgendes:
#define DEFINE_MYHEADER_GLOBALS #include "the_above_header_file.h"
während Code, der lediglich x und somefunc () verwendet, Folgendes tut:
#include "the_above_header_file.h"
Sie erhalten also eine Header-Datei, die sowohl Instanzen von Globals als auch Funktionsprototypen deklariert, wo sie benötigt werden, sowie die entsprechenden externen Deklarationen.
Also, was sind deine Lieblings-C-Programmier-Tricks in dieser Richtung?
BUILD_BUG_ON
Makro, was noch falsch mit der Verwendung von#error
innen und#if
?Antworten:
C99 bietet einige wirklich coole Sachen mit anonymen Arrays:
Sinnlose Variablen entfernen
wird
Übergeben einer variablen Anzahl von Argumenten
Statische verknüpfte Listen
Ich bin mir sicher, dass ich an viele andere coole Techniken nicht gedacht habe.
quelle
&(int){1}
, wenn Sie etwas klarer machen möchten, was Ihre Absicht hier ist.Beim Lesen des Quake 2-Quellcodes habe ich mir Folgendes ausgedacht:
(Mehr oder weniger habe ich den Code nicht zur Hand, um ihn jetzt zu überprüfen).
Seitdem öffnete sich vor meinen Augen eine neue Welt des kreativen Einsatzes des Präprozessors. Ich füge nicht mehr nur Header hinzu, sondern ab und zu ganze Codestücke (dies verbessert die Wiederverwendbarkeit erheblich) :-p
Danke John Carmack! xD
quelle
Ich verwende gerne
= {0};
Strukturen, um Strukturen zu initialisieren, ohne memset aufrufen zu müssen.Dadurch werden alle Elemente der Struktur (oder des Arrays) auf Null initialisiert (jedoch keine Füllbytes - verwenden Sie memset, wenn Sie diese ebenfalls auf Null setzen müssen).
Sie sollten sich jedoch bewusst sein, dass es bei großen, dynamisch zugewiesenen Strukturen einige Probleme gibt .
quelle
const struct something zero_something = { 0 };
und dann kann ich eine Variable imstruct something X = zero_something;
laufenden Betrieb mit oder während einer Routine zurücksetzen, die ich mit 'X = zero_something;' verwenden kann. Der einzig mögliche Einwand besteht darin, dass Daten von irgendwoher gelesen werden müssen. Heutzutage ist ein 'memset ()' vielleicht schneller - aber ich mag die Klarheit der Zuweisung, und es ist auch möglich, im Initialisierer (und memset () Werte ungleich Null zu verwenden, gefolgt von Optimierungen an einzelnen Mitgliedern kann langsamer sein als eine einfache Kopie).Wenn wir über C-Tricks sprechen, muss mein Favorit Duffs Gerät zum Abrollen der Schleife sein! Ich warte nur auf die richtige Gelegenheit, damit ich sie tatsächlich im Zorn nutzen kann ...
quelle
Verwenden
__FILE__
und__LINE__
zum Debuggenquelle
__FUNCTION__
ist nur ein Alias für__func__
und__func__
ist in c99. Sehr praktisch.__PRETTY_FUNCTION__
in C (GCC) ist nur ein weiterer Alias für__func__
, aber in C ++ erhalten Sie die vollständige Funktionssignatur.In C99
quelle
Einmal haben ein Freund von mir und ich die Rückkehr neu definiert, um einen kniffligen Fehler bei der Stapelkorruption zu finden.
Etwas wie:
quelle
DoSomeStackCheckStuff
die Funktionen aufrufen , die Sie verfolgen möchten .#define return if((DoSomeStackCheckStuff) && 0) ; else return
... genauso verrückt, denke ich!Ich mag den "Struktur-Hack" für ein dynamisch großes Objekt. Diese Seite erklärt es auch ziemlich gut (obwohl sie sich auf die C99-Version beziehen, in der Sie "str []" als letztes Mitglied einer Struktur schreiben können). Sie könnten einen String "Objekt" wie folgt machen:
Hier haben wir eine Struktur vom Typ X auf dem Heap zugewiesen, die die Größe eines int (für len) plus der Länge von "Hallo Welt" plus 1 hat (da str 1 in der Größe von (X) enthalten ist.
Dies ist im Allgemeinen nützlich, wenn Sie einen "Header" direkt vor einigen Daten variabler Länge im selben Block haben möchten.
quelle
str[1]
(nichtstr[]
) das 1-Byte von str hat, ist in der enthaltensizeof(struct X)
. Dies schließt jegliche Polsterung zwischenlen
und einstr
.str
. OK, wenn ich zuweise,sizeof(struct X) + 10
dann macht diesstr
effektiv10 - sizeof(int)
(oder mehr, da wir sagten, dass es Polsterung gibt) groß. Dies überlagertstr
und jede Polsterung danach. Der einzige Weg, auf dem es einen Unterschied geben würde, ist, wenn es ein Mitglied gäbe, nachstr
dem das Ganze sowieso kaputt geht, müssen flexible Mitglieder die letzten sein. Eine Polsterung am Ende führt möglicherweise nur dazu, dass zu viel zugewiesen wird. Bitte geben Sie ein konkretes Beispiel dafür, wie es tatsächlich schief gehen könnte.Objektorientierter Code mit C durch Emulieren von Klassen.
Erstellen Sie einfach eine Struktur und eine Reihe von Funktionen, die als ersten Parameter einen Zeiger auf diese Struktur verwenden.
quelle
Anstatt
Verwenden
quelle
Verwenden eines dummen Makrotricks, um die Pflege von Datensatzdefinitionen zu vereinfachen.
quelle
Zum Erstellen einer Variablen, die in allen Modulen außer der schreibgeschützten Variablen schreibgeschützt ist:
quelle
Source2.c
kann der Compiler davon ausgehen, dass sichMyVar
dies auch bei einem Funktionsaufruf an nicht ändertSource1.c
. (Beachten Sie, dass dies als tatsächliche const-Variable von einem Zeiger auf const abweicht. Im letzteren Fall kann das Objekt, auf das verwiesen wird, noch über einen anderen Zeiger geändert werden.)Bitverschiebungen werden nur bis zu einem Verschiebungsbetrag von 31 (auf einer 32-Bit-Ganzzahl) definiert.
Was machen Sie, wenn Sie eine berechnete Schicht haben möchten, die auch mit höheren Schichtwerten arbeiten muss? So macht es der Theora-Videocodec:
Oder viel lesbarer:
Das Ausführen der Aufgabe auf die oben gezeigte Weise ist viel schneller als die Verwendung eines Zweigs wie diesem:
quelle
v
, während derhalfshift
Trick den zulässigen Bereich bei einer 32-Bit-Architektur nur auf 63 und bei einer 64-Bit-Architektur auf 127 verdoppelt.Deklarieren von Zeigerarrays auf Funktionen zum Implementieren von Finite-State-Maschinen.
Der erfreulichste Vorteil ist, dass es einfach ist, jeden Stimulus / Zustand zu zwingen, alle Codepfade zu überprüfen.
In einem eingebetteten System ordne ich häufig einen ISR zu, um auf eine solche Tabelle zu verweisen, und revektoriere sie nach Bedarf (außerhalb des ISR).
quelle
Ein weiterer netter "Trick" vor dem Prozessor besteht darin, das Zeichen "#" zum Drucken von Debugging-Ausdrücken zu verwenden. Beispielsweise:
Bearbeiten: Der folgende Code funktioniert nur unter C ++. Vielen Dank an smcameron und Evan Teran.
Ja, die Kompilierungszeit ist immer großartig. Es kann auch geschrieben werden als:
quelle
Ich würde es nicht wirklich als Lieblingstrick bezeichnen, da ich es nie benutzt habe, aber die Erwähnung von Duffs Gerät erinnerte mich an diesen Artikel über die Implementierung von Coroutines in C. Es gibt mir immer ein Kichern, aber ich bin mir sicher, dass es das könnte irgendwann nützlich sein.
quelle
static
Variablen speichere , sondern stattdessen eine Struktur dynamisch zuordne und einen Zeiger darauf an die Coroutine-Funktion übergebe. Eine Reihe von Makros machen dies schmackhafter. Es ist nicht schön, aber besser als die Async / Callback-Version, die überall herumspringt. Ich würde grüne Threads (viaswapcontext()
on * nixes) verwenden, wenn ich könnte.Die Weile (0); hat keine Auswirkung auf das Programm, aber der Compiler gibt eine Warnung über "Dies macht nichts" aus. Dies reicht aus, um mich dazu zu bringen, auf die fehlerhafte Zeile zu schauen und dann den wahren Grund zu sehen, warum ich darauf aufmerksam machen wollte.
quelle
Ich bin ein Fan von XOR-Hacks:
2 Zeiger ohne dritten temporären Zeiger tauschen:
Oder ich mag die xor verknüpfte Liste mit nur einem Zeiger wirklich. (http://en.wikipedia.org/wiki/XOR_linked_list)
Jeder Knoten in der verknüpften Liste ist das X oder des vorherigen Knotens und des nächsten Knotens. Um vorwärts zu fahren, wird die Adresse der Knoten auf folgende Weise gefunden:
etc.
oder rückwärts fahren:
etc.
Obwohl es nicht besonders nützlich ist (Sie können nicht von einem beliebigen Knoten aus durchqueren), finde ich es sehr cool.
quelle
Dieser stammt aus dem Buch 'Genug Seil, um sich in den Fuß zu schießen':
In der Kopfzeile deklarieren
Platzieren Sie in Ihrem Code Testanweisungen, z.
Das do / while hilft, falls der Inhalt des Makros auf mehrere Anweisungen erweitert wird.
Die Anweisung wird nur gedruckt, wenn das Flag '-D RELEASE' für den Compiler nicht verwendet wird.
Sie können dann z. Übergebe die Flagge an dein Makefile usw.
Ich bin mir nicht sicher, wie das in Windows funktioniert, aber in * nix funktioniert es gut
quelle
#define D(x) do { } while(0)
stattdessen behandelt diesen Fall (und kann auf den Zweig angewendet werden, derx
ausRusty produzierte tatsächlich eine ganze Reihe von Build-Bedingungen in ccan erstellt. Schauen Sie sich das Build- Assert- Modul an:
Es gibt viele andere hilfreiche Makros im eigentlichen Header, die sich leicht einfügen lassen.
Ich versuche mit aller Kraft, dem Zug der dunklen Seite (und dem Missbrauch durch Präprozessoren) zu widerstehen, indem ich mich hauptsächlich an Inline-Funktionen halte, aber ich mag clevere, nützliche Makros wie die von Ihnen beschriebenen.
quelle
Zwei gute Quellenbücher für diese Art von Dingen sind Die Praxis des Programmierens und Schreibens von festem Code . Einer von ihnen (ich weiß nicht mehr, welcher) sagt: Bevorzugen Sie enum gegenüber #define, wo Sie können, da enum vom Compiler überprüft wird.
quelle
Nicht spezifisch für C, aber ich habe den XOR-Operator immer gemocht. Eine coole Sache, die es tun kann, ist "Tauschen ohne temporären Wert":
Ergebnis:
quelle
Siehe Frage "Versteckte Funktionen von C" .
quelle
Ich mag das Konzept
container_of
, zum Beispiel in Listen verwendet zu werden. Grundsätzlich müssen Sie nicht angeben ,next
undlast
Felder für jede Struktur , die in der Liste sein. Stattdessen hängen Sie den Listenstrukturkopf an die tatsächlich verknüpften Elemente an.Schauen Sie sich
include/linux/list.h
Beispiele aus der Praxis an.quelle
Ich denke, die Verwendung von Benutzerdatenzeigern ist ziemlich ordentlich. Eine Mode, die heutzutage an Boden verliert. Es ist nicht so sehr eine C-Funktion, aber in C ziemlich einfach zu verwenden.
quelle
Ich benutze X-Makros , um den Pre-Compiler Code generieren zu lassen. Sie sind besonders nützlich, um Fehlerwerte und zugehörige Fehlerzeichenfolgen an einer Stelle zu definieren, können jedoch weit darüber hinausgehen.
quelle
Unsere Codebasis hat einen ähnlichen Trick wie
Dies ermöglicht die Verfolgung von Speicherlecks im Debug-Modus. Ich fand das immer cool.
quelle
Spaß mit Makros:
quelle
Hier ist ein Beispiel, wie Sie C-Code völlig unbewusst machen können, was tatsächlich von HW zum Ausführen der App verwendet wird. Die main.c führt das Setup durch und dann kann die freie Ebene auf jedem Compiler / Arch implementiert werden. Ich denke, es ist ziemlich ordentlich, um C-Code ein wenig zu abstrahieren, so dass es nicht zu spezifisch wird.
Fügen Sie hier ein vollständiges kompilierbares Beispiel hinzu.
quelle
Füllen Sie die Lücken aus, damit weder Hallo noch Hallo in der Ausgabe erscheinen.
ans:
fclose(stdout)
quelle
{}
Symbolleistenschaltfläche formatieren (ich habe es für Sie erledigt). Die Schaltfläche "Anführungszeichen" behält keine Leerzeichen bei und wendet keine Syntaxhervorhebung an.