Qual è il "tipo" di dati che i puntatori conservano nel linguaggio C?


30

So che i puntatori contengono indirizzi. So che i tipi di puntatori sono "generalmente" noti in base al "tipo" di dati a cui puntano. Ma i puntatori sono ancora variabili e gli indirizzi che detengono devono avere un "tipo" di dati. Secondo le mie informazioni, gli indirizzi sono in formato esadecimale. Ma non so ancora quale "tipo" di dati sia questo esadecimale. (Nota che so cos'è un esadecimale, ma quando dici 10CBA20, per esempio, questa stringa di caratteri è numeri interi? Cosa? Quando voglio accedere all'indirizzo e manipolarlo ... devo conoscerne il tipo. Questo è per questo che sto chiedendo.)


17
I puntatori non sono variabili , ma valori . Le variabili contengono valori (e se il loro tipo è un tipo di puntatore, quel valore è un puntatore e potrebbe essere l'indirizzo di una zona di memoria contenente qualcosa di significativo). Una determinata zona di memoria può essere utilizzata per contenere vari valori di diversi tipi.
Basile Starynkevitch,

29
"gli indirizzi sono in formato esadecimale" No, questo è solo il debugger o i bit di formattazione di una libreria. Con lo stesso argomento potresti dire che sono in binario o ottale.
usr

Faresti meglio a chiedere informazioni sul formato , non sul tipo . Quindi alcune risposte fuoripista di seguito ... (anche se Kilian è perfetto).
Corse di leggerezza con Monica il

1
Penso che il problema più profondo qui sia la comprensione del tipo di OP . Quando si tratta di esso, i valori che stai manipolando nel tuo programma sono solo bit di memoria. I tipi sono il modo in cui il programmatore dice al compilatore come trattare quei bit quando genera il codice assembly.
Justin Lardinois,

Suppongo che sia troppo tardi per modificarlo con tutte quelle risposte, ma questa domanda sarebbe stata migliore se avessi limitato l'hardware e / o il sistema operativo, ad esempio "su Linux x64".
hyde,

Risposte:


64

Il tipo di una variabile puntatore è ... pointer.

Le operazioni che puoi formalmente fare in C sono di confrontarlo (con altri puntatori, o il valore NULL / zero speciale), per aggiungere o sottrarre numeri interi o per lanciarlo su altri puntatori.

Una volta accettato il comportamento indefinito , puoi vedere qual è il valore in realtà. Di solito sarà una parola macchina, lo stesso tipo di cose di un numero intero, e di solito può essere lanciato senza perdita da e verso un tipo intero. (Molto del codice di Windows lo fa nascondendo i puntatori nei typedef DWORD o HANDLE).

Ci sono alcune architetture in cui i puntatori non sono semplici perché la memoria non è piatta. DOS / 8086 'vicino' e 'lontano'; Diversi spazi di memoria e codice di PIC.


2
Puoi anche prendere la differenza tra due puntatori p1-p2. Il risultato è un valore integrale con segno. In particolare,&(array[i])-&(array[j]) == i-j
MSalters,

13
In realtà, viene anche specificata la conversione in un tipo integrale, specificatamente in intptr_te uintptr_tche sono garantiti "abbastanza grandi" per i valori del puntatore.
Matthieu M.

3
Puoi fare in modo che la conversione funzioni, ma il mapping tra numeri interi e puntatori è definito dall'implementazione. (L'unica eccezione è 0 -> null, e anche questo è specificato solo se lo 0 è un IIRC costante.)
cHao

7
L'aggiunta pdell'identificatore a printf rende una rappresentazione leggibile dall'uomo di un puntatore vuoto un comportamento definito, se l'implementazione dipende in c.
dmckee,

6
Questa risposta ha l'idea generalmente giusta, ma fallisce sulle affermazioni specifiche. Il collegamento di un puntatore al tipo integrale non è un comportamento indefinito e i tipi di dati HANDLE di Windows non sono valori del puntatore (non sono puntatori nascosti nei tipi di dati integrali, sono numeri interi nascosti nei tipi di puntatore, per evitare l'aritmetica).
Ben Voigt,

44

Stai complicando le cose.

Gli indirizzi sono solo numeri interi, punto. Idealmente sono il numero della cella di memoria di riferimento (in pratica questo diventa più complicato a causa di segmenti, memoria virtuale ecc.).

