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..
(((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.