PDA

View Full Version : [C] Piccolo aiuto con le stringe


Hieicker
16-01-2013, 14:48
Ciao a tutti! Son tornato con qualche altro dubbio. Sto cercando di provare le varie funzioni presenti in C riguardo le stringhe, come ad esempio la "strcmp" utilizzata per il confronto tra due stringhe. Sto quindi provando a fare una semplice funzioncina che prende in ingresso due stringhe e fa il confronto. Questo è il codice che ho scritto:


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int confronta_stringa(char *stringa, char *stringa2);

int main()
{
int operazione;
char *stringa;
char *stringa2;
float operatore1, operatore2;
printf("inserisci l'operazione che vuoi svolgere\n");
printf("1 - Riempi una stringa\n2 - Confronta Stringhe\n3");
scanf("%d",&operazione);

switch(operazione){
case 1:
push_stringa(stringa);
break;
case 2:
confronta_stringa(stringa, stringa2);
break;
default:
printf("Non hai digitato correttamente un numero");
break;
}
return 0;
}

int confronta_stringa(char *stringa, char *stringa2){
/*
int numero;
printf("quanti caratteri devono comporre la stringa?\n");
scanf("%d", &numero);
stringa = (char*) malloc(numero*sizeof(char));
*/
printf("Inserisci la prima stringa\n");
scanf("%s", stringa);
printf("inserisci la seconda stringa\n");
scanf("%s", stringa2);
if (!strcmp(stringa, stringa2))
printf("diversi");

return 0;
}



Ora quello che non capisco, è il fatto che dopo aver inserito le due stringhe ottengo un errore di segmentation fault. In più non ho ben chiaro se in questo caso (in cui non so di quanti caratteri sarà composta la stringa) mi conviene utilizzare la funzione malloc o se posso farne a meno.

Grazie,

Hieicker

Hieicker
16-01-2013, 15:17
Ok grazie Antonio, anche se non ho ben presente cosa intendi con "terminatore". Comunque se vedi, la parte di codice in cui dovrei inserire il numero, attualmente è commentata, quindi non viene applicata.

