Cosa trovano difficile la gente riguardo ai puntatori C? [chiuso]


173

Dal numero di domande pubblicate qui, è chiaro che le persone hanno dei problemi piuttosto fondamentali quando si concentrano sui puntatori e sull'aritmetica dei puntatori.

Sono curioso di sapere perché. Non mi hanno mai veramente causato grossi problemi (anche se ne ho saputo per la prima volta nel Neolitico). Per scrivere risposte migliori a queste domande, vorrei sapere cosa trovano difficile la gente.

Quindi, se stai lottando con i puntatori, o di recente sei stato ma improvvisamente "capito", quali erano gli aspetti dei puntatori che ti hanno causato problemi?


54
Snarky, sopravvalutato, inutilmente controverso, risponde con un pizzico di verità: sono stati paralizzati --- menomati mentalmente, dico 'ya --- dando per primo uno di questi linguaggi "espressivi" di alto livello. Avrebbero dovuto iniziare programmando su bare metal come intendeva God a Daniel Boone!
dmckee --- ex gattino moderatore,

3
... e programmatore sarebbe meglio, perché si svilupperà in una discussione, nonostante voi migliori sforzi.
dmckee --- ex gattino moderatore,

Questo è polemico e soggettivo, genera N risultati, il mio difficile è il tuo facile. Probabilmente funzionerà con i programmatori, ma non stiamo ancora migrando queste domande perché il sito non è fuori dalla beta.
Sam Saffron,

2
@Sam Saffron: anche se in genere sono d'accordo sul fatto che si tratti più di una domanda da programmatore. Tipo SE, onestamente non sarebbe male se le persone fossero disposte a contrassegnare "Penso che sia facile" e "Odio vedere i puntatori" come spam loro sono.
jkerian,

3
Qualcuno deve sollevarlo: "È come un dito che punta lontano verso la luna. Non concentrarti sul dito o ti perderai tutta quella gloria celeste" - Bruce Lee
mu è troppo corto il

Risposte:


86

Sospetto che le persone stiano andando troppo in profondità nelle loro risposte. Non è davvero necessaria una comprensione della pianificazione, delle effettive operazioni della CPU o della gestione della memoria a livello di assembly.

Durante l'insegnamento, ho scoperto che le seguenti lacune nella comprensione degli studenti erano la fonte più comune di problemi:

  1. Heap vs Stack storage. È semplicemente sorprendente quante persone non lo capiscono, anche in senso generale.
  2. Stack frame. Solo il concetto generale di una sezione dedicata dello stack per le variabili locali, insieme al motivo per cui è uno "stack" ... dettagli come la memorizzazione della posizione di ritorno, i dettagli del gestore delle eccezioni e i registri precedenti possono essere tranquillamente lasciati finché qualcuno non tenta di costruire un compilatore.
  3. "La memoria è memoria è memoria" Il cast cambia solo le versioni degli operatori o la quantità di spazio che il compilatore offre per un particolare pezzo di memoria. Sai che a che fare con questo problema quando si parla di "ciò che (primitiva) variabile X realmente è".

La maggior parte dei miei studenti è stata in grado di comprendere un disegno semplificato di un pezzo di memoria, in genere la sezione delle variabili locali dello stack nell'ambito corrente. Aiutare in genere a fornire indirizzi fittizi espliciti ai vari luoghi.

Immagino in sintesi, sto dicendo che se vuoi capire i puntatori, devi capire le variabili e cosa sono in realtà nelle architetture moderne.


13
IMHO, comprendere stack e heap non sono necessari quanto i dettagli della CPU di basso livello. Stack e heap sono i dettagli di implementazione. Le specifiche ISO C non hanno una sola menzione della parola "stack" e nemmeno K&R.
sigjuice,

4
@sigjuice: le tue obiezioni mancano il punto sia della domanda che della risposta. A) K&R C è un anacronismo B) ISO C non è l'unico linguaggio con puntatori, i miei punti 1 e 2 sono stati sviluppati contro un linguaggio non basato su C C) Il 95% delle architetture (non le lingue) là fuori usano l'heap / stack system, è abbastanza comune che vengano spiegate le eccezioni relative ad esso. D) Il punto della domanda era "perché le persone non capiscono i puntatori", non "come posso spiegare ISO C"
jkerian

9
@John Marchetti: Ancora di più ... dato che la domanda era "Qual è il problema alla radice del problema delle persone con i puntatori", non credo che le persone che pongono le domande relative ai puntatori sarebbero terribilmente colpite da "Non ho davvero bisogno di sapere "come risposta. Ovviamente non sono d'accordo. :)
jkerian,

3
@jkerian Potrebbe essere obsoleto, ma le tre o quattro pagine di K&R che spiegano i puntatori lo fanno senza la necessità di dettagli di implementazione. Una conoscenza dei dettagli di implementazione è utile per vari motivi, ma IMHO, non dovrebbe essere un prerequisito per comprendere i costrutti chiave di una lingua.
sigjuice,

3
"Aiutare in genere a fornire indirizzi fittizi espliciti ai vari luoghi". -> +1
fredoverflow

146

Quando ho iniziato a lavorare con loro, il problema più grande che avevo era la sintassi.

int* ip;
int * ip;
int *ip;

sono tutti uguali.

ma:

int* ip1, ip2;  //second one isn't a pointer!
int *ip1, *ip2;

Perché? perché la parte "puntatore" della dichiarazione appartiene alla variabile e non al tipo.

E poi dereferenziare la cosa usa una notazione molto simile:

*ip = 4;  //sets the value of the thing pointed to by ip to '4'
x = ip;   //hey, that's not '4'!
x = *ip;  //ahh... there's that '4'

Tranne quando hai effettivamente bisogno di ottenere un puntatore ... allora usi una e commerciale!

int *ip = &x;

Evviva per coerenza!

Quindi, apparentemente solo per essere cretini e dimostrare quanto siano intelligenti, molti sviluppatori di librerie usano puntatori-puntatori-puntatori-puntatori, e se si aspettano un array di quelle cose, beh, perché non passare un puntatore anche a quello .

void foo(****ipppArr);

per chiamare questo, ho bisogno dell'indirizzo dell'array di puntatori a puntatori a puntatori di ints:

foo(&(***ipppArr));

