Pagina 3 di 3

Re: Puntatori chiarimenti [risolto]

Inviato: mar 21 lug 2020, 17:52
da SlackNewbie
Salve!!!
Scusate se riapro la discussione ma mi è sorto un dubbio leggendo un po di cose a proposito dell'uso delle stringhe.
Perchè una stringa può essere dichiarata tramite un puntatore?
Cioè, mi spiego meglio:

Codice: Seleziona tutto

char stringa="abcdef"; oppure potri scrivere 
                                   char stringa[]={'a','b','c','d','e','f'};
                                   char stringa[7]={'a','b','c','d','e','f','\0'}
printf("%s",stringa);
char *stringa1="abcdef";
printf("\n%s",stringa1);
Non capisco il perchè di questa sintassi.
Perfchè le cose non vanno se faccio
int *vett[]={1,2,3}
?
Sto facendo mente locale sul discorso del legame tra array e puntatori solo che non mi riesco a spiegare la questione....

Re: Puntatori chiarimenti [risolto]

Inviato: mer 22 lug 2020, 0:12
da albatros
Mah, non mi sono mai posto il problema, è una sintassi che torna comoda... La stringa è un qualcosa di ben definito, il compilatore sa esattamente quanta memoria allocare e come inizializzarla. Di fatto è un array di caratteri e il nome corrisponde all'indirizzo in memoria dove si trova il primo carattere dell'array.
[code]#include<stdio.h>
#include<stdlib.h>
int main()
{
char* p={"pippo"};
printf("Valore di &p[0]: %p\n",&p[0]);
printf("Valore di p: %p\n",p);
return 0;
}
[/code]
A me da:
Valore di &p[0]: 0x556b1947a004
Valore di p: 0x556b1947a004