La sintassi esadecimale è una finzione completa che esiste solo per comodità dei programmatori. 0x1A e 26 hanno esattamente lo stesso numero esattamente dello stesso tipo , e nemmeno lo è il computer utilizzato: internamente, il computer utilizza sempre 00011010 (una serie di segnali binari).

Il fatto che un compilatore ti consenta o meno di trattare i puntatori come numeri dipende dalla definizione del linguaggio - i linguaggi di "programmazione dei sistemi" sono tradizionalmente più trasparenti su come funzionano le cose sotto il cofano, mentre i linguaggi "di alto livello" più spesso cercano di nascondere il metallo nudo dal programmatore - ma questo non cambia nulla del fatto che i puntatori sono numeri e di solito il tipo di numero più comune (quello con tanti bit quanti sono l'architettura del processore).


26
Gli indirizzi sono sicuramente non solo numeri interi. Proprio come numeri in virgola mobile sono sicuramente non solo numeri interi.
gnasher729,

8
Infatti. Il controesempio più noto è Intel 8086, in cui i puntatori sono due numeri interi.
MSalters,

5
@Rob In un modello di memoria segmentato, un puntatore può essere un singolo valore (un indirizzo relativo all'inizio del segmento; un offset) con il segmento implicito oppure un segmento / selettore e coppia di offset . (Penso che Intel abbia usato il termine "selettore"; sono troppo pigro per cercarlo.) Sull'8086, questi erano rappresentati come due numeri interi a 16 bit, che si univano per formare un indirizzo fisico a 20 bit. (Sì, potresti indirizzare la stessa cella di memoria in molti, molti modi diversi, se tu fossi così incline: indirizzo = (segmento << 4 + offset) e 0xfffff.) Questo ha portato avanti attraverso tutte le compatibilità x86 quando si esegue in modalità reale.
un CVn

4
Come programmatore di assemblatori a lungo termine, posso attestare che la memoria del computer non è altro che posizioni di memoria che contengono numeri interi. Tuttavia, è importante trattarli e tenere traccia di ciò che rappresentano questi numeri interi. Ad esempio, sul mio sistema, il numero decimale 4075876853 è memorizzato come x'F2F0F1F5 ', che è la stringa' 2015 'in EBCDIC. Il decimale 2015 verrebbe archiviato come 000007DF mentre x'0002015C 'rappresenta il decimale 2015 in formato decimale compresso. Come programmatore di assemblatori, devi tenerne traccia; il compilatore lo fa per i linguaggi HL.
Steve Ives,

7
Gli indirizzi possono essere messi in corrispondenza uno-a-uno con numeri interi, ma anche tutto il resto su un computer :)
hobbs

16

Un puntatore è proprio questo: un puntatore. Non è qualcos'altro. Non cercare di pensare che sia qualcos'altro.

In linguaggi come C, C ++ e Objective-C, i puntatori ai dati hanno quattro tipi di valori possibili:

  1. Un puntatore può essere l'indirizzo di un oggetto.
  2. Un puntatore può puntare appena oltre l'ultimo elemento di un array.
  3. Un puntatore può essere un puntatore nullo, il che significa che non punta a nulla.
  4. Un puntatore può avere un valore indeterminato, in altre parole è spazzatura e tutto può succedere (comprese le cose cattive) se si tenta di usarlo.

Esistono anche puntatori a funzione, che identificano una funzione, oppure sono puntatori a funzione nulla o hanno un valore indeterminato.

Altri puntatori sono "puntatore al membro" in C ++. Questi sicuramente non sono indirizzi di memoria! Invece, identificano un membro di qualsiasi istanza di una classe. In Objective-C, hai dei selettori, che sono qualcosa come "puntatore a un metodo di istanza con un determinato nome di metodo e nomi di argomenti". Come un puntatore membro, identifica tutti i metodi di tutte le classi purché abbiano lo stesso aspetto.

Puoi esaminare come un compilatore specifico implementa i puntatori, ma questa è una domanda completamente diversa.


4
Esistono puntatori alle funzioni e, in C ++, puntatori ai membri.
sdenham,

I puntatori C ++ ai membri non sono indirizzi di memoria? Certo che lo sono. class A { public: int num; int x; }; int A::*pmi = &A::num; A a; int n = a.*pmi;La variabile pminon sarebbe molto utile se non contenesse un indirizzo di memoria, vale a dire, come stabilisce l'ultima riga del codice, l'indirizzo del membro numdell'istanza adella classe A. Potresti lanciarlo su un normale intpuntatore (anche se il compilatore probabilmente ti darebbe un avvertimento) e dereferenziarlo con successo (dimostrando che è zucchero sintattico per qualsiasi altro puntatore).
dodgethesteamroller,

9

Un puntatore è un modello di bit che indirizza (identificando in modo univoco ai fini della lettura o della scrittura) una parola di memoria nella RAM. Per ragioni storiche e convenzionali, l'unità di aggiornamento è di otto bit, nota in inglese come "byte" o in francese, piuttosto più logicamente, come ottetto. Questo è onnipresente ma non inerente; sono esistite altre dimensioni.

Se ricordo bene c'era un computer che utilizzava una parola a 29 bit; non solo questo non è un potere di due, è anche primo. Pensavo fosse SILLIAC ma l'articolo di Wikipedia pertinente non lo supporta. CAN BUS utilizza indirizzi a 29 bit ma, per convenzione, gli indirizzi di rete non vengono indicati come puntatori anche quando sono funzionalmente identici.

Le persone continuano a sostenere che i puntatori sono numeri interi. Questo non è né intrinseco né essenziale, ma se interpretiamo i modelli di bit come numeri interi emerge l'utile qualità dell'ordinalità, che consente un'implementazione molto diretta (e quindi efficiente su hardware di piccole dimensioni) di costrutti come "stringa" e "matrice". La nozione di memoria contigua dipende dall'adiacenza ordinale ed è possibile il posizionamento relativo; il confronto dei numeri interi e le operazioni aritmetiche possono essere applicati in modo significativo. Per questo motivo esiste quasi sempre una forte correlazione tra la dimensione della parola per l'indirizzamento della memoria e l'ALU (la cosa che fa matematica intera).

A volte i due non corrispondono. Nei primi PC il bus degli indirizzi era largo 24 bit.


Nitpick, oggigiorno in sistemi operativi comuni, il puntatore identifica la posizione nella memoria virtuale e non ha nulla a che fare direttamente con una parola RAM fisica (la posizione della memoria virtuale potrebbe anche non esistere fisicamente se si trova in una pagina di memoria nota per essere tutti a zero dal sistema operativo ).
hyde,

@hyde - La tua argomentazione ha valore nel contesto in cui ovviamente l'hai intesa, ma la forma dominante di computer è il controller incorporato, dove meraviglie come la memoria virtuale sono innovazioni recenti con distribuzione limitata. Inoltre, ciò che è stato sottolineato in alcun modo aiuta l'OP a comprendere i puntatori. Ho pensato che un certo contesto storico avrebbe reso tutto molto meno arbitrario.
Peter Wone,

Non so se parlare di RAM aiuterà OP a capire, in quanto la domanda riguarda in particolare quali sono realmente i puntatori . Oh, un altro nitpick, nel puntatore c, per definizione, punta a byte (può essere tranquillamente trasmesso ad char*es. Per scopi di copia / confronto della memoria e sizeof char==1come definito dallo standard C), non parola (a meno che la dimensione della parola CPU non sia uguale alla dimensione byte).
hyde,

In sostanza, i puntatori sono le chiavi hash per l'archiviazione. Questo è invariante di lingua e piattaforma.
Peter Wone,

La domanda riguarda i puntatori c . E i puntatori sicuramente non sono chiavi hash, perché non esiste una tabella hash, nessun algoritmo di hashing. Sono naturalmente una sorta di chiavi mappa / dizionario (per una definizione sufficientemente ampia di "mappa"), ma non chiavi hash .
hyde,

6

Fondamentalmente ogni computer moderno è una macchina che spinge i bit. Solitamente sposta i bit in gruppi di dati, chiamati byte, parole, parole o parole chiave.

Un byte è composto da 8 bit, una parola 2 byte (o 16 bit), una parola 2 parole (o 32 bit) e una parola q 2 parole (o 64 bit). Questi non sono l'unico modo per disporre i bit. Si verifica anche la manipolazione a 128 e 256 bit, spesso nelle istruzioni SIMD.

Le istruzioni di assemblaggio operano su registri e gli indirizzi di memoria normalmente operano in una delle forme sopra indicate.

Gli ALU (unità logiche aritmetiche) operano su tali fasci di bit come se rappresentassero numeri interi (di solito il formato del complemento a due) e FPU come se fossero valori in virgola mobile (di solito in stile IEEE 754 floate double). Altre parti si comporteranno come se fossero dati raggruppati di un certo formato, caratteri, voci della tabella, istruzioni della CPU o indirizzi.

Su un tipico computer a 64 bit, i bundle di 8 byte (64 bit) sono indirizzi. Visualizziamo questi indirizzi in modo convenzionale come in un formato esadecimale (come 0xabcd1234cdef5678), ma questo è solo un modo semplice per gli umani di leggere i modelli di bit. Ogni byte (8 bit) è scritto come due caratteri esadecimali (equivalentemente ogni carattere esadecimale - da 0 a F - rappresenta 4 bit).

Ciò che sta realmente accadendo (per un certo livello di realtà) è che ci sono bit, di solito memorizzati in un registro o memorizzati in posizioni adiacenti in un banco di memoria, e stiamo solo cercando di descriverli a un altro essere umano.

Seguire un puntatore consiste nel chiedere al controller di memoria di fornirci alcuni dati in quella posizione. Generalmente chiedete al controller di memoria un certo numero di byte in una determinata posizione (beh, implicitamente una serie di posizioni, di solito contigue), e viene fornito attraverso vari meccanismi in cui non entrerò.

Il codice di solito specifica una destinazione per il recupero dei dati - un registro, un altro indirizzo di memoria, ecc. - Di solito è una cattiva idea caricare i dati in virgola mobile in un registro in attesa di un numero intero o viceversa.

Il tipo di dati in C / C ++ è qualcosa di cui il compilatore tiene traccia e cambia il codice generato. Di solito non c'è nulla di intrinseco nei dati che lo rende effettivamente di alcun tipo. Solo una raccolta di bit (disposti in byte) che vengono manipolati in modo intero (o in modo float o in modo indiretto) dal codice.

Ci sono delle eccezioni. Ci sono architetture in cui certe cose sono un diverso tipo di bit. L'esempio più comune sono le pagine di esecuzione protette - mentre le istruzioni che indicano alla CPU cosa fare sono bit, in fase di esecuzione le pagine (di memoria) contenenti codice da eseguire sono contrassegnate in modo speciale, non possono essere modificate e non è possibile eseguire pagine che non sono contrassegnate come pagine di esecuzione.

Esistono anche dati di sola lettura (a volte memorizzati nella ROM su cui non è possibile scrivere fisicamente!), Problemi di allineamento (alcuni processori non possono caricare messaggi doubledi memoria a meno che non siano allineati in modi particolari o istruzioni SIMD che richiedono un certo allineamento) e miriadi di altre stranezze di architettura.

Anche il livello di dettaglio sopra riportato è una bugia. I computer non "spingono" veramente i bit, ma spingono davvero tensioni e correnti. Queste tensioni e correnti a volte non fanno ciò che dovrebbero "fare" al livello di astrazione dei bit. I chip sono progettati per rilevare la maggior parte di tali errori e correggerli senza che l'astrazione di livello superiore debba esserne consapevole.

Anche questa è una bugia.

Ogni livello di astrazione nasconde quello sottostante e ti consente di pensare a risolvere i problemi senza dover tenere a mente i diagrammi di Feynman per poterli stampare "Hello World".

Quindi, a un livello sufficiente di onestà, i computer spingono i bit e questi bit ricevono significato dal modo in cui vengono utilizzati.


3

Le persone hanno fatto molto se i puntatori sono numeri interi o no. In realtà ci sono risposte a queste domande. Tuttavia, dovrai fare un passo nel paese delle specifiche, che non è per i deboli di cuore. Daremo uno sguardo alla specifica C, ISO / IEC 9899: TC2

6.3.2.3 Puntatori

  1. Un numero intero può essere convertito in qualsiasi tipo di puntatore. Ad eccezione di quanto precedentemente specificato, il risultato è definito dall'implementazione, potrebbe non essere allineato correttamente, potrebbe non puntare a un'entità del tipo di riferimento e potrebbe essere una rappresentazione trap.

  2. Qualsiasi tipo di puntatore può essere convertito in un tipo intero. Ad eccezione di quanto precedentemente specificato, il risultato è definito dall'implementazione. Se il risultato non può essere rappresentato nel tipo intero, il comportamento non è definito. Non è necessario che il risultato sia compreso nell'intervallo di valori di alcun tipo intero.

Ora, per questo, dovrai conoscere alcuni termini comuni specifici. "implementazione definita" significa che ogni singolo compilatore può definirlo in modo diverso. In effetti, un compilatore può persino definirlo in modi diversi a seconda delle impostazioni del compilatore. Comportamento indefinito significa che al compilatore è permesso fare assolutamente qualsiasi cosa, dall'errore di compilazione a comportamenti inspiegabili, al funzionamento perfettamente.

Da questo possiamo vedere che il modulo di archiviazione sottostante non è specificato, a parte il fatto che potrebbe esserci una conversione in un tipo intero. A dire il vero, praticamente ogni compilatore sotto il sole rappresenta i puntatori sotto il cofano come indirizzi interi (con una manciata di casi speciali in cui potrebbe essere rappresentato come 2 numeri interi anziché solo 1), ma la specifica consente assolutamente qualsiasi cosa, come indirizzi come una stringa di 10 caratteri!

Se avanziamo rapidamente da C e osserviamo le specifiche C ++, otteniamo un po 'più di chiarezza reinterpret_cast, ma questo è un linguaggio diverso, quindi il suo valore può variare:

ISO / IEC N337: specifica del progetto C ++ 11 (ho solo il progetto a portata di mano)

5.2.10 Reinterpretare il cast

  1. Un puntatore può essere esplicitamente convertito in qualsiasi tipo integrale abbastanza grande da mantenerlo. La funzione di mappatura è definita dall'implementazione. [Nota: si intende che non sorprenda chi conosce la struttura di indirizzamento della macchina sottostante. —End note] Un valore di tipo std :: nullptr_t può essere convertito in un tipo integrale; la conversione ha lo stesso significato e validità di una conversione di (void *) 0 nel tipo integrale. [Nota: un reinterpret_cast non può essere utilizzato per convertire un valore di qualsiasi tipo nel tipo std :: nullptr_t. —Endola nota]

  2. Un valore di tipo integrale o di enumerazione può essere convertito esplicitamente in un puntatore. Un puntatore convertito in un numero intero di dimensioni sufficienti (se presente nell'implementazione) e di nuovo nello stesso tipo di puntatore avrà il suo valore originale; i mapping tra puntatori e numeri interi sono altrimenti definiti dall'implementazione. [Nota: ad eccezione di quanto descritto in 3.7.4.3, il risultato di tale conversione non sarà un valore del puntatore derivato in modo sicuro. —Endola nota]

Come puoi vedere qui, con qualche anno in più, C ++ ha scoperto che era sicuro supporre che esistesse una mappatura su numeri interi, quindi non si parla più di comportamento indefinito (sebbene ci sia un'interessante contraddizione tra le parti 4 e 5 con la frase "se ne esiste una nell'implementazione")


Cosa dovresti togliere da questo?

  • L'esatta rappresentazione dei puntatori è definita dall'implementazione. (in effetti, solo per renderlo più disordinato, alcuni piccoli computer incorporati rappresentano il puntatore null, (vuoto ) 0, come indirizzo 255 per supportare alcuni trucchi di aliasing dell'indirizzo che usano) *
  • Se devi chiedere della rappresentazione dei puntatori in memoria, probabilmente non sei nel punto della tua carriera di programmatore in cui vuoi armeggiare con loro.

La scommessa migliore: lanciare su un (carattere *). Le specifiche C e C ++ sono piene di regole che specificano l'imballaggio di matrici e strutture, ed entrambe consentono sempre il cast di qualsiasi puntatore a un carattere *. char è sempre 1 byte (non garantito in C, ma in C ++ 11 è diventato una parte obbligatoria del linguaggio, quindi è relativamente sicuro supporre che sia 1 byte ovunque). Ciò consente di eseguire alcune operazioni aritmetiche dei puntatori a livello di byte per byte senza ricorrere alla necessità di conoscere effettivamente le rappresentazioni specifiche dei puntatori di implementazione.


Puoi necessariamente lanciare un puntatore a una funzione char *? Sto pensando a una macchina ipotetica con spazi di indirizzi separati per codice e dati.
Philip Kendall,

@PhilipKendall Un buon punto. Non ho incluso quella parte della specifica, ma i puntatori a funzione sono trattati come una cosa completamente diversa rispetto ai puntatori di dati nella specifica a causa esattamente del problema che sollevi. Anche i suggerimenti dei membri vengono trattati in modo diverso (ma agiscono anche in modo molto diverso)
Cort Ammon - Reinstate Monica il

A charè sempre 1 byte in C. Citando dallo standard C: "L'operatore sizeof restituisce la dimensione (in byte) del suo operando" e "Quando sizeof viene applicato a un operando che ha tipo char, unsigned char o sign char, (o una sua versione qualificata) il risultato è 1. " Forse stai pensando che un byte è lungo 8 bit. Questo non è necessariamente il caso. Per essere conforme allo standard, un byte deve contenere almeno 8 bit.
David Hammen,

La specifica descrive la conversione tra puntatore e tipi interi. Va sempre tenuto presente che una "conversione" tra tipi non implica un'uguaglianza dei tipi, né che una rappresentazione binaria dei due tipi in memoria avrebbe lo stesso schema di bit. (ASCII può essere "convertito" in EBCDIC. Il big-endian può essere "convertito" in little-endian. Ecc.)
user2338816

1

Sulla maggior parte delle architetture, il tipo di puntatore cessa di esistere una volta che sono stati tradotti in codice macchina (tranne forse per "puntatori fatali"). Pertanto, un puntatore a un intsarebbe indistinguibile da un puntatore a a double, almeno da solo. *

[*] Tuttavia, puoi ancora fare ipotesi in base al tipo di operazioni che ti vengono applicate.


1

Una cosa importante da capire su C e C ++ è quali tipi sono effettivamente. Tutto ciò che fanno realmente è indicare al compilatore come interpretare un insieme di bit / byte. Iniziamo con il seguente codice:

int var = -1337;

A seconda dell'architettura, di solito a un intero viene assegnato 32 bit di spazio per memorizzare quel valore. Ciò significa che lo spazio nella memoria in cui è archiviato var apparirà come "11111111 11111111 11111010 11000111" o in esadecimale "0xFFFFFAC7". Questo è tutto. Questo è tutto ciò che viene memorizzato in quella posizione. Tutti i tipi fanno è dire al compilatore come interpretare tali informazioni. I puntatori non sono diversi. Se faccio qualcosa del genere:

int* var_ptr = &var;   //the ampersand is telling C "get the address where var's value is located"

Quindi il compilatore otterrà la posizione di var e quindi memorizzerà tale indirizzo nello stesso modo in cui il primo frammento di codice salva il valore -1337. Non c'è differenza nel modo in cui sono memorizzati, solo nel modo in cui vengono utilizzati. Non importa nemmeno che ho fatto var_ptr un puntatore a un int. Se lo volessi, potresti farlo.

unsigned int var2 = *(unsigned int*)var_ptr;

Questo copierà il valore esadecimale sopra di var (0xFFFFFAC7) nella posizione in cui è memorizzato il valore di var2. Se dovessimo quindi utilizzare var2, scopriremmo che il valore sarebbe 4294965959. I byte in var2 sono gli stessi di var, ma il valore numerico differisce. Il compilatore li ha interpretati diversamente perché gli abbiamo detto che quei bit rappresentano un lungo senza segno. Puoi fare lo stesso anche per il valore del puntatore.

unsigned int var3 = (unsigned int)var_ptr;

In questo esempio finiresti per interpretare il valore che rappresenta l'indirizzo di var come int senza segno.

Spero che questo chiarisca le cose per te e ti dia una visione migliore di come funziona C. Si noti che NON DOVREBBE fare nessuna delle cose folli che ho fatto nelle due righe seguenti nel codice di produzione effettivo. Quello era solo per dimostrazione.


1

Numero intero.

Lo spazio degli indirizzi in un computer è numerato in sequenza, a partire da 0, e aumenta di 1. Quindi un puntatore conterrà un numero intero che corrisponde a un indirizzo nello spazio degli indirizzi.


1

I tipi si combinano.

In particolare, alcuni tipi si combinano, quasi come se fossero parametrizzati con segnaposto. I tipi di array e puntatori sono così; hanno uno di questi segnaposto, che è il tipo dell'elemento dell'array o della cosa puntata, rispettivamente. Anche i tipi di funzione sono così; possono avere più segnaposto per i parametri e un segnaposto per il tipo restituito.

Una variabile dichiarata per contenere un puntatore a char ha il tipo "pointer to char". Una variabile dichiarata per contenere un puntatore a puntatore a int ha il tipo "puntatore a puntatore a int".

Un (valore di) tipo "puntatore a puntatore a int" può essere modificato in "puntatore a int" mediante un'operazione di dereference. Quindi, la nozione di tipo non è solo parole ma un costrutto matematicamente significativo, che determina cosa possiamo fare con i valori del tipo (come la dereferenza, o passa come parametro o assegna a variabile; determina anche la dimensione (conteggio dei byte) di indicizzazione, aritmetica e operazioni di incremento / decremento).

PS Se vuoi approfondire i tipi, prova questo blog: http://www.goodmath.org/blog/2015/05/13/expressions-and-arity-part-1/

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.