Convenzioni di denominazione utilizzate per variabili e funzioni in C [chiuso]


13

Durante la codifica di un grande progetto in CI si è verificato un problema. Se continuo a scrivere più codice, ci sarà un momento in cui sarà difficile organizzare il codice. Voglio dire che la denominazione di funzioni e variabili per le diverse parti del programma può sembrare confusa.

Quindi stavo pensando se ci sono utili convenzioni di denominazione che posso usare per le variabili e le funzioni C?

La maggior parte delle lingue suggerisce una convenzione di denominazione. Ma per C l'unica cosa che ho letto finora è che i nomi dovrebbero essere descrittivi per la leggibilità del codice.

MODIFICARE:

Esempi di alcuni esempi di convenzioni di denominazione suggerite:

Ho letto alcune altre convenzioni di denominazione per Java da qualche parte, ma non ricordo dove.


Cita alcuni esempi di lingue con convenzioni di denominazione suggerite. E dove possiamo trovare quelle convenzioni di denominazione.
Filippo,

@Philip Esempi aggiunti
Aseem Bansal,

1
Non ci dovrebbero essere problemi con le variabili poiché non si usano i globali. E per i nomi di funzione: se il nome del modulo è order.c, si potrebbe denominare le funzioni order_add(), order_del()e così via. Potrebbero esserci vecchi sistemi che ti dicono che il nome deve essere univoco all'interno dei primi 8 caratteri. Quando passi a c ++ più tardi per caso, ti piacerà scrivere order::add()e order::del()poi.
ott--

Risposte:


17

Se continuo a scrivere più codice, ci sarà un momento in cui sarà difficile organizzare il codice.

Questo è il tuo problema: ottenere l'organizzazione giusta e lo stile dovrebbe fluire più facilmente.

Non aspettare per organizzare il tuo codice: mantieni il tuo codice organizzato man mano che procedi. Sebbene la lingua non lo faccia per te, il codice dovrebbe comunque essere organizzato in moduli con basso accoppiamento e alta coesione.

Questi moduli forniscono quindi naturalmente uno spazio dei nomi. Abbrevia il nome del modulo (se è lungo) e prefiggi i nomi delle funzioni con il loro modulo per evitare collisioni.

A livello di singoli identificatori, questi sono all'incirca in ordine crescente di soggettività:

  1. scegli una convenzione e mantienila
    • ad esempio, function_like_this(struct TypeLikeThis variable)è comune
  2. evitare assolutamente la notazione ungherese (scusate JNL)

    • a meno che tu non sia disposto a usarlo come previsto originariamente, il che significa la notazione di app di Simonyi piuttosto che la terribile versione dei sistemi

      Perché? Potrei scrivere un saggio a riguardo, ma suggerirò invece di leggere questo articolo di Joel Spolsky, e poi di cercare ancora un po 'se sei interessato. C'è un link al documento originale di Simonyi in fondo.

  3. evitare i typedef dei puntatori a meno che non siano tipi di cookie veramente opachi - confondono solo le cose

    struct Type *ok;
    typedef struct Type *TypePtr;
    TypePtr yuck;

    Cosa intendo per tipo di cookie opaco ? Intendo qualcosa usato all'interno di un modulo (o libreria, o qualsiasi altra cosa) che deve essere passato al codice client, ma quel codice client non può essere usato direttamente. Lo restituisce alla libreria.

    Ad esempio, una libreria di database potrebbe esporre un'interfaccia simile

    /* Lots of buffering, IPC and metadata magic held in here.
       No, you don't get to look inside. */
    struct DBContextT;
    /* In fact, you only ever get a pointer, so let's give it a nice name */
    typedef struct DBContexT *DBContext;
    
    DBContext db_allocate_context(/*maybe some optional flags?*/);
    void db_release_context(DBContext);
    int db_connect(DBContext, const char *connect);
    int db_disconnect(DBContext);
    int db_execute(DBContext, const char *sql);

    Ora, il contesto è opaco per il codice client, perché non puoi guardarti dentro. Lo passi di nuovo in biblioteca. Qualcosa di simile FILEè anche opaco e un descrittore di file intero è anche un cookie , ma non è opaco.


