2015-05-31 16 views
25

Il seguente programma stampa lo stesso numero due volte il gcc 4.8.2:Le parentesi fanno la differenza quando si determina la dimensione di un array?

#include <stdio.h> 

int main() 
{ 
    char a[13]; 
    printf("sizeof a is %zu\n", sizeof a); 
    printf("sizeof(a) is %zu\n", sizeof(a)); 
} 

Secondo this reddit post, gcc non è conforme agli standard in questo senso, perché un'espressione tra parentesi non è sulla lista delle eccezioni per quando decadimento da matrice a puntatore non accade.

Questo ragazzo è corretto? Ecco la citazione norma pertinente:

Tranne quando è l'operando dell'operatore sizeof o unario & dell'operatore, o è una stringa di caratteri letterali utilizzate per inizializzare una matrice di tipo di carattere, o è una vasta stringa letterale utilizzato per inizializzare un array con tipo di elemento compatibile con wchar_t, un lvalue che ha tipo 'array di tipo' viene convertito in un'espressione che ha tipo 'puntatore a tipo' che punta al membro iniziale dell'oggetto array e non è un lvalue .

Giusto per essere chiari, egli sostiene che (a) dovrebbe innescare decadimento gamma-to-pointer, perché parentesi non sono coperti nella lista di cui sopra (sizeof operatore, unario & operatore, stringa letterale come inizializzatore).

+7

No, quel ragazzo è seriamente confuso –

+1

Con le sue stesse parole, sarei d'accordo con * confusamente terminato * – chqrlie

+1

Non ho trattato questa roba per circa 15 anni, ma sto sicuramente ricordando uno scenario con, penso , 'sizeof' dove la presenza o l'assenza di parentesi era significativa - determinato se si stesse prendendo la dimensione del puntatore o la dimensione dell'elemento o qualcosa del genere. –

risposta

24

Se parentesi apparentemente ridondanti influenzano la semantica di un programma è un problema di vecchia data nello standard C che ancora non è stato adeguatamente risolto.

Si dice comunemente che ((void*)0) non sia tecnicamente una costante di puntatore nullo, perché non esiste una regola che dica che una costante di puntatore nullo tra parentesi è una costante di puntatore nullo.

Alcuni compilatori generano un errore per char s[] = ("abc");, perché mentre un array di caratteri può essere inizializzato da un valore letterale stringa, quella regola non include i valori letterali stringa parentesi.

Ci sono molti esempi simili. Hai trovato uno di loro.

Da quello che posso dire, il consenso è fondamentalmente che la regola dovrebbe essere essere ciò che fa C++, ma ciò che C non ha mai adottato formalmente.C++ fa un'espressione tra parentesi funzionalmente equivalente all'espressione non tra parentesi, con alcune eccezioni esplicitamente dichiarate. Ciò coprirebbe tutti quei problemi contemporaneamente.

Quindi tecnicamente, il ragazzo potrebbe essere considerato corretto, ma è un'interpretazione eccessivamente rigida dello standard che nessuno segue realmente, poiché è risaputo che lo standard è semplicemente difettoso qui.

+1

È abbastanza grave che non si possa semplicemente testare se un identificatore si riferisce a un puntatore o ad un array, rendendo 'errorof (argv)' soggetto ad errori. Sarebbe anche peggio se 'sizeof (argv)' e 'sizeof argv' fossero valutati in una cosa diversa. Ciò che è veramente necessario è un operatore 'countof (a)' che valuta il numero di elementi di un array e genera un errore di tempo di compilazione quando applicato a un array non. – chqrlie

+0

@chqrlie Preferirei avere i blocchi necessari per creare 'countof' (di solito li ho visti chiamati' LENGTHOF', ma entrambi funzionano). Con '_Generic' di C11, tutto ciò che manca, penso, è qualcosa come' decltype' di C++ 11. Se lo avessimo, saremmo in grado di verificare staticamente che il tipo di 'argv' è diverso da' & * argv'. – hvd

+0

Trovo 'LENGTHOF (a)' meno attraente a causa della potenziale confusione tra la lunghezza di una stringa e la dimensione della matrice di byte corrispondente. Sono d'accordo che gli strumenti sarebbero sufficienti, come questo: 'typeof (a)! = Typeof (& * (a))' – chqrlie

21

Da C99, 6.5.1, sulle espressioni tra parentesi:

suo tipo e valore sono identici a quelli dell'espressione unparenthesized.

A prima vista, sembrerebbe che questo conflitto con l'elenco delle eccezioni ti riferisci (6.3.2.1):

Tranne quando è l'operando dell'operatore sizeof o unario & operatore, oppure è una stringa letterale utilizzato per inizializzare un array, un'espressione che ha tipo "array di tipo" viene convertito in un'espressione di tipo "puntatore a" ...

Tuttavia, questo elenco è nel contesto di operatori/operandi; le parentesi non sembrano essere considerate un operatore (in base alla categorizzazione implicita nella struttura della sezione 6.5).

+0

È possibile collegarsi direttamente alla sezione grazie ad una coraggiosa anima che ha passato molte ore a convertire il documento pdf in html: http://www.iso-9899.info/n1256.html#6.5.1p5, http: // www. iso-9899.info/n1256.html#6.3.2.1p3 ... o per C11: http://www.iso-9899.info/n1570.html#6.5.1p5, http: //www.iso-9899 .info/n1570.html # 6.3.2.1p3 – Sebivor

+1

C11 sembra consentire un _type-name_ solo nella versione parenthized (6.5.3 # 1). A tale proposito, una versione con parentesi attorno a un'espressione _unary_ sarebbe sintatticamente la versione non personalizzata con la parentesi che fa parte della _unary-expression_. Rende il compito del compilatore non più facile. Modifica: stesso per C99. – Olaf