Mentre con il tuo ultimo esempio, tu stai dichiarando un array di puntatori a interi e lo stai inizializzando con interi, non con puntatori a interi. Inoltre, in generale non puoi decidere di assegnare a un puntatore un indirizzo a caso, ci deve pensare il sistema operativo, altrimenti è molto probabile un segmentation fault ([url]https://it.wikipedia.org/wiki/Errore_di_segmentazione[/url]).
Puoi dichiarare e inizializzare un array di interi con la sintassi degli array, il sistema operativo allocherà per te la memoria. Ma un puntatore, d'altra parte, non alloca memoria, o la trova già pronta perché di altre variabili già dichiarate oppure va allocata con malloc o calloc. Una volta che una porzione di memoria è allocata, ti puoi spostare al suo interno sia con la sintassi degli array che con l'aritmetica dei puntatori.

Considera il seguente programmino:
[code]#include<stdio.h>
#include<stdlib.h>

int main()
{
int i;
printf("Creo e inizializzo un vettore di interi: int vet[]={100,200,300,400,500};\n");
int vet[]={100,200,300,400,500};
printf("Ora lo visualizzo con: for(i=0;i<5;i++) printf(\"%%d\\n\",vet[i]);\n");
for(i=0;i<5;i++) printf("%d\n",vet[i]);
int a=100;
int b=200;
int c=300;
int d=400;
int e=500;
printf("Ora creo un vettore di puntatori a interi e lo inizializzo con NULL: int* vptr[]={NULL,NULL,NULL,NULL,NULL};\n");
int* vptr[]={NULL,NULL,NULL,NULL,NULL};
printf("Da notare che gli elementi di vptr non sono interi, ma sono puntatori a interi. Ora gli assegno di indirizzi di alcune variabili int.\n");
vptr[0]=&a;
vptr[1]=&b;
vptr[2]=&c;
vptr[3]=&d;
vptr[4]=&e;
printf("Visualizzo gli elementi di vptr, che sono indirizzi di memoria: for(i=0;i<5;i++) printf(\"%%p\\n\",vptr[i]);\n");
for(i=0;i<5;i++) printf("%p\n",vptr[i]);
printf("Visualizzo i valori a cui puntano gli indirizzi di vptr: for(i=0;i<5;i++) printf(\"%%d\\n\",*vptr[i]);\n");
for(i=0;i<5;i++) printf("%d\n",*vptr[i]);


return 0;
}
[/code]
[] ha priorità maggiore di *, quindi *vptr[i] equivale a *(vptr[i]), ossia prima viene preso il valore nella i-esima posizione di vptr, che è un puntatore, poi * fornisce il valore a cui punta, che è un intero.
Dovresti ottenere qualcosa tipo:

Creo e inizializzo un vettore di interi: int vet[]={100,200,300,400,500};
Ora lo visualizzo con: for(i=0;i<5;i++) printf("%d\n",vet[i]);
100
200
300
400
500
Ora creo un vettore di puntatori a interi e lo inizializzo con NULL: int* vptr[]={NULL,NULL,NULL,NULL,NULL};
Da notare che gli elementi di vptr non sono interi, ma sono puntatori a interi. Ora gli assegno di indirizzi di alcune variabili int.
Visualizzo gli elementi di vptr, che sono indirizzi di memoria: for(i=0;i<5;i++) printf("%p\n",vptr[i]);
0x7ffdc667db88
0x7ffdc667db8c
0x7ffdc667db90
0x7ffdc667db94
0x7ffdc667db98
Visualizzo i valori a cui puntano gli indirizzi di vptr: for(i=0;i<5;i++) printf("%d\n",*vptr[i]);
100
200
300
400
500

P.S. Scusa, ho disabilitato il bbcode perché avevo problemi con gli [i], che venivano interpretati come inizio di corsivi. Potrei cambiare nome alla variabile, ma ora non ho tempo, penso e spero, comunque, che si capisca lo stesso. Ciao

Re: Puntatori chiarimenti [risolto]

Inviato: mer 22 lug 2020, 11:23
da SlackNewbie
Ciao albatros :) e grazie ancora per l'aiuto
Sei riuscito in effetti a chiarirmi un fatto a cui stavo iniziando a pensare e cioè come dichiarare un array di puntatori.Ma il discorso che tu hai fatto a tal proposito vale anche per array di puntatori che puntano ad altri tipi di variabili ??Ad esempiio,char o anche magari fare un array di puntatori che puntano ad una struct??
Dubbio:come funziona un punatore ad una struct??esso mi restituisce l'indirizzo del primo campo o cosa??Questo è un dubbio che mi è venuto nel momento in cui sto scrivendo, casomai ci provo a testare il mio dubbio sul compilatore.
Sono riuscito a recuperare tre/quattro libri sul C oltre quello consigliato dal prof. però molti miei dubbi e domande strane sull'uso ( o un loro uso combinato) dei tipi di dato non trovano risposta....e quindi sono costretto ad andare a tentativi compilando pezzetti di codice che implementando i vari dubbi..mah non è esattamente oggettiva come cosa...
Ma secondo te,albatros,sbaglio a farmi venire questi dubbi senza una reale applicazione in un qualche esercizio d'esame o problema (con annessa soluzione algoritmica)??Cioè questi sono tutti dubbi che sono io a creare e che non scaturiscono da una qualche soluzione di un problema da implementare in C.Non so se sono stato chiaro.
Ritornando però alla scrittura:

Codice: Seleziona tutto

char stringa="abcdef"; oppure potri scrivere 
                                   char stringa[]={'a','b','c','d','e','f'};
                                   char stringa[7]={'a','b','c','d','e','f','\0'}
