Ich weiß, dass hinter allen C-Compiler-Implementierungen ein Standard steckt, daher sollte es keine versteckten Funktionen geben. Trotzdem bin ich sicher, dass alle C-Entwickler versteckte / geheime Tricks haben, die sie ständig anwenden.
c
hidden-features
bernardn
quelle
quelle
Antworten:
Funktionszeiger. Sie können eine Tabelle mit Funktionszeigern verwenden, um beispielsweise schnelle Code-Interpreter mit indirektem Thread (FORTH) oder Byte-Code-Dispatcher zu implementieren oder OO-ähnliche virtuelle Methoden zu simulieren.
Dann gibt es versteckte Juwelen in der Standardbibliothek, wie z. B. qsort (), bsearch (), strpbrk (), strcspn () [die beiden letzteren sind nützlich für die Implementierung eines strtok () - Ersatzes].
Eine Fehlfunktion von C ist, dass ein vorzeichenbehafteter arithmetischer Überlauf ein undefiniertes Verhalten (UB) ist. Wenn Sie also einen Ausdruck wie x + y sehen, bei dem es sich um signierte Ints handelt, kann er möglicherweise überlaufen und UB verursachen.
quelle
Eher ein Trick des GCC-Compilers, aber Sie können dem Compiler Hinweise zur Verzweigungsanzeige geben (im Linux-Kernel üblich).
Siehe: http://kerneltrap.org/node/4705
Was mir daran gefällt, ist, dass es einigen Funktionen auch etwas Ausdruckskraft verleiht.
quelle
Dies ist ein optionales Element im Standard, aber es muss eine versteckte Funktion sein, da die Leute sie ständig neu definieren. Eine Codebasis, an der ich gearbeitet habe (und vorerst noch arbeite), hat mehrere Neudefinitionen, alle mit unterschiedlichen Bezeichnern. Meistens mit Präprozessor-Makros:
Und so weiter. Ich möchte mir die Haare ausreißen. Verwenden Sie einfach die verdammten Standard-Integer-Typedefs!
quelle
Der Komma-Operator ist nicht weit verbreitet. Es kann sicherlich missbraucht werden, aber es kann auch sehr nützlich sein. Diese Verwendung ist die häufigste:
Sie können diesen Operator jedoch überall verwenden. Beobachten:
Jede Anweisung wird ausgewertet, aber der Wert des Ausdrucks ist der der zuletzt ausgewerteten Anweisung.
quelle
Struktur auf Null initialisieren
Dadurch werden alle Strukturelemente auf Null gesetzt.
quelle
memset
/calloc
mache "alle Bytes Null" (dh physikalische Nullen), was in der Tat nicht für alle Typen definiert ist.{ 0 }
garantiert, dass alles mit den richtigen logischen Nullwerten intilaisiert wird . Es wird beispielsweise garantiert, dass Zeiger ihre richtigen Nullwerte erhalten, selbst wenn der Nullwert auf der angegebenen Plattform ist0xBAADFOOD
.memset
tut (mit0
als zweites Argument). Sie erhalten eine logische Null, wenn Sie das Objekt im Quellcode initialisieren / zuweisen0
(oder{ 0 }
). Diese beiden Arten von Nullen führen nicht unbedingt zum gleichen Ergebnis. Wie im Beispiel mit Zeiger. Wenn Siememset
einen Zeiger verwenden, erhalten Sie einen0x0000
Zeiger. Wenn Sie0
jedoch einem Zeiger zuweisen , erhalten Sie einen Nullzeigerwert , der auf der physischen Ebene0xBAADF00D
oder etwas anderes sein kann.double
. Normalerweise wird es gemäß dem IEEE-754-Standard implementiert, bei dem die logische Null und die physikalische Null gleich sind. IEEE-754 wird jedoch von der Sprache nicht benötigt. Es kann also vorkommen, dassdouble d = 0;
einige Bits im Speicher, die von belegtd
werden, nicht Null sind , wenn Sie dies tun (logische Null) .Konstanten mit mehreren Zeichen:
Dies setzt
x
auf0x41424344
(oder0x44434241
, abhängig von der Architektur).BEARBEITEN: Diese Technik ist nicht portierbar, insbesondere wenn Sie den int serialisieren. Es kann jedoch äußerst nützlich sein, selbstdokumentierende Aufzählungen zu erstellen. z.B
Dies macht es viel einfacher, wenn Sie sich einen Rohspeicherauszug ansehen und den Wert einer Aufzählung ermitteln müssen, ohne ihn nachschlagen zu müssen.
quelle
Ich habe nie Bitfelder verwendet, aber sie klingen cool für Sachen mit extrem niedrigem Pegel.
Dies bedeutet,
sizeof(cat)
dass so klein sein kann wiesizeof(char)
.Eingebaute Kommentare von Aaron und Leppie , danke Jungs.
quelle
C hat einen Standard, aber nicht alle C-Compiler sind vollständig kompatibel (ich habe noch keinen vollständig kompatiblen C99-Compiler gesehen!).
Die Tricks, die ich bevorzuge, sind jedoch nicht offensichtlich und plattformübergreifend portierbar, da sie auf der C-Semantik beruhen. In der Regel handelt es sich um Makros oder Bitarithmetik.
Beispiel: Vertauschen von zwei Ganzzahlen ohne Vorzeichen ohne Verwendung einer temporären Variablen:
oder "Erweitern von C", um endliche Zustandsmaschinen darzustellen wie:
Dies kann mit den folgenden Makros erreicht werden:
Im Allgemeinen mag ich die Tricks nicht, die klug sind, aber das Lesen des Codes unnötig kompliziert machen (als Swap-Beispiel), und ich liebe diejenigen, die den Code klarer machen und die Absicht direkt vermitteln (wie das FSM-Beispiel). .
quelle
Interlacing-Strukturen wie Duff's Device :
quelle
Ich mag bestimmte Initialisierer, die in C99 hinzugefügt wurden (und lange Zeit in gcc unterstützt wurden):
Die Array-Initialisierung ist nicht mehr positionsabhängig. Wenn Sie die Werte von FOO oder BAR ändern, entspricht die Array-Initialisierung automatisch ihrem neuen Wert.
quelle
C99 verfügt über eine fantastische Strukturinitialisierung beliebiger Reihenfolge.
quelle
anonyme Strukturen und Arrays sind meine Favoriten. (vgl. http://www.run.montefiore.ulg.ac.be/~martin/resources/kung-f00.html )
oder
Es kann sogar verwendet werden, um verknüpfte Listen zu instanziieren ...
quelle
gcc hat eine Reihe von Erweiterungen der C-Sprache, die mir gefallen und die hier zu finden sind . Einige meiner Favoriten sind Funktionsattribute . Ein äußerst nützliches Beispiel ist das Formatattribut. Dies kann verwendet werden, wenn Sie eine benutzerdefinierte Funktion definieren, die eine Zeichenfolge im printf-Format verwendet. Wenn Sie dieses Funktionsattribut aktivieren, überprüft gcc Ihre Argumente, um sicherzustellen, dass Ihre Formatzeichenfolge und Ihre Argumente übereinstimmen, und generiert gegebenenfalls Warnungen oder Fehler.
quelle
Die (versteckte) Funktion, die mich "schockierte", als ich sie zum ersten Mal sah, handelt von printf. Mit dieser Funktion können Sie Variablen zum Formatieren von Formatspezifizierern selbst verwenden. Suchen Sie nach dem Code, Sie werden besser sehen:
Das Zeichen * erzielt diesen Effekt.
quelle
Nun ... Ich denke, dass eine der Stärken der C-Sprache ihre Portabilität und Standardität ist. Wenn ich also in der Implementierung, die ich derzeit verwende, einen "versteckten Trick" finde, versuche ich, ihn nicht zu verwenden, weil ich versuche, meinen zu behalten C-Code als Standard und tragbar wie möglich.
quelle
Behauptungen zur Kompilierungszeit, wie hier bereits erläutert .
quelle
Konstante Zeichenfolgenverkettung
Ich war ziemlich überrascht, dass ich es nicht bereits in den Antworten gesehen habe, da alle mir bekannten Compiler es unterstützen, aber viele Programmierer scheinen es zu ignorieren. Manchmal ist es sehr praktisch und nicht nur beim Schreiben von Makros.
Anwendungsfall, den ich in meinem aktuellen Code habe: Ich habe eine
#define PATH "/some/path/"
in einer Konfigurationsdatei (wirklich wird sie durch das Makefile festgelegt). Jetzt möchte ich den vollständigen Pfad einschließlich der Dateinamen zum Öffnen von Ressourcen erstellen. Es geht nur um:Anstelle des schrecklichen, aber sehr häufigen:
Beachten Sie, dass die übliche schreckliche Lösung ist:
quelle
Nun, ich habe es nie benutzt und bin mir nicht sicher, ob ich es jemals jemandem empfehlen würde, aber ich denke, diese Frage wäre unvollständig, ohne Simon Tathams Co-Routine-Trick zu erwähnen .
quelle
Beim Initialisieren von Arrays oder Aufzählungen können Sie nach dem letzten Element in der Initialisierungsliste ein Komma setzen. z.B:
Dies wurde getan, damit Sie sich beim automatischen Generieren von Code keine Gedanken über das Entfernen des letzten Kommas machen müssen.
quelle
Strukturzuordnung ist cool. Viele Leute scheinen nicht zu erkennen, dass Strukturen auch Werte sind und herum zugewiesen werden können, es besteht keine Notwendigkeit, sie zu verwenden
memcpy()
, wenn eine einfache Zuweisung den Trick macht.Betrachten Sie beispielsweise eine imaginäre 2D-Grafikbibliothek, die einen Typ definiert, der eine (ganzzahlige) Bildschirmkoordinate darstellt:
Jetzt tun Sie Dinge, die möglicherweise "falsch" aussehen, z. B. eine Funktion schreiben, die einen aus Funktionsargumenten initialisierten Punkt erstellt und wie folgt zurückgibt:
Dies ist sicher, solange (natürlich) der Rückgabewert mithilfe der Strukturzuweisung durch den Wert kopiert wird:
Auf diese Weise können Sie recht sauberen und objektorientierten Code schreiben, alles in einfachem Standard C.
quelle
Seltsame Vektorindizierung:
quelle
C-Compiler implementieren einen von mehreren Standards. Ein Standard bedeutet jedoch nicht, dass alle Aspekte der Sprache definiert sind. Das Gerät von Duff ist beispielsweise eine beliebte "versteckte" Funktion, die so populär geworden ist, dass moderne Compiler über einen speziellen Erkennungscode verfügen, um sicherzustellen, dass Optimierungstechniken den gewünschten Effekt dieses häufig verwendeten Musters nicht beeinträchtigen.
Im Allgemeinen wird von versteckten Funktionen oder Sprachtricks abgeraten, wenn Sie auf der Rasierklinge der C-Standards arbeiten, die Ihr Compiler verwendet. Viele solcher Tricks funktionieren nicht von einem Compiler zum anderen, und häufig schlagen diese Funktionen von einer Version einer Compilersuite eines bestimmten Herstellers zu einer anderen Version fehl.
Verschiedene Tricks, die C-Code gebrochen haben, umfassen:
Andere Probleme und Probleme, die auftreten, wenn Programmierer Annahmen über Ausführungsmodelle treffen, die alle in den meisten C-Standards als "compilerabhängiges" Verhalten angegeben sind.
quelle
Wenn Sie sscanf verwenden, können Sie% n verwenden, um herauszufinden, wo Sie weiterlesen sollten:
Anscheinend können Sie keine weitere Antwort hinzufügen, daher werde ich hier eine zweite einfügen. Sie können "&&" und "||" verwenden. als Bedingungen:
Dieser Code gibt Folgendes aus:
quelle
Die Verwendung von INT (3) zum Festlegen des Haltepunkts am Code ist mein absoluter Favorit
quelle
Meine bevorzugte "versteckte" Funktion von C ist die Verwendung von% n in printf, um auf den Stapel zurückzuschreiben. Normalerweise werden die Parameterwerte von printf basierend auf der Formatzeichenfolge aus dem Stapel entfernt, aber% n kann sie zurückschreiben.
Lesen Sie hier Abschnitt 3.4.2 . Kann zu vielen bösen Schwachstellen führen.
quelle
Überprüfung der Annahmen zur Kompilierungszeit mithilfe von Aufzählungen: Dummes Beispiel, kann aber für Bibliotheken mit konfigurierbaren Konstanten zur Kompilierungszeit sehr nützlich sein.
quelle
#define CompilerAssert(exp) extern char _CompilerAssert[(exp)?1:-1]
)Gcc (c) bietet einige unterhaltsame Funktionen, die Sie aktivieren können, z. B. verschachtelte Funktionsdeklarationen und die Form a ?: B des Operators ?:, Die a zurückgibt, wenn a nicht falsch ist.
quelle
Ich habe kürzlich 0 Bitfelder entdeckt.
welches ein Layout von geben wird
statt ohne: 0;
Das Feld mit der Breite 0 gibt an, dass die folgenden Bitfelder für die nächste atomare Entität gesetzt werden sollen (
char
)quelle
Makros mit variablen Argumenten im C99-Stil, auch bekannt als
das würde gerne verwendet werden
Hier verwende ich auch den Stringize-Operator und die String-Konstanten-Konzentration, andere Funktionen, die ich wirklich mag.
quelle
In einigen Fällen sind auch automatische Variablen mit variabler Größe nützlich. Diese wurden in nC99 hinzugefügt und werden seit langem in gcc unterstützt.
Am Ende befindet sich ein Puffer auf dem Stapel mit Platz für den Protokollheader mit fester Größe sowie Daten mit variabler Größe. Sie können den gleichen Effekt mit alloca () erzielen, aber diese Syntax ist kompakter.
Sie müssen sicherstellen, dass extraPadding ein angemessener Wert ist, bevor Sie diese Routine aufrufen. Andernfalls wird der Stapel gesprengt. Sie müssen die Argumente überprüfen, bevor Sie malloc oder eine andere Speicherzuweisungstechnik aufrufen. Dies ist also nicht wirklich ungewöhnlich.
quelle