Qual è l'uso idiomatico di blocchi arbitrari in C?

15

Un blocco è un elenco di istruzioni da eseguire. Gli esempi di dove i blocchi si presentano in C sono dopo un'istruzione while e in if if

while( boolean expression)
    statement OR block

if (boolean expression)
    statement OR block

C consente anche di annidare un blocco in un blocco. Posso usare questo per riutilizzare i nomi delle variabili, supponiamo che mi piaccia davvero x "

int x = 0;
while (x < 10)
{
    {
        int x = 5;
        printf("%d",x)
    }
    x = x+1;
}

stamperà il numero 5 dieci volte. Immagino di poter vedere situazioni in cui è consigliabile mantenere basso il numero di nomi di variabili. Forse nella macro espansione. Tuttavia, non riesco a vedere alcun motivo per aver bisogno di questa funzione. Qualcuno può aiutarmi a capire gli usi di questa funzionalità fornendo alcuni idiomi in cui viene utilizzata.

    
posta Jonathan Gallagher 22.12.2013 - 21:00
fonte

7 risposte

8

L'idea non è di mantenere basso il numero di nomi di variabili o incoraggiare in altro modo il riutilizzo dei nomi, ma piuttosto di limitare l'ambito delle variabili. Se hai:

int x = 0;
//...
{
    int y = 3;
    //...
}
//...

quindi l'ambito di y è limitato al blocco, il che significa che puoi dimenticartene prima o dopo il blocco. Vedete questo usato più spesso in connessione con loop e condizionali. Lo vedi anche più spesso in linguaggi C-like come C ++, in cui una variabile che va fuori dal campo di applicazione causa la sua distruzione.

    
risposta data 22.12.2013 - 21:19
fonte
16

Perché nei vecchi tempi di C le nuove variabili potevano essere dichiarate solo in un nuovo blocco.

In questo modo i programmatori potevano introdurre nuove variabili nel mezzo di una funzione senza farla fuoriuscire e riducendo al minimo l'uso dello stack.

Con gli ottimizzatori di oggi è inutile e un segno che devi prendere in considerazione l'estrazione del blocco nella sua funzione.

In un'istruzione switch, è utile racchiudere i casi nei propri blocchi per evitare la doppia dichiarazione.

In C ++ è molto utile ad esempio per i lock guard RAII e garantisce che i distruttori eseguano il lock di rilascio quando l'esecuzione esce dal campo di applicazione e continua a fare altre cose al di fuori della sezione critica.

    
risposta data 22.12.2013 - 21:34
fonte
2

Non guarderei come blocchi "arbitrari". Non si tratta di una funzionalità pensata tanto per l'uso da parte degli sviluppatori, ma il modo in cui C utilizza i blocchi consente di utilizzare lo stesso costrutto di blocco in un numero di punti con la stessa semantica. Un blocco (in C) è un nuovo ambito e le variabili che lo lasciano vengono eliminate. Questo è uniforme indipendentemente da come viene usato il blocco.

In altre lingue, questo non è il caso. Questo ha il vantaggio di consentire meno abusi come quello che mostri, ma lo svantaggio che i blocchi si comportano diversamente a seconda del contesto in cui si trovano.

Raramente ho visto blocchi autonomi usati in C o C ++ - spesso quando c'è una struttura di grandi dimensioni o un oggetto che rappresenta una connessione o qualcosa di cui si vuole forzare la distruzione. Di solito questo è un suggerimento che la tua funzione sta facendo troppe cose e / o è troppo lunga.

    
risposta data 22.12.2013 - 21:08
fonte
2

Devi rendertene conto, i principi di programmazione che sembrano ovvi ora non erano sempre così. Le buone pratiche C dipendono molto da quanti anni sono i tuoi esempi. Quando C è stato introdotto per la prima volta, la suddivisione del codice in piccole funzioni è stata considerata troppo inefficiente. Dennis Ritchie fondamentalmente ha mentito e ha detto che le chiamate di funzione erano davvero efficienti in C (non erano in quel momento), il che significava che le persone iniziarono ad usarle di più, anche se i programmatori C in qualche modo non hanno mai completamente superato una cultura dell'ottimizzazione prematura. p>

È è una buona pratica di programmazione anche oggi per limitare il più possibile l'ambito delle tue variabili. Al giorno d'oggi, di solito lo facciamo creando una nuova funzione, ma se le funzioni sono considerate costose, l'introduzione di un nuovo blocco è un modo logico per limitare l'ambito senza il sovraccarico di una chiamata di funzione.

