Perché i array C non possono avere lunghezza 0?

13

Lo standard C11 dice che gli array, sia di dimensioni che di lunghezza variabile "devono avere un valore maggiore di zero." Qual è la giustificazione per non consentire una lunghezza di 0?

Soprattutto per array di lunghezza variabile ha perfettamente senso avere una dimensione di zero ogni tanto. È anche utile per gli array statici quando la loro dimensione proviene da una macro o l'opzione di configurazione build.

È interessante notare che GCC (e clang) fornisce estensioni che consentono array di lunghezza zero. Java consente anche array di lunghezza zero.

    
posta Kevin Cox 10.07.2014 - 23:59
fonte

6 risposte

11

Il problema che vorrei scommettere è che i C array sono solo indicatori all'inizio di un blocco di memoria assegnato. Avere una dimensione 0 significherebbe che hai un puntatore a ... niente? Non puoi avere nulla, quindi ci dovrebbe essere una scelta arbitraria. Non puoi usare null , perché i tuoi array di lunghezza 0 assomigliano a puntatori nulli. E a quel punto ogni diversa implementazione sceglierà comportamenti arbitrari diversi, portando al caos.

    
risposta data 11.07.2014 - 00:08
fonte
6

Esaminiamo in che modo un array viene solitamente disposto in memoria:

         +----+
arr[0] : |    |
         +----+
arr[1] : |    |
         +----+
arr[2] : |    |
         +----+
          ...
         +----+
arr[n] : |    |
         +----+

Si noti che non esiste un oggetto separato denominato arr che memorizza l'indirizzo del primo elemento; quando una matrice appare in un'espressione, C calcola l'indirizzo del primo elemento, se necessario.

Quindi, pensiamo a questo: una matrice a 0 elementi dovrebbe avere nessuna memoria messa da parte per essa, il che significa che non c'è niente per calcolare l'indirizzo dell'array (mettere un altro In questo modo, non esiste una mappatura dell'oggetto per l'identificatore). È come dire: "Voglio creare una variabile int che non occupa memoria". È un'operazione assurda.

Modifica

Gli array Java sono animali completamente diversi dagli array C e C ++; non sono un tipo primitivo, ma un tipo di riferimento derivato da Object .

Modifica 2

Un punto sollevato nei commenti seguenti: il vincolo "maggiore di 0" si applica solo agli array in cui la dimensione è specificata tramite un'espressione costante ; un VLA può avere una lunghezza 0 Dichiarare un VLA con un'espressione non costante con valore 0 non è una violazione del vincolo, ma invoca un comportamento non definito.

È chiaro che gli VLA sono animali diversi dagli array regolari e la loro implementazione può consentire una dimensione 0 . Non possono essere dichiarati static o nell'ambito del file, poiché la dimensione di tali oggetti deve essere nota prima dell'avvio del programma.

Inoltre non vale nulla che a partire da C11 non siano necessarie implementazioni per supportare gli VLA.

    
risposta data 11.07.2014 - 17:08
fonte
2

Normalmente vorresti che la tua matrice di dimensioni zero (in effetti variabili) conoscesse le sue dimensioni in fase di esecuzione. Quindi impacchettalo in struct e utilizza membri flessibili dell'array , ad esempio:

struct my_st {
   unsigned len;
   double flexarray[]; // of size len
};

Ovviamente il membro della matrice flessibile deve essere l'ultimo nella sua struct e devi avere qualcosa prima. Spesso ciò sarebbe correlato alla lunghezza effettiva occupata dal tempo di esecuzione di quel membro di array flessibile.

Naturalmente assegneresti:

 unsigned len = some_length_computation();
 struct my_st*p = malloc(sizeof(struct my_st)+len*sizeof(double));
 if (!p) { perror("malloc my_st"); exit(EXIT_FAILURE); };
 p->len = len;
 for (unsigned ix=0; ix<len; ix++)
    p->flexarray[ix] = log(3.0+(double)ix);

AFAIK, questo era già possibile in C99 ed è molto utile.

BTW, i membri flessibili dell'array non esistono in C ++ (perché sarebbe difficile definire quando e come dovrebbero essere costruiti e distrutti). Vedi comunque il futuro std :: dynarray

    
risposta data 11.07.2014 - 20:30
fonte
2