M@Rk0
16-01-2013, 15:17
prova ad usare
stringa = (char*) malloc((numero+1)*sizeof(char))
perchè ti serve anche il terminatore di stringa '/0'
poi non ho capito alcune cose del codice:
la funzione push_stringa(stringa) quando la usi?
fai la malloc solo per stringa e non per stringa2?
se specifichi n caratteri per la stringa poi non controlli che l'utente ne inserisca davvero (al massimo) n-1, ricadendo quindi nell'errore del terminatore mancante.
Credo che il sigseg ti venga fuori proprio perchè una volta premuto invio, provi a mettere n+1 caratteri in un'area di memoria grande n, quindi cerchi di accedere ad un'area "vietata" (spiegato un po' a cazzo di cane perchè a lezione stavo poco attento, mi bastava che i programmi che facevo funzionassero :asd:)

EDIT:
Ok grazie Antonio, anche se non ho ben presente cosa intendi con "terminatore". Comunque se vedi, la parte di codice in cui dovrei inserire il numero, attualmente è commentata, quindi non viene applicata.

il terminatore di stringa è un carattere identificato da '/0' (ricorda che gli apici singoli delimitano un char e i doppi una string), serve per far capire che sei arrivato alla fine di una stringa (come il punto nella sintassi italiana, alla fine di una frase)

comunque se la parte commentata non viene applicata, tu crei soltanto due puntatori a stringa, non allochi lo spazio per scriverci dentro (cosa che faresti con la malloc commentata)

EDIT2: sono andato a rinfrescarmi la memoria. prova ad usare anche la fgets(stringa,numero,stdin) che legge numero caratteri dallo standard input

darksax
16-01-2013, 16:08
Dal listato sembra che manchi l'allocazione della memoria (malloc()) per
char *stringa;
char *stringa2;

se l'input delle stringhe, suppongo, dovrebbe avvenire con la funzione push_stringa(stringa);
(e stringa2 ??)
forse lo fai li dentro (allora manca '*' e '&')

Se invece utilizzi solo la (come sembra dal codice)
confronta_stringa()
allora ti manca sicuramente la malloc per le 2 stringhe
(la scanf non alloca spazio).

stohuman
17-01-2013, 09:11
(spiegato un po' a cazzo di cane perchè a lezione stavo poco attento, mi bastava che i programmi che facevo funzionassero :asd:)

Mi ricorda...
http://blog.tsmts.com/store/blog/doc/1328329421_steve_billEditB20120130222031.jpg


Tornando, nella funzione confronta_stringa()..

int numero;
printf("quanti caratteri devono comporre la stringa?\n");
scanf("%d", &numero);
stringa = (char*) malloc(numero*sizeof(char));

Non so perche ma non mi torna.. apparte il fatto che nella malloc non metti anche stringa2, stringa ha gia dei blocchi in memoria, e col c "vecchio" non si puo riallocare piu memoria alla stessa variabile.
Ora se quello che sto dicendo non sono boiate dovresti dichiarare una terza stringa interna a quella funzione.

darksax
17-01-2013, 09:40
Manca la malloc di stringa2 e lo spazio per il terminatore (\0).
Inoltre ti conviene impostare a size dei caratteri da accettare con la scanf()
per non incorrere in un buffer overflow.
percio' se allochi numero*sizeof(char)
dovrai avere in input numero*sizeof(char)-1

@stohuman
Si puo' riallocare un spazio gia' allocato con la "realloc"

stohuman
17-01-2013, 09:57
Ero a conoscenza solo di malloc e calloc, sto ancora studiando questa parte :D

kwb
17-01-2013, 10:07
Inoltre ti conviene impostare a size dei caratteri da accettare con la scanf()
per non incorrere in un buffer overflow.
percio' se allochi numero*sizeof(char)
dovrai avere in input [B]numero*sizeof(char)-1


A scopo informativo, c'è da dire anche che finchè si tratta di array di caratteri sizeof(char) = 1 . Quindi scrivere numero o numero*sizeof(char) è identico.
È comunque buona pratica fare come dici tu :)

Lo specifico perchè quando io ho studiato sta cosa nessuno me lo aveva detto e sono diventato matto per capire come mai se allocavo un array di char andava e se lo facevo uguale con degli interi mi dava problemi. :D

Hieicker
17-01-2013, 10:11
Ragazzi grazie a tutti per il prezioso aiuto :) ! Ora sono al lavoro, ma stasera appena ho un po di tempo provo a seguire i vostri consigli allocando lo spazio per le due variabili stringa che uso.

La funzione push_stringa è una cosa a parte, quindi non tenetene conto :).

darksax
17-01-2013, 11:17
[QUOTE=darksax;38872526]
...
percio' se allochi numero*sizeof(char)
dovrai avere in input [B]numero*sizeof(char)-1

mi autoquoto :)
Per chiarezza si dovrebbe scrivere
numero*sizeof(char) - sizeof(char)

M@Rk0
17-01-2013, 13:45
[QUOTE=darksax;38872526]
...
percio' se allochi numero*sizeof(char)
dovrai avere in input [B]numero*sizeof(char)-1

mi autoquoto :)
Per chiarezza si dovrebbe scrivere
numero*sizeof(char) - sizeof(char)

che raccogliendo diventa (numero-1)*sizeof(char) :asd:

comunque il problema sussiste sempre se non c'è un controllo su quanti caratteri verranno poi immessi da tastiera, quindi consiglierei l'uso di fgets(stringa,numero,stdin)

darksax
17-01-2013, 14:31
Ed in ultimo...
utilizzando la malloc devi ricordarti della free() :cool:

M@Rk0
17-01-2013, 16:10
comunque se è un programma fine a se stesso per testare le funzioni che operano su stringhe, tanto vale barare:

non inizializzi i due puntatori a stringa nel main, la funzione perde gli argomenti, e nella funzione inizializzi le due stringhe con
char stringa[numero+1];
char stringa2[numero+1];
dopo aver chiesto il numero :asd:

