Ich habe einen Java-Hintergrund (aus meinen CS-Kursen) und ein Semester C++. Ich beende gerade ein OpenCV-Projekt für meine Co-Op, das in reinem C ist, also bin ich etwas spät dran, diese Frage zu stellen.
Was sind die Designprozesse und Codierungsstandards für reines C?
Ich bin vertraut mit objektorientierter Programmierung, Design und Best Practices. Ich bin nur ein bisschen ratlos bei einer nicht objektorientierten Sprache wie C. Jede einzelne Variable und Funktion scheint global zu sein. Es fühlt sich für mich wie ein echtes Durcheinander an.
Vielleicht interessieren Sie sich für die Antworten auf eine ähnliche Frage, die ich vor nicht allzu langer Zeit gestellt habe. Wenn Sie außerdem an C-Styleguides interessiert sind, sollten Sie einen Blick darauf werfen diese Seite da es ein Repository für C (und C++) Styleguides ist. Wenn Sie Lust auf a gut lachenwerfen Sie bitte einen Blick auf die NASA-C-Styleguide. Werfen Sie insbesondere einen Blick auf die fest Kommentar … Sie werden wissen, von wem ich spreche. Bitte keine Kommentare wie diese schreiben.
Ich persönlich empfehle die Styleguide für Indian Hill C mit einigen Modifikationen. Außerdem möchten Sie vielleicht das Buch kaufen C-Schnittstellen und Implementierungen wenn Sie Probleme haben, umfangreiche Programme in C zu entwerfen.
Ich glaube ehrlich gesagt nicht, dass eine Reihe von Antworten auf StackOverflow Ihnen beibringen wird, wie man gut strukturierte C-Programme entwirft und schreibt. Sie müssen ein gutes Buch lesen, und das Offensichtliche zum Lesen ist Die Programmiersprache C von Kernighan & Ritchie.
Ich habe keine Berufserfahrung in C (nur in C++), also nehmt meine Ratschläge, Tricks und Tipps nicht zu ernst, da sie “objektorientiert” sind.
Fast Objekt C?
Die Simulation grundlegender objektähnlicher Merkmale kann einfach durchgeführt werden:
Deklarieren Sie im Header Ihren Typ vorwärts, geben Sie ihn ein und deklarieren Sie die “Methoden”. Zum Beispiel:
/* MyString.h */
#include <string.h>
/* Forward declaration */
struct StructMyString ;
/* Typedef of forward-declaration (note: Not possible in C++) */
typedef struct StructMyString MyString ;
MyString * MyString_new() ;
MyString * MyString_create(const char * p_pString) ;
void MyString_delete(MyString * p_pThis) ;
size_t MyString_length(const MyString * p_pThis) ;
MyString * MyString_copy(MyString * p_pThis, const MyString * p_pSource) ;
MyString * MyString_concat(MyString * p_pThis, const MyString * p_pSource) ;
const char * MyString_get_c_string(const MyString * p_pThis) ;
MyString * MyString_copy_c_string(MyString * p_pThis, const char * p_pSource) ;
MyString * MyString_concat_c_string(MyString * p_pThis, const char * p_pSource) ;
Sie werden sehen, dass jeder Funktion ein Präfix vorangestellt ist. Ich wähle den Namen der “Struktur”, um sicherzustellen, dass es keine Kollision mit einem anderen Code gibt.
Sie werden auch sehen, dass ich “p_pThis” verwendet habe, um bei der OO-ähnlichen Idee zu bleiben.
Definieren Sie in der Quelldatei Ihren Typ und definieren Sie die Funktionen:
/* MyString.c */
#include "MyString.h"
#include <string.h>
#include <stdlib.h>
struct StructMyString
{
char * m_pString ;
size_t m_iSize ;
} ;
MyString * MyString_new()
{
MyString * pMyString = malloc(sizeof(MyString)) ;
pMyString->m_iSize = 0 ;
pMyString->m_pString = malloc((pMyString->m_iSize + 1) * sizeof(char)) ;
pMyString->m_pString[0] = 0 ;
return pMyString ;
}
/* etc. */
Wenn Sie “private” Funktionen (oder private globale Variablen) wollen, deklarieren Sie sie in der C-Quelle als statisch. Auf diese Weise sind sie von außen nicht sichtbar:
static void doSomethingPrivate()
{
/* etc. */
}
static int g_iMyPrivateCounter = 0 ;
Wenn Sie Erbschaft wollen, dann sind Sie fast am Arsch. Wenn Sie glaubten, alles in C sei global, einschließlich Variablen, dann sollten Sie mehr Erfahrung in C sammeln, bevor Sie überhaupt versuchen, darüber nachzudenken, wie Vererbung simuliert werden könnte.
Sonstiges Tipps
Vermeiden Sie mehrere Codepfade.
Beispielsweise ist eine mehrfache Rückgabe riskant. Zum Beispiel:
void doSomething(int i)
{
void * p = malloc(25) ;
if(i > 0)
{
/* this will leak memory ! */
return ;
}
free(p) ;
}
Vermeiden Sie nicht-konstante Globale
Dazu gehören “statische” Variablen (die keine statischen Funktionen sind).
Globale nicht-konstante Variablen sind fast immer eine schlechte Idee (dh ein Beispiel für eine beschissene Funktion finden Sie unter C API strtok), und wenn Sie Multithread-sicheren Code produzieren, sind sie mühsam zu handhaben.
Vermeiden Sie Namenskollisionen
Wählen Sie einen “Namespace” für Ihre Funktionen und für Ihre Definitionen. Das könnte sein:
#define GROOVY_LIB_x_MY_CONST_INT 42
void GroovyLib_dosomething() ;
Vorsicht definiert
Defines können in C nicht vermieden werden, aber sie können Seiteneffekte haben!
#define MAX(a, b) (a > b) ? (a) : (b)
void doSomething()
{
int i = 0, j = 1, k ;
k = MAX(i, j) ; /* now, k == 1, i == 0 and j == 1 */
k = MAX(i, j++) ; /* now, k == 2, i == 0 and j == 3, NOT 2, and NOT 1 !!! */
}
Initialisieren Sie Ihre Variablen
Vermeiden Sie es, Variablen zu deklarieren, ohne sie zu initialisieren:
int i = 42 ; /* now i = 42 */
int j ; /* now j can have any value */
double k ; /* now f can have any value, including invalid ones ! */
Nicht initialisierte Variablen sind Ursachen für schmerzhafte Fehler.
Kennen Sie die gesamte C-API
Die im K&R beschriebene C-API-Funktionsliste ist ziemlich klein. Sie werden die ganze Liste in 20 Minuten lesen. Diese Funktionen müssen Sie kennen.
Willst du etwas Erfahrung?
Schreiben Sie die C-API neu. Versuchen Sie beispielsweise, Ihre eigene Version der string.h-Funktionen zu schreiben, um zu sehen, wie es gemacht wird.
Sie können den Gültigkeitsbereich einer Funktion oder eines Objekts lokal für seine Quelldatei festlegen, indem Sie es als “statisch” deklarieren. Das hilft ein bisschen.
Ansonsten ist die typische Redewendung, die ich sehe, um Namespace-Konflikte zu vermeiden, eine Art Einrichtungskennung in den Namen einzufügen. Zum Beispiel könnte alles in foo.c foo_* heißen
Arbeiten Sie mit anderen guten C-Programmierern zusammen. Haben Sie eine Codeüberprüfung mit ihnen. Lassen Sie sie nicht nur Ihren Code sehen, sondern Sie sehen sich ihren Code an.
Eine gute Nachricht ist, dass Sie in C halbobjektorientiert programmieren können. Sie können Daten schützen, Zugriffsfunktionen verfügbar machen usw. Es hat vielleicht nicht die ganze Raffinesse von C++, aber von dem, was ich von C++-Code anderer Leute gesehen habe , viele Leute nutzen die Fantasie sowieso nicht. Mit anderen Worten, die Leute schreiben C-Code innerhalb einer Klasse, wo Sie in C denselben Code ohne den Klassencontainer schreiben würden.
Lesen Sie zuerst ein Buch über C-Programmierung und -Stil, K&R ist in Ordnung. Zweitens würde ich das Auschecken empfehlen CERT-Programmierstandard. Auch wenn sich diese Seite hauptsächlich auf “sichere” Codierungsstandards konzentriert, handelt es sich bei einem Großteil des Inhalts hier um allgemeine Codequalitätsstandards, die jeder befolgen sollte. Wenn Sie die hier genannten Dinge tun, wird Ihre Qualität verbessert, lästige Fehler beseitigt und als Nebeneffekt Ihr Code sicherer.
Vielleicht möchten Sie sich die Quelle des Linux-Kernels genau ansehen….. Übrigens ist es nicht das einfachste Stück Code, mit dem Sie beginnen können, aber da Sie einen Programmierhintergrund haben, kann es hilfreich sein … Als Objekt -orientierten Programmierern finden Sie möglicherweise insbesondere die Fehlerbehandlung in C eine schwierige Aufgabe. Vielleicht hilft das weiter—> http://www.freetype.org/david/reliable-c.html
Funktionen sind “global”, aber Variablen können unterschiedliche Geltungsbereiche haben …
– Paercebal
11. August 2009 um 19:47 Uhr
Funktionen können natürlich global oder dateistatisch sein
– anon
11. August 2009 um 19:48 Uhr
@GMan, wenn genügend verschiedene Benutzer die Frage bearbeiten, wird sie von selbst zu CW. Ich habe meine CW-Stimme hinzugefügt…
– bdonlan
11. August 2009 um 19:51 Uhr
@bdonian Ein grober Missbrauch der Bearbeitungsfunktion, IMHO.
– anon
11. August 2009 um 20:06 Uhr