Se l'espressione type name[count] è scritta in qualche funzione, allora dici al compilatore C di allocare sul frame dello stack sizeof(type)*count byte e calcola l'indirizzo del primo elemento dell'array.

Se l'espressione type name[count] è scritta al di fuori di tutte le definizioni di funzioni e struct, allora dici al compilatore C di allocare sul segmento dati sizeof(type)*count byte e calcola l'indirizzo del primo elemento dell'array.

name è in realtà un oggetto costante che memorizza l'indirizzo del primo elemento dell'array e ogni oggetto che memorizza un indirizzo di una certa memoria è chiamato puntatore, quindi questo è il motivo per cui si tratta name come puntatore anziché un array. Si noti che è possibile accedere agli array in C solo tramite puntatori.

Se count è un'espressione costante che valuta zero, allora comunichi al compilatore C di allocare zero byte sul frame dello stack o sul segmento dati e restituire l'indirizzo del primo elemento dell'array, ma il problema nel fare questo è che il primo elemento dell'array di lunghezza zero non esiste e non puoi calcolare l'indirizzo di qualcosa che non esiste.

Questo è razionale quell'elemento n. count+1 non esiste in count -length array, quindi questo è il motivo per cui il compilatore C vieta di definire l'array a lunghezza zero come variabile all'interno e all'esterno di una funzione, perché qual è il contenuto di name quindi ? Quale indirizzo name memorizza esattamente?

Se p è un puntatore, l'espressione p[n] equivale a *(p + n)

Dove l'asterisco * nell'espressione destra è operazione di dereferenziazione del puntatore, che significa accedere alla memoria puntata da p + n o accedere alla memoria il cui indirizzo è memorizzato in p + n , dove p + n è espressione del puntatore, esso prende l'indirizzo di p e aggiunge a questo indirizzo il numero n moltiplica la dimensione del tipo del puntatore p .

È possibile aggiungere un indirizzo e un numero?

Sì, è possibile, perché l'indirizzo è un numero intero senza segno comunemente rappresentato in notazione esadecimale.

    
risposta data 06.06.2018 - 07:08
fonte
1

Se vuoi un puntatore a un indirizzo di memoria, dichiarane uno. Un array punta in realtà su un blocco di memoria che hai prenotato. Le matrici decadono ai puntatori quando passano alle funzioni, ma se la memoria a cui puntano è nell'heap, nessun problema. Non c'è motivo di dichiarare una matrice di dimensione zero.

    
risposta data 11.07.2014 - 03:26
fonte
1

Dai giorni del C89 originale, quando uno standard C specificava che qualcosa aveva un comportamento indefinito, ciò che significava era "fare qualunque cosa avrebbe reso un'implementazione su una particolare piattaforma di destinazione più adatta allo scopo previsto". Gli autori dello Standard non volevano provare a indovinare quali comportamenti potessero essere più adatti per uno scopo particolare. Le implementazioni C89 esistenti con estensioni VLA potevano avere comportamenti diversi, ma logici, quando avevano una dimensione pari a zero (ad esempio, alcuni potevano aver trattato l'array come un'espressione di indirizzo che produceva NULL, mentre altri lo trattavano come un'espressione di indirizzo che poteva eguagliare l'indirizzo di un'altra variabile arbitraria, ma potrebbe tranquillamente averne aggiunto zero senza intrappolare). Se qualche codice si fosse basato su un comportamento così diverso, gli autori dello Standard non volevano impedire ai compilatori di continuare a supportare tale codice.

Piuttosto che cercare di indovinare quali potrebbero essere le implementazioni, o suggerire che qualsiasi comportamento dovrebbe essere considerato superiore a qualsiasi altro, gli autori dello Standard hanno semplicemente permesso agli implementatori di usare il giudizio nel gestire quel caso come meglio hanno ritenuto opportuno. Le implementazioni che usano malloc () dietro le quinte potrebbero trattare l'indirizzo dell'array come NULL (se il valore di malloc della dimensione zero restituisce null), quelli che usano calcoli dell'indirizzo dello stack potrebbero produrre un puntatore che corrisponde ad un altro indirizzo della variabile e alcune altre implementazioni potrebbero fare altre cose. Non penso che si aspettassero che gli scrittori di compilatori avrebbero fatto di tutto per far si che il caso dell'angolo a dimensione zero si comporti in modo deliberatamente inutile.

    
risposta data 07.03.2017 - 19:26
fonte

Leggi altre domande sui tag