Tra sei mesi, quando dovrò mantenere questo codice, passerò più tempo a cercare di capire cosa significhi tutto ciò che riscrivere da zero. (sì, probabilmente ho sbagliato quella sintassi - è passato un po 'di tempo da quando ho fatto qualcosa in C. Mi manca un po', ma poi sono un po 'un massochista)


21
Il tuo commento sul primo, >> * ip = 4; // imposta il valore di ip su '4' << è errato. Dovrebbe essere >> // imposta il valore della cosa indicata da ip su '4'
aaaa bbbb,

8
Impilare troppi tipi uno sopra l'altro è una cattiva idea, in qualsiasi lingua. Potresti trovare la scritta "foo (& (*** ipppArr));" strano in C, ma scrivendo qualcosa come "std :: map <std :: pair <int, int>, std :: pair <std :: vector <int>, std :: tuple <int, double, std :: list <int> >>> "in C ++ è anche molto complesso. Ciò non significa che i puntatori nei contenitori C o STL in C ++ siano complessi. Significa solo che devi usare migliori definizioni dei tipi per renderlo comprensibile per il lettore del tuo codice.
Patrick,

20
Sinceramente non posso credere che un malinteso sulla sintassi sia la risposta più votata. Questa è la parte più semplice dei puntatori.
Jason,

4
Anche leggendo questa risposta, sono stato tentato di prendere un foglio di carta e disegnare l'immagine. In C, disegnavo sempre immagini.
Michael Easter,

19
@Jason Qual è la misura oggettiva della difficoltà oltre a ciò che la maggior parte delle persone trova difficile?
Rupert Madden-Abbott,

52

La corretta comprensione dei puntatori richiede la conoscenza dell'architettura della macchina sottostante.

Molti programmatori oggi non sanno come funziona la loro macchina, proprio come la maggior parte delle persone che sanno guidare una macchina non sanno nulla del motore.


18
@dmckee: Beh, sbaglio? Quanti programmatori Java potrebbero gestire un segfault?
Robert Harvey,

5
I segfault hanno a che fare con lo spostamento del bastone? - Un programmatore Java
Tom Anderson,

6
@Robert: era inteso come un vero complemento. Questo è un argomento difficile da discutere senza ferire i sentimenti delle persone. E temo che il mio commento abbia accelerato il conflitto che pensavo fossi riuscito a evitare. Mea Cupla.
dmckee --- ex gattino moderatore,

30
Non sono d'accordo; non è necessario comprendere l'architettura sottostante per ottenere i puntatori (sono comunque un'astrazione).
Jason,

11
@Jason: in C, un puntatore è essenzialmente un indirizzo di memoria. Lavorare con loro in sicurezza è impossibile senza comprendere l'architettura della macchina. Vedi en.wikipedia.org/wiki/Pointer_(computing) e boredzo.org/pointers/#definition
Robert Harvey,

42

Quando si tratta di puntatori, le persone che si confondono sono ampiamente in uno dei due campi. Sono stato (sono?) In entrambi.

La array[]folla

Questa è la folla che direttamente non sa come tradurre dalla notazione con il puntatore alla notazione con array (o non sa nemmeno che sono persino correlati). Ecco quattro modi per accedere agli elementi di un array:

  1. notazione di matrice (indicizzazione) con il nome della matrice
  2. notazione di array (indicizzazione) con il nome del puntatore
  3. notazione del puntatore (il *) con il nome del puntatore
  4. notazione del puntatore (il *) con il nome dell'array

 

int vals[5] = {10, 20, 30, 40, 50};
int *ptr;
ptr = vals;

array       element            pointer
notation    number     vals    notation

vals[0]     0          10      *(ptr + 0)
ptr[0]                         *(vals + 0)

vals[1]     1          20      *(ptr + 1)
ptr[1]                         *(vals + 1)

vals[2]     2          30      *(ptr + 2)
ptr[2]                         *(vals + 2)

vals[3]     3          40      *(ptr + 3)
ptr[3]                         *(vals + 3)

vals[4]     4          50      *(ptr + 4)
ptr[4]                         *(vals + 4)

L'idea qui è che l'accesso alle matrici tramite puntatori sembra piuttosto semplice e diretto, ma un sacco di cose molto complicate e intelligenti possono essere fatte in questo modo. Alcuni dei quali possono lasciare confusi i programmatori C / C ++ esperti, e tanto meno i neofiti inesperti.

Il reference to a pointere la pointer to a pointerfolla

Questo è un ottimo articolo che spiega la differenza e che citerò e ruberò del codice :)

Come piccolo esempio, può essere molto difficile vedere esattamente cosa voleva fare l'autore se ti sei imbattuto in qualcosa del genere:

//function prototype
void func(int*& rpInt); // I mean, seriously, int*& ??

int main()
{
  int nvar=2;
  int* pvar=&nvar;
  func(pvar);
  ....
  return 0;
}

O, in misura minore, qualcosa del genere:

//function prototype
void func(int** ppInt);

int main()
{
  int nvar=2;
  int* pvar=&nvar;
  func(&pvar);
  ....
  return 0;
}

Quindi alla fine, cosa risolviamo veramente con tutto questo incomprensibile? Niente.

Ora abbiamo visto la sintassi di ptr-to-ptr e ref-to-ptr. Ci sono dei vantaggi l'uno rispetto all'altro? Ho paura, no. L'uso di uno di entrambi, per alcuni programmatori sono solo preferenze personali. Alcuni che usano ref-to-ptr dicono che la sintassi è "più pulita" mentre altri che usano ptr-to-ptr, dicono che la sintassi di ptr-to-ptr lo rende più chiaro a coloro che leggono quello che stai facendo.

Questa complessità e l' intercambiabilità apparente ( apparentemente audace) con i riferimenti, che è spesso un altro avvertimento dei puntatori e un errore dei nuovi arrivati, rende difficile la comprensione dei puntatori. È anche importante capire, per amor di completamento, che i puntatori a riferimenti sono illegali in C e C ++ per ragioni confuse che ti portano in lvalue- rvaluesemantica.

Come ha sottolineato una risposta precedente, molte volte avrai solo questi programmatori hotshot che pensano di essere intelligenti usando ******awesome_var->lol_im_so_clever()e la maggior parte di noi è probabilmente colpevole di scrivere tali atrocità a volte, ma non è semplicemente un buon codice e certamente non è mantenibile .

Bene, questa risposta si è rivelata più lunga di quanto sperassi ...


5
Penso che potresti aver dato una risposta C ++ a una domanda C qui ... almeno nella seconda parte.
detly

I puntatori ai puntatori si applicano anche a C: p
David Titarenco,

1
Eh? Vedo solo i puntatori ai puntatori quando si passa intorno agli array - il tuo secondo esempio non è realmente applicabile al codice C più decente. Inoltre, stai trascinando C nel pasticcio di C ++ - i riferimenti in C non esistono.
nuovo123456

Devi avere a che fare con i puntatori ai puntatori in molti casi legittimi. Ad esempio, quando si ha a che fare con un puntatore a funzione che punta a una funzione che restituisce un puntatore. Un altro esempio: una struttura che può contenere un numero variabile di altre strutture. E molti altri ...
David Titarenco

2
"I puntatori a riferimenti sono illegali in C" - più come "assente" :)
Kos