Una nota sul design

Ho usato la frase accoppiamento basso e alta coesione sopra senza spiegazioni, e mi sento un po 'male per questo. Puoi cercarlo e probabilmente trovare dei buoni risultati, ma proverò ad affrontarlo brevemente (di nuovo, potrei scrivere un saggio ma cercherò di non farlo).

La libreria DB di cui sopra mostra un accoppiamento basso perché espone una piccola interfaccia al mondo esterno. Nascondendo i suoi dettagli di implementazione (in parte con il trucco del cookie opaco), impedisce al codice client di dipendere da tali dettagli.

Immagina invece del cookie opaco, dichiariamo la struttura del contesto in modo che il suo contenuto sia visibile e che includa un descrittore di file socket per una connessione TCP al database. Se successivamente si modifica l'implementazione per supportare l'utilizzo di un segmento di memoria condivisa quando il DB è in esecuzione sullo stesso computer, è necessario ricompilare il client anziché ricollegarlo. Ancora peggio, il client avrebbe potuto iniziare a utilizzare il descrittore di file, ad esempio chiamando setsockoptper modificare la dimensione del buffer predefinita e ora ha bisogno anche di una modifica del codice. Tutti questi dettagli dovrebbero essere nascosti all'interno del nostro modulo laddove possibile, e ciò garantisce un basso accoppiamento tra i moduli.

L'esempio mostra anche un'elevata coesione , in quanto tutti i metodi nel modulo riguardano lo stesso compito (accesso al DB). Ciò significa che solo il codice che deve conoscere i dettagli di implementazione (ovvero i contenuti dei nostri cookie) può effettivamente accedervi, il che semplifica il debug.

Puoi anche vedere che avere una sola preoccupazione ha reso facile scegliere un prefisso per raggruppare queste funzioni.

Ora, dire che questo esempio è buono è facile (soprattutto perché non è nemmeno completo), ma non ti aiuta immediatamente. Il trucco è guardare, mentre scrivi ed estendi il tuo codice, per funzioni che fanno cose simili o operano sugli stessi tipi (che potrebbero essere candidati per il proprio modulo), e anche per funzioni che fanno molte cose separate che non sono " Sono davvero imparentati e potrebbero essere candidati alla scissione.


Potete aiutarmi a capire perché l'ungherese è evitato? Solo curioso di saperne di più. :)
JNL,

@JNL: un commento è troppo breve per spiegare correttamente. Ti suggerisco di pubblicarlo come una nuova domanda.
Bart van Ingen Schenau,

with low coupling and high cohesion. Cosa significa? E per favore, spiega sui tipi di cookie opachi. Non ho idea di cosa significhi.
Aseem Bansal,

Ho cercato di affrontare entrambi brevemente, e sinceramente non sono riuscito a brevità. Spero che dovrebbe iniziare comunque.
Inutile

Sto rispondendo dopo alcuni giorni. Scusa per quello. Ho letto la tua descrizione di low coupling and high cohesion. Quindi, fondamentalmente, significa incapsulare le cose quando posso e dovrebbe essere fatto in modo tale che le funzioni effettivamente necessarie debbano avere accesso. Alcune cose sono andate oltre la mia testa, ma penso ancora di aver capito bene.
Aseem Bansal,

5

Secondo me il 90% del problema di denominazione viene risolto se si tengono a mente tre cose: a) rendere i nomi delle variabili e delle funzioni il più descrittivi possibile, b) essere coerenti in tutto il codice (ovvero, se una funzione è denominata addNumbers, a la seconda funzione dovrebbe essere denominata multiplyNumbers e non numbersMul) ec) cercare di abbreviare i nomi, se possibile, poiché è necessario digitarli.

Detto questo, se vuoi dare un'occhiata ad altri aspetti su questo argomento, la pagina Wikipedia sulle Convenzioni di denominazione ha un buon elenco di cose che dovresti tenere a mente. Ha anche una sezione su C e C ++:

