Perché C fornisce sia l'operatore virgola che il punto e virgola per separare le istruzioni?

0

Sia l'operatore virgola che il punto e virgola possono essere utilizzati per separare le istruzioni. Consideriamo questo semplice codice:

#include <stdio.h>

void do_something(int*i) {(*i)++;}

int main() {
    int i;
    scanf("%d", &i),
    i++,
    do_something((i++, &i)),
    printf("i is now %d\n", i);
    if(i == 5) printf("MAGIC NUMBER\n");
    return 0;
}

Ho sperimentato quando posso usare la virgola al posto del punto e virgola. Risulta che il punto e virgola è obbligatorio dopo int i e prima di return e if ; e, ovviamente, dopo la parentesi di chiusura.

Perché non possiamo avere solo una dichiarazione che separa la struttura? Perché non è stata combinata la virgola e il punto e virgola? Il loro uso sembra molto sovrapporsi, solo che in alcuni contesti (come prima delle parole chiave) possiamo usare solo il punto e virgola, mentre in altri contesti (come nella condizione del ciclo while o in un argomento di funzione) possiamo usare solo la virgola.

    
posta gaazkam 31.03.2018 - 00:03
fonte

4 risposte

5

In genere hai ragione che l'operatore virgola e il punto e virgola sono molto simili e servono entrambi come punto di sequenza. In alcune lingue, esiste un solo operatore che serve a entrambi gli scopi.

Tuttavia, la sintassi C è orientata all'istruzione . Le dichiarazioni e le espressioni hanno ruoli molto diversi nella grammatica. Pertanto, mentre un punto e virgola viene utilizzato per separare istruzioni , l'operatore virgola può essere utilizzato per separare espressioni . Questo ha il vantaggio di rendere la sintassi un po 'più robusta. Per esempio. questo è chiaramente un errore di sintassi:

foo(f();
g());

laddove foo((f(), g())) è valido, ma in genere di cattivo stile.

Nei linguaggi orientati all'espressione, non esistono o meno costrutti di sintassi a livello di istruzione. In queste lingue, potrebbe non esserci un operatore virgola. Per esempio. in Rust, è possibile incorporare istruzioni in un'espressione, come

foo({ f(); g() });

Qui, un operatore virgola non è più necessario. Si noti che il compilatore GCC offre un'estensione di sintassi simile per C e C ++. Nello standard C ++ 11, è possibile ottenere una sintassi simile attraverso un lambda immediatamente richiamato:

foo([]{ f(); return g(); }());
    
risposta data 31.03.2018 - 00:14
fonte
2

L'operatore virgola in C funziona sia come operatore che come separatore. Mentre lavora con le chiamate di dichiarazione e funzione, l'operatore virgola funziona come separatore.

Nel tuo caso, tecnicamente funziona come un operatore. Ogni istruzione che hai scritto separata da "," è un'espressione in sé.

Una dichiarazione come

(expression 1,expression 2,.......,expression n)

valuta tutte le espressioni e restituisce il valore dell'espressione n. Questo è il motivo per cui non puoi usarlo dopo "int i" o prima "return" o "if" poiché queste sono tutte affermazioni e non espressioni.

Puoi farlo

int i;
i,
scanf("%d", &i),
i++,
do_something((i++, &i)),
printf("i is now %d\n", i);
if(i == 5) printf("MAGIC NUMBER\n");
return 0;

perché la semplice scrittura del nome della variabile restituisce il suo valore.

Nel tuo codice tutte le istruzioni separate da "," sono valutate e il valore restituito è quello restituito dalla funzione printf () (cioè il numero di caratteri stampati)

Prova questo

int i;
int val=(
scanf("%d", &i),
i++,
do_something((i++, &i)),
printf("i is now %d\n", i));
if(i == 5) printf("MAGIC NUMBER\n");
printf("%d",val);
return 0;

Con l'input come 1, i diventa 4 e l'output è

i is now 4
11

poiché la stringa "i is now 4 \ n" contiene 11 caratteri

    
risposta data 17.06.2018 - 06:44
fonte
1