Hieicker
17-01-2013, 20:07
Ciao ragazzi! Aggiornamento. Ho allocato le due stringhe ed ora la funzione sembra svolgere le sue istruzioni egregiamente. Tuttavia come dite voi potrei ottimizzarla ulteriormente. Innanzitutto non ho usato un terminatore. Nel manuale che sto leggendo non è ancora stato nominato, quindi vi chiedo... A che scopo viene utilizzato?
Inoltre non ho ben capito se sbaglio qualcosa nella alloc o nel modo in cui gli passo l'input. Ecco il codice. Ho aggiunto le due funzioni free per deallocare la memoria alla fine della funzione.


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int confronta_stringa(char *stringa, char *stringa2);

int main()
{
int operazione;
char *stringa;
char *stringa2;
float operatore1, operatore2;
printf("inserisci l'operazione che vuoi svolgere\n");
printf("1 - Riempi una stringa\n2 - Confronta Stringhe\n3 - Esci dal programma\n");
scanf("%d",&operazione);

switch(operazione){
case 1:
push_stringa(stringa);
break;
case 2:
confronta_stringa(stringa, stringa2);
break;
case 3:
break;
default:
printf("Non hai digitato correttamente un numero");
break;

}
return 0;
}

int confronta_stringa(char *stringa, char *stringa2){
int numero;
printf("quanti caratteri devono comporre ogni stringa?\n");
scanf("%d", &numero);
stringa = (char*) malloc(numero*sizeof(char));
stringa2 = (char*) malloc(numero*sizeof(char));
printf("Inserisci la prima stringa\n");
scanf("%s", stringa);
printf("inserisci la seconda stringa\n");
scanf("%s", stringa2);
if (!strcmp(stringa, stringa2))
printf("Le stringe da te inserite sono uguali");

free(stringa);
free(stringa2);
return 0;
}


Grazie ancora per l'aiuto :).

kwb
17-01-2013, 20:58
Lo '\0' viene utilizzato semplicemente per indicare la fine di una stringa.
Può essere utile in altre funzioni o in altri casi per capire quando la stringa è finita ( ad esempio strlen ritorna il numero di caratteri della stringa fino al terminatore '\0' -escluso ).

M@Rk0
17-01-2013, 21:15
il terminatore di stringa '\0' (carattere che corrisponde a NULL) serve alle funzioni (anche quelle che potresti scrivere tu!) per sapere dove finisce una stringa (come strlen ad esempio).
Quando dici char stringa[10]; quello che succede è (in parole povere) che viene allocata in memoria un'area contigua di 10 Byte. Magari in quei 10 Byte prima c'era scritto qualcosa che non è più utilizzato, e c'è rimasto. Infatti se provi a fare un programma che alloca una stringa e poi la stampa direttamente vedrai che marasma di caratteri senza senso ti può venir fuori.
ES:
questa è la memoria prima di fare char stringa[10];

Indirizzi di memoria: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |...
------------------------|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---
Contenuto: # | @ | 3 | x | + | ù | * | # | # | ò | ç | : | % | & | £ | € |...
(in realtà il contenuto è la codifica in bit dei caratteri che ho messo, ma per farla breve ho messo la rappresentazione in carattere)
mettiamo che alla richiesta di allocazione di memoria, lo scheduler del Sistema Operativo assegni lo spazio [3,4,5,6,7,8,9,A,B,C].
Ora, se fai printf("%c",stringa[0]); il risultato sarà il carattere 'x'.
mentre con printf("%s",stringa); darà in output "x+ù*##òç:%".
se a questo punto fai scanf("%s", stringa); e scrivi "ciao", il contenuto della memoria sarà questo:
Indirizzi di memoria: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |...
------------------------|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---
Contenuto: # | @ | 3 | c | i | a | o | \0| # | ò | ç | : | % | & | £ | € |...
(ho evidenziato in rosso le locazioni di memoria riservate alla stringa, in marrone il contanuto e in arancione il contenuto modificato)