29

Incolpo la qualità dei materiali di riferimento e le persone che insegnano, personalmente; la maggior parte dei concetti in C (ma soprattutto i puntatori) sono semplicemente insegnati male. Continuo a minacciare di scrivere il mio libro C (intitolato L'ultima cosa che il mondo ha bisogno è un altro libro sul linguaggio di programmazione C ), ma non ho il tempo o la pazienza per farlo. Quindi esco qui e lancio citazioni casuali dallo Standard alle persone.

C'è anche il fatto che quando C è stato inizialmente progettato, si presumeva che tu comprendessi l'architettura della macchina a un livello abbastanza dettagliato solo perché non c'era modo di evitarlo nel tuo lavoro quotidiano (la memoria era così stretta e i processori erano così lenti hai dovuto capire come ciò che hai scritto ha influito sulle prestazioni).


3
Sì. 'int foo = 5; int * pfoo = & foo; Vedi quanto è utile? OK, spostandomi ... 'Non ho usato i puntatori fino a quando non ho scritto la mia libreria di elenchi con doppio link.
John Lopez,

2
+1. Uso come tutor gli studenti CS100 e molti dei loro problemi sono stati risolti semplicemente esaminando i puntatori in modo comprensibile.
benzado,

1
+1 per il contesto storico. Avendo iniziato molto tempo dopo, questo non mi è mai venuto in mente.
Lumi,

26

C'è un ottimo articolo a sostegno dell'idea che i puntatori sono difficili sul sito di Joel Spolsky - The Perils of JavaSchools .

[Disclaimer - Non sono un odiatore di Java in .]


2
@Jason - questo è vero ma non nega l'argomento.
Steve Townsend,

4
Spolsky non sta dicendo che JavaSchools sia la ragione per cui le persone trovano difficili i puntatori. Sta dicendo che si traducono in persone analfabete puntatore con lauree in Informatica.
benzado,

1
@benzado - punto giusto - il mio breve post sarebbe migliorato se leggesse "un ottimo articolo a sostegno dell'idea che i puntatori sono difficili". Ciò che l'articolo implica è che "avere una laurea in CS da una" buona scuola "" non è un buon predittore di successo come uno sviluppatore di una volta, mentre "capisce i suggerimenti" (e la ricorsione) lo è ancora.
Steve Townsend,

1
@Steve Townsend: penso che ti stia perdendo il punto della discussione del signor Spolsky.
Jason,

2
@Steve Townsend: Mr. Spolsky sostiene che le scuole Java stanno generando una generazione di programmatori che non conoscono i puntatori e la ricorsione, non che i puntatori sono difficili a causa della prevalenza delle scuole Java. Come hai affermato "c'è un ottimo articolo sul perché questo è difficile" e collegato a detto articolo, sembra che tu abbia quest'ultima interpretazione. Perdonami se sbaglio.
Jason,

24

Molte cose sono più difficili da capire se non si è radicati nella conoscenza che è "sotto". Quando ho insegnato a CS è diventato molto più facile quando ho iniziato i miei studenti a programmare una "macchina" molto semplice, un computer decimale simulato con codici opzionali decimali la cui memoria consisteva in registri decimali e indirizzi decimali. Avrebbero messo in programmi molto brevi per, ad esempio, aggiungere una serie di numeri per ottenere un totale. Quindi avrebbero fatto un passo singolo per vedere cosa stava succedendo. Potrebbero tenere premuto il tasto "invio" e guardarlo correre "veloce".

Sono sicuro che quasi tutti su SO si chiedono perché sia ​​utile diventare così semplici. Dimentichiamo com'è stato non sapere come programmare. Giocare con un computer giocattolo mette in atto concetti senza i quali non è possibile programmare, come le idee secondo cui il calcolo è un processo passo-passo, utilizzando un piccolo numero di primitive di base per costruire programmi e il concetto di memoria variabili come luoghi in cui sono memorizzati i numeri, in cui l'indirizzo o il nome della variabile sono distinti dal numero che contiene. Esiste una distinzione tra l'ora in cui si accede al programma e l'ora in cui "viene eseguito". Mi piace imparare a programmare come attraversare una serie di "speed bump", come programmi molto semplici, quindi loop e subroutine, quindi array, quindi I / O sequenziali, quindi puntatori e struttura dei dati.

Infine, quando si arriva a C, i suggerimenti sono confusi sebbene K&R abbia fatto un ottimo lavoro nel spiegarli. Il modo in cui li ho imparati in C era sapere come leggerli, da destra a sinistra. Come quando vedo int *pnella mia testa dico " pindica un int". C è stato inventato come un passo avanti rispetto al linguaggio assembly ed è quello che mi piace al riguardo - è vicino a quel "terreno". I puntatori, come qualsiasi altra cosa, sono più difficili da capire se non si dispone di tale messa a terra.


1
Un buon modo per imparare questo è programmare i microcontrollori a 8 bit. Sono facili da capire Prendi i controller Atmel AVR; sono persino supportati da gcc.
Xenu,

@Xenu: sono d'accordo. Per me era Intel 8008 e 8051 :-)
Mike Dunlavey,

Per me era un computer personalizzato a 8 bit (il "Forse") al MIT nelle nebbie oscure del tempo.
QuantumMechanic,

Mike - dovresti ottenere un CARDIACO per i tuoi studenti :)
QuantumMechanic,

1
@Quantum: CARDIAC- buono, non ne avevo sentito parlare. Il "Forse" - fammi indovinare, era che quando Sussman (et al) avevano persone che leggevano il libro Mead-Conway e creavano i loro chip LSI? Questo è stato un po 'dopo il mio tempo lì.
Mike Dunlavey,

17