C ha due diverse nozioni: espressioni e dichiarazioni . Verifica leggendo alcuni standard C come n1570 . Si noti che le espressioni sono istruzioni, ma alcune istruzioni (ad esempio while , switch , goto ...) non sono espressioni.

Alcune lingue (come Ocaml, Scheme, Lisp, ....) hanno solo espressioni (e non hanno alcun tipo di dichiarazione). In tali lingue la sequenza richiede solo un costrutto. Si noti ad esempio che in Ocaml, il suo ; è come l'operatore virgola C, e che in Schema begin è per costruzioni di sequenze (ma accetta molti operandi). Ovviamente dovresti leggere SICP se non l'hai ancora fatto.

L' operatore virgola sta facendo (come operatore condizionale ternario , o binary + ) un'espressione composta (fatta di operandi, che sono sotto-espressioni più semplici).

Il punto e virgola separa due istruzioni all'interno di sequenze di istruzioni (che appaiono in blocchi). Ovviamente si potrebbe (ma questo non è leggibile) separare le espressioni da virgole per renderle una dichiarazione; ma è possibile (e questo è più leggibile) creare una sequenza di istruzioni di espressione.

Both the comma operator and the semicolon can be used to separate statements.

Questo è sbagliato: while(i<10) {i++;}, i non è un'istruzione C valida -ma entrambe le parti sinistra e destra di , sono istruzioni valide (quella giusta, i , essendo un dichiarazione di espressione ). I compilatori GCC e Clang accettano come estensione espressioni di dichiarazione (per qualche motivo, questa utile funzionalità esiste da più di una dozzina di anni ma non è stato standardizzato.

(Non ne sono sicuro, ma ho sentito che B , l'antenato di C, aveva solo espressioni, quindi la sua while "istruzione" valutata in -1 quando usata come espressione, potrei sbagliarmi ....)

Preferirei che C avesse solo espressioni, alcune delle quali (come while ) essendo di tipo void . Ma non è così che il linguaggio di programmazione si è evoluto e ora viene definito. Preferirei anche espressioni di istruzioni e calcolato goto -s (e qualche altro estensioni fornite da GCC) per far parte dello standard C. Non so perché non sia così! Parte del motivo è che l'essere membro del comitato di standardizzazione C è un lotto di lavoro (quasi un lavoro a tempo pieno) e attività di lobbying e costa molto, in particolare nei viaggi- (e penso che la gente di GCC non avevano abbastanza soldi per quello, quando - nel secolo precedente - introdussero le loro estensioni C).

BTW, dopo aver letto il Dragon Book e approfondito lo studio C11 standard, potresti progettare un linguaggio simile a C che ha solo espressioni (cioè, che mescola le istruzioni in espressioni come vorrei che C facesse ) e implementarlo nel compilatore giocattolo (utilizzando una libreria di generazione di codice esistente come < a href="https://gcc.gnu.org/onlinedocs/jit/"> libgccjit o LLVM o compilandolo in C ). Questo potrebbe rendere interessante un progetto studentesco di semestre (a livello di master). Cerca in lingue accademiche come Terra o Cyclone per l'ispirazione . Si potrebbe anche abbastanza facilmente (semestre di lavoro) patch GCC o Clang per aggiungere un nuovo costrutto: -let chiamalo "l'espressione dell'istruzione vuota" - dove void seguito da qualche semplice istruzione o blocco di non espressione è una nuova espressione valida di tipo void (quindi void while(i<10) {i++;}, i sarebbe un'espressione valida). Avere quell'estensione accettata dalla comunità GCC o Clang è una storia diversa (oggi, entrambe le comunità GCC e Clang non sono amichevoli alle nuove estensioni della lingua).

    
risposta data 17.06.2018 - 09:26
fonte
0

L'operatore virgola non separa le affermazioni. Combina più espressioni in un'espressione più grande: expr1, espr2 evalutes expr1, seguito da un punto di sequenza, quindi valuta expr2.

Ma la tua domanda è "perché": è perché 40 anni fa quel tipo di scorciatoia era considerato un'idea intelligente e utile. 40 anni dopo, le idee intelligenti e utili sono cambiate molto.

    
risposta data 17.06.2018 - 16:19
fonte

Leggi altre domande sui tag