In C e C ++, le parole chiave e gli identificatori di libreria standard sono per lo più in minuscolo. Nella libreria standard C, i nomi abbreviati sono i più comuni (ad es. Isalnum per una funzione che verifica se un carattere è alfanumerico), mentre la libreria standard C ++ utilizza spesso un carattere di sottolineatura come separatore di parole (ad esempio out_of_range). Gli identificatori che rappresentano le macro sono, per convenzione, scritti usando solo lettere maiuscole e caratteri di sottolineatura (questo è correlato alla convenzione in molti linguaggi di programmazione dell'uso di identificatori maiuscoli per le costanti). I nomi che contengono un carattere di sottolineatura doppio o che iniziano con un carattere di sottolineatura e una lettera maiuscola sono riservati per l'implementazione (compilatore, libreria standard) e non devono essere utilizzati (ad esempio, riservato__ o _Reserved). [5] [6] Questo è superficialmente simile allo strapping, ma la semantica differisce:


3
"cerca di abbreviare i nomi se possibile" Usa un IDE con il completamento automatico, quindi i nomi delle tue funzioni possono essere lunghi e descrittivi quanto devono essere come devi solo digitare una volta.
Joel,

1
@Joel consiglio terribile. Non tutti useranno lo stesso IDE come te.
James,

6
@James Non ne hanno bisogno, possono semplicemente usare qualsiasi IDE decente. Quindi non devi sacrificare la chiarezza per la produttività.
Gioele,

Il termine IDE è allungato un po 'sottile ora un giorno. Tecnicamente Notepad ++ è un IDE perché puoi configurarlo per compilare ed eseguire il tuo progetto, ma è principalmente un editor di testo. E si completa automaticamente.
Filippo

5

L'unico vincolo duro in C è che non ci sono spazi dei nomi. Pertanto, è necessario trovare un modo per rendere la rename()funzione del vostro file system libreria distinta dalla rename()funzione del vostro supporto biblioteca. La solita soluzione è un prefisso, come ad esempio: filesystem_rename()e media_rename().

L'altro consiglio generale è: rimanere coerenti all'interno di un progetto o di un team. La leggibilità sarà migliorata.


+1: Ciò è particolarmente vero per i simboli esportati in una libreria. "Mi dispiace, ma quella libreria di filesystem non si
adatta

2

SE STAI CERCANDO UN FORMATO ACCETTATO GLOBALMENTE

MISRA / JSF / AUTOSAR copre quasi il 100% di qualsiasi standard industriale per la denominazione e l'organizzazione del codice C / C ++. Il problema è che non saranno liberi di procurarsi, cioè ciascuna delle guide costa un po 'di soldi. So che il libro standard di codifica MISRA 2008 C / C ++ probabilmente costa circa 50 USD.

Puoi pensare a questi come ai riferimenti di Harvard per la bibliografia e la lettura di addizioni quando scrivi un diario. Ho usato MISRA ed è un buon modo per nominare le tue funzioni e variabili e organizzarle per un uso corretto.

SE STAI CERCANDO QUALCOSA DI TEMPORANEO

I riferimenti che hai fornito per Python e Java sono okay immagino. Ho visto persone che adottano commenti, nomi e organizzazione di codice in stile javadoc. In effetti, nel mio ultimo progetto, ho dovuto scrivere codice C ++ in funzioni / nomi variabili simili a Java. Due ragioni dietro questo:

1) Apparentemente era più facile da seguire.

2) I requisiti del codice di produzione non hanno toccato il terreno degli standard di sistema software critici per la sicurezza.

3) Il codice legacy era (in qualche modo) in quel formato.

4) Doxygen ha permesso commenti sytle di Javadoc. In quel momento, stavamo usando doxygen per generare documentazione per i ragazzi della produzione.