Tuttavia, ho iniziato a programmare in C più di 20 anni fa, quando dovevi dichiarare tutte le tue variabili nella parte superiore di un oscilloscopio, e non ricordo che lo shadowing variabile sia mai stato considerato di buon livello. Redeclaring in due blocchi uno dopo l'altro come in un'istruzione switch, sì, ma non in ombra. Forse se la variabile era già utilizzata e il nome specifico era altamente idiomatico per l'API che stai chiamando, ad esempio dest e src in strcpy , ad esempio.

    
risposta data 23.12.2013 - 05:27
fonte
1

I blocchi arbitrari sono utili per introdurre variabili intermedie sono usati solo in casi speciali di calcolo.

Questo è un modello comune nel calcolo scientifico, dove numerico procedure in genere:

  1. si basano su molti parametri o quantità intermedie;
  2. devono occuparsi di molti casi speciali.

A causa del secondo punto, è utile introdurre temporaneamente variabili di portata limitata, che si ottiene usando un blocco arbitrario o introducendo una funzione ausiliaria.

Anche se l'introduzione di una funzione ausiliaria può sembrare un no brainer o una best practice per seguire ciecamente in realtà c'è poco benefici per farlo in questa particolare situazione.

Perché ci sono molti parametri e quantità intermedie, noi voglio introdurre una struttura per passare queste alle auxilarie la funzione.

Tuttavia, dal momento che vogliamo essere coerenti con le nostre pratiche, non lo faremo introdurre solo una funzione ausiliaria, ma diverse. Quindi, noi introdurre strutture ad-hoc che trasmettano parametri per ogni funzione, che introducono un sacco di code-overhead per spostare i parametri indietro e avanti, o introduciamo un uno domina tutti loro struttura del foglio di lavoro, che contiene tutte le nostre variabili ma sembra un grabpack di bit senza coerenza, dove in qualsiasi momento solo la metà dei parametri avere un significato interessante.

Quindi queste strutture ausiliarie sono in genere ingombranti e usarli significa scegliere tra code-bloat o introdurre un astrazione la cui portata è troppo ampia e indebolire il significato del programma, invece di potenziarlo .

L'introduzione di funzioni ausiliarie potrebbe facilitare il collaudo dell'unità di programma introducendo una granularità di prova più fine ma combinando unità test per le non procedure di basso livello e test di regressione in la forma di confronti (con numdiff) di tracce numeriche del le procedure fa un lavoro altrettanto valido.

    
risposta data 23.12.2013 - 09:39
fonte
0

In generale, il riutilizzo dei nomi delle variabili in questo modo causa troppa confusione ai futuri lettori del tuo codice. È meglio nominare semplicemente la variabile interna qualcos'altro. In effetti, il linguaggio C # non consente in modo specifico l'uso delle variabili in questo modo.

Ho potuto vedere come l'uso di blocchi nidificati nelle espansioni di macro sarebbe utile per prevenire le collisioni di nomi variabili.

    
risposta data 22.12.2013 - 21:03
fonte
0

È per l'ambito di cose che normalmente non hanno scope a quel livello. Questo è estremamente raro, e in generale il refactoring è una scelta migliore, ma l'ho incontrato una o due volte in passato nelle istruzioni switch:

switch(foo) {
   case 1:
      {
         // bar
      }
   case 2:
   case 3:
      // baz
      break;
   case 4:
   case 5:
      // bang
      break;
}

Quando consideri la manutenzione, il refactoring dovrebbe essere bilanciato con tutte le implementazioni di fila, purché ciascuna sia lunga solo un paio di righe. Può essere più facile averli tutti insieme, piuttosto che un elenco di nomi di funzioni.

Nel mio caso, se ricordo bene, la maggior parte dei casi erano leggere variazioni dello stesso codice - tranne che uno di essi era identico a un altro, solo con una pre-elaborazione extra. Uno switch ha finito per essere la forma più semplice per il codice, e l'ambito extra ha permesso l'uso di variabili locali extra che non avrebbe dovuto preoccuparsi di ulteriori casi (come i nomi di variabili che si sovrappongono accidentalmente a uno definito prima dello switch ma usato più tardi).

Si noti che l'istruzione switch è semplicemente l'uso che ho incontrato. Sono sicuro che può essere applicato anche altrove, ma non ricordo di averli visti usati in quel modo in modo efficace.

    
risposta data 22.12.2013 - 22:37
fonte

Leggi altre domande sui tag