Non ho ricevuto indicazioni finché non ho letto la descrizione in K&R. Fino a quel momento, i puntatori non avevano senso. Ho letto un sacco di cose in cui le persone dicevano "Non imparare i puntatori, sono confusi e ti faranno male alla testa e ti daranno aneurismi", quindi ho evitato a lungo da esso e ho creato questa aria non necessaria di concetto difficile .

Altrimenti, principalmente quello che pensavo fosse, perché mai vorresti una variabile che dovessi passare attraverso i cerchi per ottenere il valore di, e se volessi assegnare cose ad esso, dovevi fare cose strane per ottenere valori in loro. Il punto centrale di una variabile è qualcosa per memorizzare un valore, ho pensato, quindi il motivo per cui qualcuno voleva renderlo complicato era al di là di me. "Quindi con un puntatore devi usare l' *operatore per ottenere il suo valore ??? Che tipo di variabile sciocca è quella?" , Ho pensato. Inutile, nessun gioco di parole inteso.

Il motivo per cui era complicato era perché non capivo che un puntatore fosse un indirizzo a qualcosa. Se spieghi che si tratta di un indirizzo, che è qualcosa che contiene un indirizzo per qualcos'altro e che puoi manipolare quell'indirizzo per fare cose utili, penso che potrebbe chiarire la confusione.

Una classe che richiedeva l'uso dei puntatori per accedere / modificare le porte su un PC, l'uso dell'aritmetica dei puntatori per indirizzare diverse posizioni di memoria e la ricerca di un codice C più complicato che modificava i loro argomenti mi dissuadeva dall'idea che i puntatori fossero, ovviamente, inutili.


4
Se hai risorse limitate con cui lavorare (RAM, ROM, CPU), come nelle applicazioni integrate, i puntatori hanno rapidamente molto più senso.
Nick T,

+1 per il commento di Nick, specialmente per le strutture di passaggio.
nuovo123456

12

Ecco un esempio di puntatore / array che mi ha dato una pausa. Supponiamo di avere due array:

uint8_t source[16] = { /* some initialization values here */ };
uint8_t destination[16];

E il tuo obiettivo è copiare i contenuti di uint8_t dalla destinazione di origine usando memcpy (). Indovina quale dei seguenti obiettivi raggiunge questo obiettivo:

memcpy(destination, source, sizeof(source));
memcpy(&destination, source, sizeof(source));
memcpy(&destination[0], source, sizeof(source));
memcpy(destination, &source, sizeof(source));
memcpy(&destination, &source, sizeof(source));
memcpy(&destination[0], &source, sizeof(source));
memcpy(destination, &source[0], sizeof(source));
memcpy(&destination, &source[0], sizeof(source));
memcpy(&destination[0], &source[0], sizeof(source));

La risposta (Spoiler Alert!) È TUTTI. "destinazione", "e destinazione" e "e destinazione [0]" hanno tutti lo stesso valore. "& destination" è di tipo diverso rispetto agli altri due, ma ha sempre lo stesso valore. Lo stesso vale per le permutazioni di "fonte".

Per inciso, personalmente preferisco la prima versione.


Preferisco anche la prima versione (meno punteggiatura).
sigjuice,

++ Anche io, ma devi stare molto attento sizeof(source), perché se sourceè un puntatore, sizeofnon sarà quello che vuoi. A volte (non sempre) scrivo sizeof(source[0]) * number_of_elements_of_sourcesolo per stare lontano da quel bug.
Mike Dunlavey,

destinazione, destinazione e destinazione [0] non sono affatto uguali, ma ognuna, attraverso un meccanismo diverso, verrà convertita nello stesso vuoto * quando utilizzata in memcpy. Tuttavia, se usato come argomento di sizeof, otterrai due risultati diversi e tre risultati diversi sono possibili.
gnasher729,

Pensavo fosse richiesto l'indirizzo dell'operatore?
MarcusJ,

7

Dovrei iniziare dicendo che C e C ++ sono stati i primi linguaggi di programmazione che ho imparato. Ho iniziato con C, poi ho fatto C ++ a scuola, e poi sono tornato a C per diventare fluente in esso.

La prima cosa che mi ha confuso riguardo ai puntatori durante l'apprendimento del C è stata la semplice:

char ch;
char str[100];
scanf("%c %s", &ch, str);

Questa confusione era principalmente radicata nell'essere stata introdotta all'utilizzo del riferimento a una variabile per gli argomenti OUT prima che i puntatori mi fossero introdotti correttamente. Ricordo di aver saltato la scrittura dei primi esempi in C for Dummies perché erano troppo semplici solo per non far funzionare il primo programma che avevo scritto (molto probabilmente per questo).

Ciò che confondeva al riguardo era ciò che &chrealmente significava e perchéstr non ne aveva bisogno.

Dopo aver acquisito familiarità, ricordo di essere stato confuso riguardo all'allocazione dinamica. A un certo punto mi sono reso conto che avere puntatori ai dati non era estremamente utile senza un'allocazione dinamica di qualche tipo, quindi ho scritto qualcosa del tipo:

char * x = NULL;
if (y) {
     char z[100];
     x = z;
}

per tentare di allocare dinamicamente un po 'di spazio. Non ha funzionato Non ero sicuro che avrebbe funzionato, ma non sapevo come altrimenti avrebbe funzionato.

Più tardi ho imparato a conoscere mallocenew , ma mi sembravano davvero dei generatori di memoria magici. Non sapevo nulla di come avrebbero potuto funzionare.