Molti programmatori saranno contrari a questo, ma personalmente ritengo che non ci sia nulla di male nell'adottare la funzione / denominazione delle variabili in stile javadoc in C / C ++. SÌ DEL CORSO, le pratiche di organizzazione del controllo del flusso, sicurezza del filo, ecc. Devono essere affrontate indipendentemente. Tuttavia, non sono un candidato qui. Inoltre non so quanto siano rigorosi i requisiti del formato del codice di produzione. Senza dirottarlo su un'area fuori tema, ti suggerisco di rivedere i tuoi requisiti, scoprire quanto dipendi da una specifica convenzione di denominazione e andare con una soluzione menzionata nella mia e in altre risposte

Spero che questo abbia aiutato !?


In realtà stavo chiedendo questo per i codici C personali. Ma ricorderò il tuo suggerimento.
Aseem Bansal,

@AseemBansal Personale o professionista, quelli sono buoni da imparare e anche buoni da mettere sul tuo CV :) .... A te.
hagubear

0

Poche cose importanti da considerare durante la denominazione;

  1. Guarda il tipo actionObject o ObjectAction. (Object Not for C. Ma in generale quando vai ad altri linguaggi orientati agli oggetti) Questo dovrebbe aiutare

  2. Il riposo sarebbe SICURO, breve e descrittivo di sicuro.

  3. Inoltre, ha un unico scopo per ogni variabile e funzione definita, ad esempio: se si desidera memorizzare temporaneamente un valore, nominarlo come nTempVal per int
  4. Le variabili dovrebbero essere sostantivo e i metodi dovrebbero essere verbo.

6
La notazione ungherese (che precede una variabile con lettere che indicano il tipo) non porta alla fine del dolore. Per fortuna è per lo più passato di moda.
Gort the Robot,

@StevenBurnap Era solo curioso perché si evitasse il formato ungherese? Credo che sia quello che ci hanno insegnato a scuola e ho visto questo codice anche in alcuni posti di lavoro. Quale consiglieresti se non ungherese. Grazie
JNL,

1
La migliore convenzione di denominazione è solo quella usata coerentemente, con nomi chiari e descrittivi idealmente mantenuti relativamente brevi senza abbreviazioni eccessive ed evitando prefissi ridondanti. La notazione ungherese ha una scarsa utilità effettiva, rende il codice più difficile da leggere e rende più difficile la modifica dei tipi.
Gort the Robot,

2
Ecco una descrizione dell'intento originale e dell'abominio che la notazione ungherese è diventata: joelonsoftware.com/articles/Wrong.html
Residuum

@Residuum Era un buon collegamento. Aiutato molto Apprezzalo.
JNL,

0

La maggior parte delle risposte sono buone, ma voglio dire alcune cose sulle convenzioni di denominazione per libary e file inclusi, simile all'utilizzo di spazi dei nomi in altre lingue come C ++ o Java:

Se si crea una libreria, trovare un prefisso comune per i simboli esportati, ovvero funzioni globali, typedef e variabili. Ciò impedirà gli scontri con altre librerie e identificherà le funzioni come provenienti dalle tue. Questa è un po 'di app notazioni ungheresi.

Forse vai oltre e raggruppa i tuoi simboli esportati: libcurl usa curl_ * per i simboli globali, curl_easy_ *, curl_multi_ * e curl_share_ * per le diverse interfacce. Quindi oltre ad usare curl_ * per tutte le funzioni, hanno aggiunto un altro livello di "namespace" per le diverse interfacce: chiamare una funzione curl_easy_ * su un handle curl_multi_ * ora sembra sbagliato, vedere i nomi delle funzioni su http: // curl. haxx.se/libcurl/c/

Mantenendo le regole per i simboli esportati, è necessario utilizzare quelle per le funzioni statiche nei #includefile ed: provare a trovare un prefisso comune per queste funzioni. Forse hai funzioni di utilità per stringhe statiche in un file chiamato "my_string"? Prefisso tutte quelle funzioni con my_string_ *.


Per simboli esportati intendi variabili globali, funzioni, errori di battitura, ecc., Se ho ragione. Puoi spiegarci qualcosa sul raggruppamento dei simboli esportati? Pensavo lo avessi già spiegato nel paragrafo precedente. Cosa hai aggiunto nel terzo paragrafo?
Aseem Bansal,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.