printf("%s",stringa);
char *stringa1="abcdef";
printf("\n%s",stringa1);
tu dici
La stringa è un qualcosa di ben definito, il compilatore sa esattamente quanta memoria allocare e come inizializzarla. Di fatto è un array di caratteri e il nome corrisponde all'indirizzo in memoria dove si trova il primo carattere dell'array.
Ma questa cosa non vale anche per un qualunque stipo di array,ad esempio anche un array di interi?
Cioè il nome dell'array di interi non corrisponde all'indirizzo della sua prima cella di memoria?
E allora perchè per un array di itpo char posso scrivere equivalentemente
char stringa="abcdef" oppure
char *stringa="abcdef"
??
Invece per un array di interi questa cosa non vale:
int vett[]={1,2,3}
int *vet[]={1,2,3}
??
Non sto riuscendo a capire.. :oops:
(((penso che il prof non faccia queste domande all'esame)))

Re: Puntatori chiarimenti [risolto]

Inviato: mer 22 lug 2020, 14:32
da brg
Il codice

Codice: Seleziona tutto

char stringa="abcdef";
è sbagliato e non può funzionare. Un carattere non è una stringa.

Re: Puntatori chiarimenti [risolto]

Inviato: mer 22 lug 2020, 16:40
da albatros
SlackNewbie ha scritto:
mer 22 lug 2020, 11:23
E allora perchè per un array di itpo char posso scrivere equivalentemente
char stringa="abcdef" oppure
char *stringa="abcdef"
??
Invece per un array di interi questa cosa non vale:
int vett[]={1,2,3}
int *vet[]={1,2,3}
??
Non sto riuscendo a capire.. :oops:
(((penso che il prof non faccia queste domande all'esame)))
Una stringa è una entità che il C gestisce come array di caratteri. Dentro questo array di caratteri, la fine della stringa è delimitata da \0. Quando inizializzi la stringa, usi una sequenza di caratteri fra virgolette. Il compilatore la riconosce come stringa e sa cosa fare. Per gestirla come variabile devi dichiarare o un array di caratteri o un puntatore a char. In entrambi i casi il compilatore sa che cosa fare, perché, una volta allocata e inizializzata la memoria, puoi accedervi sia con la sintassi degli array sia con quella dei puntatori.
Quindi, char stringa[]="abcdef" e char *stringa="abcdef" sono entrambe definizioni corrette.

Scusa, l'altra volta non ci avevo fatto caso, ma, come invece ti ha già giustamente segnalato brg, char stringa="abcdef" invece non lo è.
Infatti, se provo a usarla, il compilatore (gcc 9.3.0) mi dice:
warning: initialization of ‘char’ from ‘char *’ makes integer from pointer without a cast [-Wint-conversion]
perché ho dichiarato stringa come singolo carattere e poi provo a inizializzarla con una stringa, per la quale il compilatore alloca e inizializza una certa porzione di memoria, restituendo un puntatore a dove inizia quest'area di memoria per gestirla successivamente. Il compilatore va avanti, ma mi avverte che io inizializzo char (che poi di fatto è un intero, come saprai c'è una corrispondenza fra questi tipi di dati, in caso contrario cerca in rete) con un puntatore a char (quello che viene fuori dall'allocazione della memoria per gestire la stringa), quindi creo un intero da un puntatore senza esplicitamente forzare la conversione fra dati diversi con un cast.
Sono stato avvisato che potrei aver fatto una sciocchezza.
E, infatti, se dopo aggiungo la riga:
printf("Stringa uno: %s\n", stringa1);
il compilatore mi avverte nuovamente:
warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘int’ [-Wformat=]
7 | printf("Stringa uno: %s\n", stringa1);
| ~^ ~~~~~~~~
| | |
| | int
| char *
| %d

che stringa1 è un char (ossia, di fatto un intero) e che io sto chiedendo di visualizzare un intero con %s, ossia un puntatore a char.
O, meglio, che %s vuole un puntatore a char, ma io gli passo un intero.
Se ignoro gli avvisi e testardamente eseguo il programma, ottengo un segmentation fault.

Come vedi, il compilatore è di grande aiuto nell'individuare questo tipo di errori (che capitano anche al programmatore migliore, basta un errore di battitura per perdere un asterisco).
A te funzionava? Avevi provato?

Nel secondo caso, con gli interi, nel primo sottocaso non ci sono ambiguità: vuoi creare un array di interi e inizializzarlo con i valori fra le parentesi graffe. Il compilatore sa cosa fare.
Nel secondo sottocaso, con int *vet[] tu stai dichiarando un array di puntatori ad interi, non puoi inizializzarlo con degli interi, lo devi inizializzare con dei puntatori ad interi.
Neppure usare int *vet={1,2,3}; va bene, perché int *vet è un puntatore a un intero e lo stai inizializzando con una sequenza di interi.

Non si potrebbe far interpretare al compilatore int *vet={1,2,3}; come int vet[]={1,2,3}; ?
Boh, alla fine il compilatore trasforma il codice in altre istruzioni comprensibili dal processore, quindi è una questione di convenzioni. Se è stato deciso di non farglielo fare, probabilmente è perché scegliere questa strada avrebbe indotto a un maggior numero di errori e ambiguità, mentre con le stringhe questo non succede, perché si tratta di convertire un qualcosa di facilmente riconoscibile, racchiuso fra virgolette, ma che non fa parte dei tipi di dato del C, in una rappresentazione interna a questo linguaggio.
D'altra parte, anche:
char *vet={'p','i','p','p','o'};
non va bene, in modo del tutto coerente con int *vet={1,2,3};
E' con questa scrittura che devi fare il confronto, non con char *vet="pippo";

Ripeto: i puntatori consentono di accedere alla memoria, ma l'allocazione della memoria non avviene dichiarando i puntatori, ma viene fatta a parte, mentre con gli array lo puoi fare contestualmente.
SlackNewbie ha scritto:
mer 22 lug 2020, 11:23
Ma secondo te,albatros,sbaglio a farmi venire questi dubbi senza una reale applicazione in un qualche esercizio d'esame o problema (con annessa soluzione algoritmica)??Cioè questi sono tutti dubbi che sono io a creare e che non scaturiscono da una qualche soluzione di un problema da implementare in C.Non so se sono stato chiaro.
I linguaggi di programmazione fanno da interfaccia fra gli umani e le macchine. Hanno una loro ragionevolezza e una loro coerenza interna e formale, molto di più delle lingue come italiano, inglese, ecc, ma si tratta pur sempre di convenzioni. Non sono matematica. La successione di Fibonacci è la stessa da 8 secoli e, senza entrare in discorsi filosofici, diciamo che ha una sua esistenza intrinseca permanente, mentre le regole del C, del Fortan, del C++, ecc. sono cambiate negli anni (es. in C, nel C99 per allocare array di dimensione nota a runtime, semplificando molto la vita al programmatore, soprattutto nel caso multidimensionale).
Quindi, secondo me, non ha molto senso preoccuparsi oltre un certo limite della consistenza sintattica di un linguaggio, a meno che non si sia nella commissione che lavora ai nuovi standard o che si usi il linguaggio a un livello molto avanzato, ad es. nel gruppo di sviluppo di un compilatore o di un debugger.
Dato che il tempo è limitato e ci sono tante cose da fare o da sapere, meglio investirlo per usare il linguaggio per farci qualcosa anziché scavare a fondo nei dettagli. Anche perché è usandolo intensamente ed estensivamente che ci si rende conto del perché sono state fatte certe scelte a livello di standard. Fai bene a cercare di capire come funziona la sintassi del C, ma, dato che hai detto che, fatto l'esame, non hai intenzione di continuare a usarlo, attenzione a non esagerare, soprattutto se questo va a discapito della pratica del linguaggio applicata a casi "concreti" (non so, ad esempio calcolare il determinante di una matrice, ordinare una lista, gestire una coda). Per ora va bene, siamo ancora alle basi e, se si hanno ben chiare, si potrà andare avanti più facilmente, tuttavia trovo già sbilanciato il tempo dedicato alla teoria rispetto a quello dedicato alla pratica.

Re: Puntatori chiarimenti [risolto]

Inviato: sab 25 lug 2020, 11:23
da SlackNewbie
Salve!!!
Ringrazio come sempre,per prima cosa,per l'aiuto che mi date.
Scusate il ritardo nel rispondervi.
Si in effettiho sbagliato a scrivere nel post..è così:
char stringa[]="abcd";
Non si potrebbe far interpretare al compilatore int *vet={1,2,3}; come int vet[]={1,2,3}; ?
Boh, alla fine il compilatore trasforma il codice in altre istruzioni comprensibili dal processore, quindi è una questione di convenzioni. Se è stato deciso di non farglielo fare, probabilmente è perché scegliere questa strada avrebbe indotto a un maggior numero di errori e ambiguità, mentre con le stringhe questo non succede, perché si tratta di convertire un qualcosa di facilmente riconoscibile, racchiuso fra virgolette, ma che non fa parte dei tipi di dato del C, in una rappresentazione interna a questo linguaggio
Ok tutto chiaro :thumbright:
Dato che il tempo è limitato e ci sono tante cose da fare o da sapere, meglio investirlo per usare il linguaggio per farci qualcosa anziché scavare a fondo nei dettagli. Anche perché è usandolo intensamente ed estensivamente che ci si rende conto del perché sono state fatte certe scelte a livello di standard
Si hai ragione albatros infatti è da un po di giorni che mi barcameno con gli esercizi....
Ringrazio nuovamente tutti per l'attenzione siete grandi =D>