Qualche tempo dopo mi è stato insegnato di nuovo la ricorsione (l'avevo imparato da solo, ma ora ero in classe) e ho chiesto come funzionava sotto il cofano - dove erano memorizzate le variabili separate. Il mio professore ha detto "in pila" e molte cose mi sono diventate chiare. Avevo già sentito il termine in precedenza e prima avevo implementato stack di software. Avevo sentito altri fare riferimento alla "pila" molto tempo prima, ma me ne ero dimenticato.

In questo periodo ho anche capito che l'uso di array multidimensionali in C può diventare molto confuso. Sapevo come funzionavano, ma erano così facili da aggrovigliare, che decisi di provare a aggirare usandoli ogni volta che potevo. Penso che il problema qui sia stato principalmente sintattico (soprattutto passando o restituendoli da funzioni).

Da quando ho scritto C ++ per la scuola per il prossimo anno o due ho avuto molta esperienza nell'uso di puntatori per strutture di dati. Qui ho avuto una nuova serie di problemi: confondere i puntatori. Avrei più livelli di puntatori (cose del genere node ***ptr;) che mi fanno inciampare. Dereggerei un puntatore il numero sbagliato di volte e alla fine ricorrerei per capire quanti ne *avevo bisogno per tentativi ed errori.

Ad un certo punto ho imparato come funzionava l'heap di un programma (in qualche modo, ma abbastanza buono da non farmi più stare sveglio la notte). Ricordo di aver letto che se guardi qualche byte prima del puntatore che mallocsu un certo sistema ritorna, puoi vedere quanti dati sono stati effettivamente allocati. Mi sono reso conto che il codice in mallocpotrebbe richiedere più memoria dal sistema operativo e questa memoria non faceva parte dei miei file eseguibili. Avere un'idea di lavoro decente su come mallocfunziona è davvero utile.

Poco dopo ho seguito un corso di assemblaggio, che non mi ha insegnato tanto sui puntatori come probabilmente pensano i programmatori. Mi ha fatto pensare di più a quale assembly il mio codice potrebbe essere tradotto. Avevo sempre cercato di scrivere un codice efficiente, ma ora avevo un'idea migliore su come farlo.

Ho anche preso un paio di lezioni in cui ho dovuto scrivere un po 'di musica . Quando scrivevo lisp non mi preoccupavo tanto dell'efficienza come in C. Avevo ben poca idea in cosa potesse essere tradotto questo codice se compilato, ma sapevo che sembrava usare molti simboli locali (variabili) creati le cose sono molto più facili. Ad un certo punto ho scritto un po 'di codice di rotazione dell'albero AVL in un po' di lisp, che mi è stato molto difficile scrivere in C ++ a causa di problemi con i puntatori. Mi sono reso conto che la mia avversione a ciò che pensavo fosse un eccesso di variabili locali aveva ostacolato la mia capacità di scrivere questo e molti altri programmi in C ++.

Ho anche preso una classe di compilatori. Mentre in questa classe sono passato al materiale avanzato e ho appreso l' assegnazione singola statica (SSA) e le variabili morte, il che non è così importante tranne che mi ha insegnato che qualsiasi compilatore decente farà un lavoro decente nel trattare con le variabili che sono non più utilizzato. Sapevo già che più variabili (compresi i puntatori) con tipi corretti e buoni nomi mi avrebbero aiutato a mantenere le cose dritte nella mia testa, ma ora sapevo anche che evitarle per motivi di efficienza era ancora più stupido di quanto dicessero i miei professori meno micro-ottimizzati me.

Quindi, per me, sapere molto del layout di memoria di un programma mi ha aiutato molto. Pensare al significato del mio codice, sia simbolicamente che sull'hardware, mi aiuta. L'uso di puntatori locali che hanno il tipo corretto aiuta molto. Scrivo spesso un codice simile a:

int foo(struct frog * f, int x, int y) {
    struct leg * g = f->left_leg;
    struct toe * t = g->big_toe;
    process(t);

così che se sbaglio un tipo di puntatore è molto chiaro dall'errore del compilatore quale sia il problema. Se avessi fatto:

int foo(struct frog * f, int x, int y) {
    process(f->left_leg->big_toe);

e ho sbagliato qualsiasi tipo di puntatore, l'errore del compilatore sarebbe molto più difficile da capire. Sarei tentato di ricorrere a cambiamenti di prova ed errore nella mia frustrazione e probabilmente peggiorare le cose.


1
+1. Completo e penetrante. Mi ero scordato di scanf, ma ora che lo fai apparire, ricordo di avere la stessa confusione.
Joe White,

6

Guardando indietro, c'erano quattro cose che mi hanno davvero aiutato a capire finalmente i suggerimenti. Prima di questo, potevo usarli, ma non li capivo completamente. Cioè, sapevo che se avessi seguito i moduli, avrei ottenuto i risultati desiderati, ma non ho compreso appieno il "perché" dei moduli. Mi rendo conto che non è esattamente quello che mi hai chiesto, ma penso che sia un corollario utile.

  1. Scrivere una routine che ha portato un puntatore a un numero intero e ha modificato l'intero. Questo mi ha dato le forme necessarie su cui costruire qualsiasi modello mentale di come funzionano i puntatori.

  2. Allocazione dinamica monodimensionale della memoria. Capire l'allocazione di memoria 1-D mi ha fatto capire il concetto del puntatore.

  3. Allocazione dinamica della memoria bidimensionale. Capire l'allocazione di memoria 2-D ha rafforzato questo concetto, ma mi ha anche insegnato che il puntatore stesso richiede memoria e deve essere preso in considerazione.

  4. Differenze tra variabili stack, variabili globali e memoria heap. Capire queste differenze mi ha insegnato i tipi di memoria a cui puntano / si riferiscono i puntatori.

Ognuno di questi elementi richiedeva di immaginare cosa stesse succedendo a un livello inferiore - costruire un modello mentale che soddisfacesse ogni caso che potevo pensare di lanciarci. Ci sono voluti tempo e fatica, ma ne è valsa la pena. Sono convinto che per capire i puntatori, devi costruire quel modello mentale su come funzionano e su come vengono implementati.

Ora torna alla tua domanda originale. Sulla base dell'elenco precedente, c'erano diversi elementi che avevo difficoltà a cogliere in origine.

  1. Come e perché si dovrebbe usare un puntatore.
  2. In che modo sono diversi e tuttavia simili agli array.
  3. Comprendere dove sono archiviate le informazioni del puntatore.
  4. Capire cosa e dove è puntato il puntatore.

Ehi, potresti indicarmi un articolo / libro / il tuo disegno / doodle / qualcosa in cui potrei imparare in un modo simile che hai descritto nella tua risposta? Credo fermamente che questa sia la strada da percorrere quando si impara bene, praticamente qualsiasi cosa. Comprensione profonda e buoni modelli mentali
Alexander Starbuck,

1
@AlexStarbuck - Non intendo che questo sembri irriverente, ma il metodo scientifico è un ottimo strumento. Disegna te stesso su ciò che pensi possa accadere per uno scenario particolare. Programma qualcosa per testarlo e analizza ciò che hai. Ha abbinato quello che ti aspetti? In caso contrario, identificare dove differisce? Ripeti se necessario, aumentando gradualmente la complessità per testare sia la tua comprensione che i tuoi modelli mentali.
Sparky

6

Ho avuto il mio "momento del puntatore" lavorando su alcuni programmi di telefonia in C. Ho dovuto scrivere un emulatore di scambio AXE10 usando un analizzatore di protocollo che comprendesse solo il classico C. Tutto dipendeva dalla conoscenza dei puntatori. Ho provato a scrivere il mio codice senza di loro (ehi, ero "pre-pointer" mi ha tagliato un po 'di gioco) e ho fallito completamente.

La chiave per comprenderli, per me, era l'operatore & (indirizzo). Una volta ho capito che &isignificava "indirizzo di i", quindi capire che *isignificava "il contenuto dell'indirizzo indicato da i" è arrivato un po 'più tardi. Ogni volta che scrivevo o leggevo il mio codice ripetevo sempre cosa significava "&" e cosa significava "*" e alla fine sono arrivato a usarli in modo intuitivo.

Con mia vergogna, sono stato costretto a VB e poi a Java, quindi la mia conoscenza del puntatore non è così nitida come una volta, ma sono contento di essere "post-pointer". Non chiedermi di usare una libreria che mi richiede di capire * * p, però.


Se &iè l'indirizzo e *iil contenuto, che cos'è i?
Thomas Ahle,

2
Sto sovraccaricando l'uso di i. Per una variabile arbitraria i, & i significa "l'indirizzo di" i, i da solo significa "il contenuto di & i", e * i significa "tratta il contenuto di & i come un indirizzo, vai a quell'indirizzo e restituisci il Contenuti".
Gary Rowe,

5

La principale difficoltà con i puntatori, almeno per me, è che non ho iniziato con C. Ho iniziato con Java. L'intera nozione di puntatori era davvero estranea fino a un paio di lezioni al college dove mi aspettavo di conoscere C. Quindi ho insegnato a me stesso le basi di C e come usare i puntatori nel loro senso basilare. Anche allora, ogni volta che mi ritrovo a leggere il codice C, devo cercare la sintassi del puntatore.

Quindi, nella mia esperienza molto limitata (1 anno di vita reale + 4 al college), i puntatori mi confondono perché non ho mai dovuto usarlo davvero in qualcosa di diverso da un ambiente scolastico. E posso simpatizzare con gli studenti che iniziano ora CS con JAVA anziché C o C ++. Come hai detto, hai imparato i suggerimenti nell'era del 'Neolitico' e probabilmente lo usi da allora. Per noi nuove persone, l'idea di allocare memoria e fare l'aritmetica puntatore è davvero estranea perché tutte queste lingue l'hanno sottratta.

PS Dopo aver letto il saggio di Spolsky, la sua descrizione di "JavaSchools" non assomigliava a ciò che ho passato al college a Cornell ('05 -'09). Ho preso le strutture e la programmazione funzionale (sml), i sistemi operativi (C), gli algoritmi (carta e penna) e tutta una serie di altre classi che non sono state insegnate in Java. Tuttavia, tutte le classi introduttive e gli elettivi sono stati tutti eseguiti in Java perché c'è valore nel non reinventare la ruota quando si tenta di fare qualcosa di più elevato rispetto all'implementazione di una tabella hash con puntatori.


4
Onestamente, dato che hai ancora problemi con i puntatori, non sono sicuro che la tua esperienza alla Cornell contraddica sostanzialmente l'articolo di Joel. Ovviamente abbastanza del tuo cervello è cablato in una mentalità Java per fare il suo punto.
jkerian,

5
Wat? I riferimenti in Java (o C #, o Python, o probabilmente decine di altre lingue) sono solo puntatori senza l'aritmetica. Comprendere i puntatori significa capire perché void foo(Clazz obj) { obj = new Clazz(); }è una no-op mentre void bar(Clazz obj) { obj.quux = new Quux(); }muta l'argomento ...

1
So quali sono i riferimenti in Java, ma sto solo dicendo che se mi chiedessi di fare una riflessione in Java o scrivere qualcosa di significativo in CI non puoi semplicemente chiarirlo. Richiede molta ricerca, come impararla per la prima volta, ogni volta.
shoebox639,

1
Come mai hai superato una classe di sistemi operativi in ​​C senza diventare fluente in C? Senza offesa, è solo che ricordo di dover praticamente sviluppare da zero un semplice sistema operativo. Devo aver usato i puntatori mille volte ...
Gravità,

5

Ecco una non-risposta: Usa cdecl (o c ++ decl) per capirlo:

eisbaw@leno:~$ cdecl explain 'int (*(*foo)(const void *))[3]'
declare foo as pointer to function (pointer to const void) returning pointer to array 3 of int

4

Aggiungono una dimensione aggiuntiva al codice senza una modifica significativa alla sintassi. Pensaci:

int a;
a = 5

C'è solo una cosa da cambiare: a. Puoi scrivere a = 6e i risultati sono ovvi per la maggior parte delle persone. Ma ora considera:

int *a;
a = &some_int;

Vi sono due aspetti arilevanti in momenti diversi: il valore effettivo di a, il puntatore e il valore "dietro" il puntatore. Puoi cambiare a:

a = &some_other_int;

... ed some_intè ancora in giro da qualche parte con lo stesso valore. Ma puoi anche cambiare ciò a cui punta:

*a = 6;

C'è un divario concettuale tra a = 6, che ha solo effetti collaterali locali e *a = 6che potrebbe influenzare un sacco di altre cose in altri luoghi. Il mio punto qui non è che il concetto di indiretto sia intrinsecamente complicato, ma perché è possibile fare sia la cosa locale immediata asia quella indiretta con *a... questo potrebbe essere ciò che confonde le persone.


4

Avevo programmato in c ++ per circa 2 anni e poi convertito in Java (5 anni) e non mi sono mai guardato indietro. Tuttavia, quando di recente ho dovuto usare alcune cose native, ho scoperto (con stupore) che non avevo dimenticato nulla dei puntatori e li trovavo persino facili da usare. Questo è un netto contrasto con quello che ho sperimentato 7 anni fa quando ho cercato di capire il concetto. Quindi, immagino che la comprensione e il gradimento siano una questione di maturità della programmazione? :)

O

I puntatori sono come andare in bicicletta, una volta che hai capito come lavorare con loro, non puoi dimenticarlo.

Tutto sommato, difficile da capire o no, l'idea del puntatore è MOLTO educativa e credo che dovrebbe essere compresa da ogni programmatore, indipendentemente dal fatto che programmi su una lingua con puntatori o meno.


3

I puntatori sono difficili a causa della direzione indiretta.


"Si dice che non ci siano problemi nell'informatica che non possono essere risolti da un ulteriore livello di riferimento indiretto" (non ho idea di chi l'abbia detto per primo)
The Archetypal Paul,

È come per magia, dove la cattiva direzione è ciò che confonde le persone (ma è assolutamente fantastico)
Nick T,

3

I puntatori (insieme ad alcuni altri aspetti del lavoro di basso livello), richiedono all'utente di portare via la magia.

La maggior parte dei programmatori di alto livello amano la magia.


3

I puntatori sono un modo per gestire la differenza tra una maniglia per un oggetto e un oggetto stesso. (ok, non necessariamente oggetti, ma sai cosa intendo, oltre a dove si trova la mia mente)

Ad un certo punto, probabilmente dovrai affrontare la differenza tra i due. In un linguaggio moderno e di alto livello ciò diventa la distinzione tra copia per valore e copia per riferimento. Ad ogni modo, è un concetto che è spesso difficile da comprendere per i programmatori.

Tuttavia, come è stato sottolineato, la sintassi per gestire questo problema in C è brutta, incoerente e confusa. Alla fine, se davvero provi a capirlo, un puntatore avrà senso. Ma quando inizi a gestire i puntatori ai puntatori e così via fino alla nausea, diventa davvero confuso per me e per le altre persone.

Un'altra cosa importante da ricordare sui puntatori è che sono pericolosi. C è il linguaggio di un programmatore principale. Presuppone che tu sappia cosa diavolo stai facendo e quindi ti dà il potere di rovinare davvero le cose. Mentre alcuni tipi di programmi devono ancora essere scritti in C, la maggior parte dei programmi no, e se hai un linguaggio che fornisce una migliore astrazione per la differenza tra un oggetto e il suo handle, allora ti suggerisco di usarlo.

Infatti, in molte moderne applicazioni C ++, spesso accade che qualsiasi aritmetica del puntatore richiesta sia incapsulata e sottratta. Non vogliamo che gli sviluppatori eseguano l'aritmetica del puntatore ovunque. Vogliamo un'API centralizzata e ben collaudata che esegua l'aritmetica del puntatore al livello più basso. Le modifiche a questo codice devono essere eseguite con grande cura e test approfonditi.


3

Penso che uno dei motivi per cui i puntatori C sono difficili è che confondono diversi concetti che non sono realmente equivalenti; tuttavia, poiché sono tutti implementati utilizzando i puntatori, le persone possono avere difficoltà a districare i concetti.

In C, i puntatori sono abituati, tra altre cose:

  • Definire strutture di dati ricorsive

In C definiresti un elenco collegato di numeri interi come questo:

struct node {
  int value;
  struct node* next;
}

Il puntatore è lì solo perché questo è l'unico modo per definire una struttura di dati ricorsiva in C, quando il concetto non ha davvero nulla a che fare con un dettaglio di così basso livello come gli indirizzi di memoria. Considera il seguente equivalente in Haskell, che non richiede l'uso di puntatori:

data List = List Int List | Null

Abbastanza semplice: un elenco è vuoto o formato da un valore e dal resto dell'elenco.

  • Scorrere su stringhe e array

Ecco come potresti applicare una funzione fooa ogni carattere di una stringa in C:

char *c;
for (c = "hello, world!"; *c != '\0'; c++) { foo(c); }

Nonostante utilizzi anche un puntatore come iteratore, questo esempio ha ben poco in comune con il precedente. La creazione di un iteratore che è possibile incrementare è un concetto diverso dalla definizione di una struttura di dati ricorsiva. Nessuno dei due concetti è particolarmente legato all'idea di un indirizzo di memoria.

  • Ottieni polimorfismo

Ecco una vera firma della funzione trovata in glib :

typedef struct g_list GList;

void  g_list_foreach    (GList *list,
                 void (*func)(void *data, void *user_data),
                         void* user_data);

Whoa! Questo è un bel boccone void*. Ed è tutto solo per dichiarare una funzione che scorre su un elenco che può contenere qualsiasi tipo di cosa, applicando una funzione a ciascun membro. Confrontalo con come mapè dichiarato in Haskell:

map::(a->b)->[a]->[b]

È molto più semplice: mapè una funzione che accetta una funzione che converte an ain a be la applica a un elenco di aper ottenere un elenco di b. Proprio come nella funzione C g_list_foreach, mapnon è necessario conoscere nulla nella propria definizione sui tipi a cui verrà applicato.

Per riassumere:

Penso che i puntatori C sarebbero molto meno confusi se le persone imparassero prima su strutture di dati ricorsive, iteratori, polimorfismo, ecc. Come concetti separati, e poi imparassero come i puntatori possono essere usati per implementare quelle idee in C , piuttosto che schiacciarle tutte concetti insieme in un unico argomento di "puntatori".


Uso improprio del c != NULLtuo esempio "Hello world" ... intendi *c != '\0'.
Olaf Seibert,

2

Penso che richieda una solida base, probabilmente a livello di macchina, con introduzione ad alcuni codici macchina, assemblaggi e come rappresentare gli elementi e la struttura dei dati nella RAM. Ci vuole un po 'di tempo, alcuni compiti a casa o una pratica di risoluzione dei problemi e qualche pensiero.

Ma se una persona conosce inizialmente lingue di alto livello (il che non è niente di sbagliato - un falegname usa un'ascia. Una persona che ha bisogno di dividere l'atomo usa qualcos'altro. Abbiamo bisogno di persone che sono falegnami e abbiamo persone che studiano atomi) e a questa persona che conosce un linguaggio di alto livello viene fornita un'introduzione di 2 minuti ai puntatori, e quindi è difficile aspettarsi che capisca l'aritmetica dei puntatori, i puntatori ai puntatori, la matrice di puntatori a stringhe di dimensioni variabili e una matrice di array di caratteri, ecc. Una solida base di basso livello può aiutare molto.


2
I puntatori di Groking non richiedono una comprensione del codice macchina o dell'assemblaggio.
Jason,

Richiesto, no. Ma le persone che comprendono l'assemblaggio probabilmente troverebbero i suggerimenti molto, molto più facili, poiché hanno già reso la maggior parte (se non tutte) le connessioni mentali necessarie.
cHao,

2

Il problema che ho sempre avuto (principalmente autodidatta) è il "quando" di usare un puntatore. Posso avvolgere la testa attorno alla sintassi per costruire un puntatore, ma devo sapere in quali circostanze un puntatore dovrebbe essere usato.

Sono l'unico con questa mentalità? ;-)


Capisco quello. La mia risposta si occupa di questo.
J. Polfer,

2

C'era una volta ... Avevamo microprocessori a 8 bit e tutti hanno scritto in assemblea. La maggior parte dei processori includeva un tipo di indirizzamento indiretto utilizzato per i jump table e i kernel. Quando sono arrivate le lingue di livello superiore, abbiamo aggiunto un sottile strato di astrazione e li abbiamo chiamati puntatori. Nel corso degli anni ci siamo sempre più allontanati dall'hardware. Ciò non è necessariamente una cattiva cosa. Sono chiamati lingue di livello superiore per un motivo. Più posso concentrarmi su ciò che voglio fare anziché sui dettagli di come è fatto meglio.


2

Sembra che molti studenti abbiano un problema con il concetto di riferimento indiretto, soprattutto quando incontrano il concetto di riferimento indiretto per la prima volta. Ricordo da quando ero uno studente che tra i +100 studenti del mio corso, solo una manciata di persone capiva davvero i suggerimenti.

Il concetto di indiretta non è qualcosa che usiamo spesso nella vita reale, e quindi è un concetto difficile da comprendere inizialmente.


2

Recentemente ho appena avuto il momento del clic del puntatore e sono stato sorpreso di averlo trovato confuso. Era più che tutti ne parlavano così tanto, che immaginavo stesse accadendo della magia oscura.

Il modo in cui l'ho ottenuto è stato questo. Immagina che a tutte le variabili definite venga assegnato spazio di memoria al momento della compilazione (nello stack). Se si desidera un programma in grado di gestire file di dati di grandi dimensioni come audio o immagini, non si vorrebbe una quantità fissa di memoria per queste potenziali strutture. Quindi aspetti fino al runtime per assegnare una certa quantità di memoria alla conservazione di questi dati (sull'heap).

Una volta che hai i tuoi dati in memoria, non vuoi copiarli tutti attorno al tuo bus di memoria ogni volta che vuoi eseguire un'operazione su di esso. Supponi di voler applicare un filtro ai dati dell'immagine. Hai un puntatore che inizia nella parte anteriore dei dati che hai assegnato all'immagine e una funzione scorre attraverso quei dati, cambiandoli in posizione. Se non sapessi cosa stiamo facendo, probabilmente finiresti per creare duplicati di dati mentre eseguivi l'operazione.

Almeno questo è il modo in cui lo vedo al momento!


Come ripensamento, potresti definire una quantità fissa di memoria per contenere immagini / audio / video, ad esempio su un dispositivo con memoria limitata, ma poi dovresti avere a che fare con un qualche tipo di streaming dentro e fuori dalla soluzione di memoria.
Chris Barry,

1

Parlando come un novizio C ++ qui:

Il sistema di puntatore ha impiegato un po 'di tempo a digerire non necessariamente a causa del concetto ma a causa della sintassi C ++ relativa a Java. Alcune cose che ho trovato confuso sono:

(1) Dichiarazione delle variabili:

A a(1);

vs.

A a = A(1);

vs.

A* a = new A(1); 

e apparentemente

A a(); 

è una dichiarazione di funzione e non una dichiarazione variabile. In altre lingue, c'è fondamentalmente solo un modo per dichiarare una variabile.

(2) La e commerciale viene utilizzata in diversi modi. Se è

int* i = &a;

quindi & a è un indirizzo di memoria.

OTOH, se lo è

void f(int &a) {}

quindi & a è un parametro passato per riferimento.

Anche se questo può sembrare banale, può essere fonte di confusione per i nuovi utenti: vengo da Java e Java è un linguaggio con un uso più uniforme degli operatori

(3) Relazione matrice-puntatore

Una cosa che è un po 'frustrante da capire è che un puntatore

int* i

può essere un puntatore a un int

int *i = &n; // 

o

può essere un array per un int

int* i = new int[5];

Quindi, solo per rendere le cose più difficili, i puntatori e l'array non sono intercambiabili in tutti i casi e i puntatori non possono essere passati come parametri dell'array.

Questo riassume alcune delle frustrazioni di base che ho avuto con C / C ++ e i suoi puntatori, che IMO, è notevolmente aggravato dal fatto che C / C ++ ha tutte queste stranezze specifiche del linguaggio.


Il C ++ 2011 ha migliorato un po 'le cose per quanto riguarda le dichiarazioni variabili.
gnasher729,

0

Personalmente non ho capito il puntatore anche dopo la laurea e dopo il mio primo lavoro. L'unica cosa che sapevo è che ne hai bisogno per un elenco collegato, alberi binari e per passare array in funzioni. Questa era la situazione anche al mio primo lavoro. Solo quando ho iniziato a rilasciare interviste, capisco che il concetto di puntatore è profondo e ha un uso e un potenziale straordinari. Poi ho iniziato a leggere K & R e a scrivere il proprio programma di test. Il mio intero obiettivo era guidato dal lavoro.
In questo momento ho scoperto che i puntatori non sono realmente né cattivi né difficili se vengono insegnati in modo positivo. Sfortunatamente quando ho imparato il C in laurea, il mio insegnante non era a conoscenza del puntatore e anche i compiti usavano meno puntatori. A livello di laurea l'uso del puntatore è davvero solo fino alla creazione di alberi binari e elenco collegato. Questo pensiero che non hai bisogno di una corretta comprensione dei puntatori per lavorare con loro, uccide l'idea di impararli.


0

Puntatori .. hah .. tutto ciò che riguarda il puntatore nella mia testa è che dà un indirizzo di memoria in cui i valori effettivi di qualunque sia il suo riferimento .. quindi nessuna magia al riguardo .. se impari qualche assemblaggio non avresti così tanti problemi ad imparare come funzionano i puntatori .. dai ragazzi ... anche in Java tutto è un riferimento ..


0

Il problema principale che le persone non capiscono perché hanno bisogno di puntatori. Perché non sono chiari su stack e heap. È bene iniziare dall'assemblatore a 16 bit per x86 con una piccola modalità di memoria. Ha aiutato molte persone a farsi un'idea di stack, heap e "indirizzo". E byte :) I programmatori moderni a volte non possono dirti quanti byte sono necessari per occupare spazio a 32 bit. Come possono farsi un'idea dei puntatori?

Il secondo momento è la notazione: dichiarate il puntatore come *, ottenete l'indirizzo come & e questo non è facile da capire per alcune persone.

E l'ultima cosa che ho visto è stato un problema di archiviazione: capiscono heap e stack ma non riescono a farsi un'idea di "statico".

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.