ora la printf di stringa darà in output "ciao", perchè legge fino al terminatore.
se il terminatore non fosse stato aggiunto (in automatico dalla scanf), avresti stampato "ciao##òç:%"

il terminatore serve ad esempio anche per leggere manualmente una stringa, magari carattere per carattere facendo un ciclo
int i=0;
while(stringa[i]!='\0')
{
fai_qualcosa(argomento1,argomento2);
i++;
}

spero di non aver detto minchiate.
ah ho visto ora il reply di kwb (che quando ho iniziato a scrivere non c'era) e in sostanza ho detto quello :asd:

darksax
18-01-2013, 09:42
Inoltre la scanf() al termine dell'input di una stringa inserisce il terminatore.
Quindi se inserisci 10 caratteri in realta ne ritrovi 11...
Ecco spiegato xche' i buffer stringa e stringa2 devono prevedere nella malloc un "+sizeof(char)"...

stringa = (char*) malloc(numero*sizeof(char));
stringa2 = (char*) malloc(numero*sizeof(char));

Facendo cosi' sei a rischio....(prima o poi vai in crash)

darksax
18-01-2013, 10:15
:muro: Se numero vale 10 ed inserisci 10 caratteri, debbugando i 2 buffer ti accorgi che ti funziona xche' probabilmente lo spazio successivo al buffer non e' utilizzato (insidiosa botta di c.),

Riprendendo l'esempio di M@Rk0, prova ad immaginare se le due variabili stringa siano allocate in modo contiguo nella memoria (stringa seguita dal buffer stringa2) ma inserite nell'ordine stringa2 e poi stringa e ricordando che la scanf aggiunge un \0 alla fine dell'input.

Ammettiamo che la malloc ritorni questi 2 puntatori

*stringa = 0x02
*stringa2 = 0x0c


Inserisco Stringa2

0 1 2 3 4 5 6 7 8 9 a b c d e f
0000 . . . . . . . . . . . . s t r i
0010 n g a _ 2 2 \0 . . . . . . . . .


e poi Stringa

0 1 2 3 4 5 6 7 8 9 a b c d e f
0000 . . s t r i n g a _ 1 1 \0 t r i
0010 n g a _ 2 2 \0 . . . . . . . . .


Il passo successivo e' la strcmp()...
Quale sara' il confronto?

*stringa = 0x02 vale "stringa_11"
*stringa2 = 0x0c vale ""
Quindi il confronto e' giustamente falso (xche' ho inserito valori differenti) ma i valori che utilizzo sono differenti da quelli che mi aspetto!!!!
Ma sopratutto se dopo Stringa2 la zona di memoria era occupata da un'altra variabile (x esempio un'altro puntatore) utilizzando quest'ultima variabile avrei un risultato catastrofico :muro:

Questo e' il famoso Buffer Overflow ed e' uno dei bachi piu' facili da generare e piu' difficili da trovare, anche xche' normalmente l'errore ti si presenta a km di distanza dalla parte di codice sbagliata....

Hieicker
06-02-2013, 19:40
Eccomi :) ! Scusate l'ultra mega ritardo ma ho avuto da fare. Periodo decisamente incasinato. Comunque...
Lo stato attuale della funzione è il seguente:

int confronta_stringa(char *stringa, char *stringa2){
int numero;
printf("quanti caratteri devono comporre ogni stringa?\n");
scanf("%d", &numero);
stringa = (char*) malloc(numero*sizeof(char));
stringa2 = (char*) malloc(numero*sizeof(char));
printf("Inserisci la prima stringa\n");
scanf("%s", stringa);
printf("inserisci la seconda stringa\n");
scanf("%s", stringa2);
if (!strcmp(stringa, stringa2))
printf("Le stringe da te inserite sono uguali");

free(stringa);
free(stringa2);
return 0;
}

Da quello che ho capito, usando la scanf non devo utilizzare il terminatore perchè lo aggiunge da sola giusto? Tuttavia devo controllare che non venga inserito una stringa più grande dello spazio allocato. Che tipo di controllo posso mettere? Come si autogestisce la macchina in questo caso?

Grazie :)