n° 154
Settembre 2010
Settembre 03, 2010, 04:22:25 am *
Benvenuto! Accedi o registrati.
Hai dimenticato l'e-mail di attivazione?

Accesso con nome utente, password e durata della sessione
Notizia: Usi il PC già da qualche anno? partecipa alla discussione nell'area Retrocomputing
 
   Indice   Linux Windows Internet videogame hardware Aiuto Ricerca Agenda Downloads Accedi Registrati  


 SONDAGGIO
Saresti interessato a seguire online dei videocorsi?
Sì | No
Se sì, quali di questi argomenti sarebbero di tuo gradimento?

.NET Framework   PHP
ASP.NET Python
Attività SEO Ruby
C# Silverlight
C/C++ Visual Basic .NET
Java Windows Azure
Java per Android Windows Phone 7
Objective-C (iPhone / iPad) Altri

Attenzione! Per partecipare al sondaggio bisogna essere registrati al forum




* Messaggi recenti
Messaggi recenti
Pagine: [1]   Vai giù
  Stampa  
Autore Discussione: Acquisire una stringa ed allocare dinamicamente lo spazio  (Letto 1175 volte)
0 utenti e 1 Utente non registrato stanno visualizzando questa discussione.
minghio
Newbie
*

Karma: +0/-0
Scollegato Scollegato

Messaggi: 12


Mostra profilo E-mail
« inserita:: Gennaio 30, 2009, 12:27:11 am »

Ciao a tutti.
Ho un piccolo dilemma riguardo a questa consegna:
"Acquisire un stringa senza dare limitazioni alla lunghezza della parola"

Conosco le funzioni malloc e calloc, ma le saprei utilizzare adeguatamente in questo caso, solo se conoscessi in anticipo il numero di caratteri della parola.
Come posso acquisire una parola ed allocare dinamicamente lo spazio per questa allora?
E' un errore assegnarla ad un puntatore e solo successivamente allocare lo spazio con malloc ?

Vi ringrazio. Buona notte.
Registrato
celeborn85
Global Moderator
Hero Member
*****

Karma: +42/-8
Scollegato Scollegato

Messaggi: 1752


Mostra profilo
« Risposta #1 inserita:: Gennaio 30, 2009, 02:58:39 am »

Puoi iniziare allocando una certa quantità di memoria e poi allocare blocchi di memoria sempre maggiori man mano che lo spazio non è più sufficiente. Puoi anche scorrere la stringa per calcolarne la lunghezza, allocare lo spazio necessario e infine acquisirla (che intendi dire esattamente con questo termine?).
Che cosa intendi con assegnarla ad un puntatore? Se la stringa è già stata allocata da altri allora sicuramente è il modo migliore, ma se con acquisire intendi dire leggerla ad esempio da file allora assegnarla ad un puntatore che non fa riferimento a una locazione di memoria valida è un grosso errore.
Registrato

I moderatori invitano tutti gli utenti a prendere visione del REGOLAMENTO e a rispettarlo.
minghio
Newbie
*

Karma: +0/-0
Scollegato Scollegato

Messaggi: 12


Mostra profilo E-mail
« Risposta #2 inserita:: Gennaio 30, 2009, 10:20:20 am »

Faccio un esempio per spiegarmi meglio.

char input[100];

scanf("%s", input);

In questo caso mi viene contestato di limitare la stringa in input a 100 caratteri. Come faccio ad allocarla dinamicamente? Per caso l'unica soluzione è allocare tutto la memoria dello heap e successivamente usare realloc quando la stringa è acquisita?
Registrato
celeborn85
Global Moderator
Hero Member
*****

Karma: +42/-8
Scollegato Scollegato

Messaggi: 1752


Mostra profilo
« Risposta #3 inserita:: Gennaio 30, 2009, 02:30:35 pm »

No, leggi un carattere per volta usando uno tra fgetc, getc, getchar e se raggiungi la fine della stringa la ridimensioni (o alloca una stringa nuova più grande di quella originale e copi il contenuto di quella precedente).
Registrato

I moderatori invitano tutti gli utenti a prendere visione del REGOLAMENTO e a rispettarlo.
minghio
Newbie
*

Karma: +0/-0
Scollegato Scollegato

Messaggi: 12


Mostra profilo E-mail
« Risposta #4 inserita:: Gennaio 30, 2009, 04:04:14 pm »

<quote>se raggiungi la fine della stringa la ridimensioni</quote>

Come faccio a sapere se ho raggiunto la fine della stringa?
Da quello che ho studiato il C non fa nessun controllo se scriviamo oltre la memoria allocata per una stringa e semplicemente va a scrivere in celle di memoria non allocate.
Potresti farmi un esempio di codice?

Registrato
M.A.W. 1968
** LEGGETE IL REGOLAMENTO ! **
Global Moderator
Hero Member
*****

Karma: +126/-3
Scollegato Scollegato

Messaggi: 2115


Discrete And Combinatorial Mathematics


Mostra profilo WWW
« Risposta #5 inserita:: Gennaio 30, 2009, 07:36:49 pm »

Come faccio a sapere se ho raggiunto la fine della stringa?

Ricade tutto integralmente sotto la tua responsabilità. Il paradigma fondante del linguaggio C è "il programmatore deve sapere ciò che fa".

Sia dunque, per fissare le idee, di la dimensione iniziale allocata per il buffer, una dimensione prudenziale: ad esempio, sia di = 128.
 
Si deve conteggiare ogni singolo carattere accettato, tramite un totalizzatore incrementato ad ogni input, che denominiamo ad esempio char_count: il controllo del superamento del limite preimpostato si ottiene quindi, banalmente, confrontando di con char_count.

Per tua cultura personale, sappi inoltre che:

- La gestione a decremento di char_count (inizializzato alla dimensione allocata e confrontato con lo zero) è prestazionalmente equivalente sui PC all'idioma del totalizzatore incrementale, ma risulta maggiormente efficiente su molte piattaforme esotiche.
Tuttavia, su quest'ultime sovente non esiste alcunché di assimilabile ad una interfaccia uomo-macchina tradizionale.
 
- La realloc() è un'operazione solitamente devastante per quanto attiene l'aspetto prestazionale di un'applicazione;

- Esiste una sintassi regexp Posix del sottosistema scanf(), supportata dalla vasta maggioranza dei compilatori C/C++, che consente di segregare l'input evitando pericolosi sconfinamenti, come nel seguente esempio che non accetta più di LENGTH caratteri nel buffer:
Codice:
#include <stdio.h>
#include <string.h>

#define LENGTH 4
#define BUFFER_SIZE (LENGTH + 1)

#define str(x) # x
#define xstr(x) str(x)

int main(void)
{
    char buff[BUFFER_SIZE];
    int rc;

    printf("\nSCANF : please enter text => \n");

    rc = scanf("%" xstr(LENGTH) "[^\n]%*[^\n]", buff);
   
    if (!feof(stdin))
    {
        getc(stdin);
    }
   
    if (rc == 0 || rc == EOF)
    {
        *buff = '\0';
    }

    printf("\nSCANF : length is %u\n", (unsigned)strlen(buff));
    printf("size is %u\n", (unsigned)sizeof(buff));
    printf("string is #%s#\n", buff);
    return 0;
}

Le "strane" macro str() e xstr() presenti servono per aggirare un noto limite sintattico, rendendo possibile l'uso di un simbolo di preprocessore nella stringa di formato.

Per chi necessita dei sottotitoli: nulla (o quasi) impedisce ovviamente di utilizzare direttamente la forma dinamica banale
Codice:
    char fmt_str[16];
    int max_len = LENGTH;
    ...
    sprintf(fmt_str, "%%%d[^\\n]%%*[^\\n]", max_len);
    scanf(fmt_str, buff);
...e ovviamente si può anche inserire scolasticamente la costante letterale entro la stringa di formato, ma le differenze tra i tre approcci sono ben evidenti.
Quello qui proposto nel primo esempio fissa la dimensione a compile time, pur mantenendo la flessibilità di una costante manifesta, il che lo rende immediatamente superiore all'approccio bovino del letterale; il secondo metodo, proposto immediatamente sopra questo paragrafo, è dinamico e avviene a runtime, con una (sia pur minima) penalità prestazionale e con inerenti rischi (sia pur remoti) di avvelenamento/iniezione di stack o heap - mentre nel primo caso la stringa di formato viene trattata come costante da qualsiasi compilatore minimamente decente, e quindi gestita in apposito segmento di memoria logicamente protetto su x86 e affini.


EDIT: Aggiunti alcuni utili link su segnalazione del buon Bonzo, che ringrazio. Ghigno
Registrato

I Moderatori invitano tutti gli utenti a prendere visione del REGOLAMENTO e a rispettarlo.

Un blog? Io?
minghio
Newbie
*

Karma: +0/-0
Scollegato Scollegato

Messaggi: 12


Mostra profilo E-mail
« Risposta #6 inserita:: Gennaio 31, 2009, 02:03:02 pm »

Ti ringrazio per avermi fatto conoscere le macro.

Vi posto un esempio di codice dove penso sia chiaro quello che provo a fare.
In pratica alloco memoria per un carattere alla volta: è una cosa che si può fare?
Se si, potreste aiutarmi a capire cosa sbaglio? Nell'esecuzione del programma ricevo un segmentation fault..

Codice:
#include <stdio.h>
#include <stdlib.h>

/*Prototipi di funzione*/
void acquisisci_stringa(char * );

int main (void)
{
   char * parola;

   parola = NULL;

   acquisisci_stringa(parola);

   printf("\n%s\n", parola);

   return 0;
} /*Fine del main*/


void acquisisci_stringa(char * input )
{

   /*DICHIARAZIONE DELLE VARIABILI LOCALI*/
   char a;
   int j=0;

   printf("\nDigita la parola da elaborare\n");
   
   while( ( a = getchar() ) != '\n'  ){
      /*ALLOCO MEMORIA PER UN CARATTERE ALLA VOLTA*/
      input = (char *) malloc( sizeof(char) );

      input[0] = a;
      ++input;
      ++j;
   }

   input = malloc( sizeof(char) );
   input[0] = '\0';
}
Registrato
M.A.W. 1968
** LEGGETE IL REGOLAMENTO ! **
Global Moderator
Hero Member
*****

Karma: +126/-3
Scollegato Scollegato

Messaggi: 2115


Discrete And Combinatorial Mathematics


Mostra profilo WWW
« Risposta #7 inserita:: Gennaio 31, 2009, 03:38:31 pm »

Il tuo esempio contiene alcuni significativi errori.

In considerazione del tuo impegno, suggerisco una bozza di soluzione in ambiente windows, concettualmente inelegante ma di indubbio valore didattico.
Deve compilare senza errori con Borland C/C++ 5.5.1.

Codice:
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <stdlib.h>

#define INITIAL_SIZE 16

void acquisisci_stringa(char *in_str)
{
    char c;
    int pos = 0;
    int buf_size;
   
    /* Preallocazione iniziale */
    in_str = (char *)malloc(sizeof(char) * (INITIAL_SIZE +1));
    buf_size = INITIAL_SIZE;
   
    if (NULL == in_str)
    {
        fputs("Errore di allocazione", stderr);
        return;
    }
   
    while ((c = (char)getch()) != '\r')
    {
        printf("{%c (%02X) %02d %02d}\n", c, c, pos, buf_size);
        in_str[pos++] = c;
        if (pos == buf_size)
        {
            /* Soluzione didatticamente valida ma prestazionalmente inefficiente */
            ++buf_size;
            in_str = (char *)realloc(in_str, sizeof(char) * buf_size);
            if (NULL == in_str)
            {
                fputs("Errore di reallocazione", stderr);
                return;
            }
        }
    }
    in_str[pos] = '\0';
}

int main(void)
{
    char *parola;
    acquisisci_stringa(parola);

    if (NULL != parola)
    {
        printf("\n%s\n", parola);
        free(parola);
    }
   
    return (0);
}

In una applicazione realistica, si avrà invece un buffer (statico) nello heap, di dimensioni esorbitanti rispetto ad un banale input interattivo (es. 1 Mb...) nel quale vengono di volta in volta immesse stringhe temporanee, preferibilmente con il metodo della limitazione del numero di caratteri esemplificato nel mio post precedente. Tali stringhe, una volta "sanificate" quanto a dimensione e contenuto, saranno poi debitamente copiate nelle loro rispettive destinazioni. Sorriso
Registrato

I Moderatori invitano tutti gli utenti a prendere visione del REGOLAMENTO e a rispettarlo.

Un blog? Io?
minghio
Newbie
*

Karma: +0/-0
Scollegato Scollegato

Messaggi: 12


Mostra profilo E-mail
« Risposta #8 inserita:: Gennaio 31, 2009, 06:12:51 pm »

Posto di seguito il mio codice modificato dopo aver visto il suo esempio.
I vari controlli per verificare se un puntatore è a NULL dopo l'allocazione di memoria li inserirò successivamente.
Codice:
#include <stdio.h>
#include <stdlib.h>
#define SIZE 10

/*Prototipi di funzione*/
void acquisisci_stringa(char * );

int main (void)
{
   char * parola;

   parola = malloc( sizeof(char)* SIZE );

   acquisisci_stringa(parola);

   printf("\n%s\n", parola);

   return 0;
}/*FINE DEL MAIN*/


void acquisisci_stringa(char * input )
{

   /*DICHIARAZIONE DELLE VARIABILI LOCALI*/
   char a;
   int j = 0;
   int buffer_size = SIZE;
 
   
   while( ( a = getchar() ) != '\n'  ){

      input[j++] = a;

      if (j==buffer_size){

         input = (char *) realloc(input, sizeof(char)*(++buffer_size) );
      }
   }

   input[j] = '\0';
 
}/*FINE DI acquisisci_stringa*/

Ora il programma sembra funzionare correttamente.
Il puntatore parola è stato inizializzato con malloc nel main.
Avrei preferito fare questa inizializzazione nella funzione acquisisci_stringa utilizzando il parametro formale, ma in questo modo il programma non funziona correttamente: ritorna dei caratteri strani al posto della stringa..
Come mai? Se passo ad una funzione un parametro per indirizzo e poi lo inizializzo con malloc, sto agendo sulla variabile contenuta nel main?

La ringrazio di nuovo.
Registrato
M.A.W. 1968
** LEGGETE IL REGOLAMENTO ! **
Global Moderator
Hero Member
*****

Karma: +126/-3
Scollegato Scollegato

Messaggi: 2115


Discrete And Combinatorial Mathematics


Mostra profilo WWW
« Risposta #9 inserita:: Gennaio 31, 2009, 08:49:47 pm »

Il sorgente da me proposto lasciava volutamente inalterate (sebbene segregate dai controlli) una o due innocue "trappole" Ghigno dalla versione iniziale, per stimolare ulteriori domande.

Come hai facilmente intuito, il passaggio di un puntatore come parametro non consente, infatti, che il suo effettivo contenuto (ovvero l'indirizzo della locazione di memoria alla quale esso "punta") venga modificato entro la funzione chiamata.

I due linguaggi C e C++ utilizzano varie strategie per affrontare questa limitazione, dovuta al fatto che la funzione chiamata riceve in effetti una "copia" ovvero un secondo puntatore il cui contenuto viene copiato dal puntatore originale: tale copia viene poi "distrutta" al termine della funzione chiamata (scope locale, ossia automatic).

Gli idiomi più diffusi sono:
- La funzione chiamata restituisce un puntatore, nel quale - se necessario - viene inizialmente copiato il contenuto del puntatore del medesimo tipo che ha ricevuto come parametro. Soluzione scolastica, impiegata nelle (poche) funzioni della libreria standard orientate al trattamento delle stringhe, ma poco elegante e sconsigliata dalle norme di stile (è più opportuno che le funzioni utente restituiscano valori booleani o piccoli interi che ne indichino eventuali codici di errore, o il successo).

- Si utilizza un puntatore ad un puntatore:
Codice:
void acquisisci_stringa(char **);

int main (void)
{
    char *parola;
    ...
    acquisisci_stringa(&parola);
}

void acquisisci_stringa(char **inbuf)
{
    char *aux;
    ...
    aux = malloc(...);
    ...
    aux[pos] = '\0';
    *inbuf = aux;
}

- In C++, si può infine utilizzare il passaggio del parametro per riferimento.


PS: Come già abbiamo considerato varie volte, in Rete - specialmente nei forum e nelle ML - tradizionalmente si dà del tu a chiunque, anche a Donald E. Knuth o Dana Scott. Questo sia perché lo esige il "webglish", sia per lo spirito di equanimità e profonda democrazia liberale che pervade la rete fin dai suoi esordi "sociali" in ambito accademico ed amatoriale.

Nella lingua inglese non esistono formalmente il "lei" o il "voi", ma solo appellativi rispettosi e titoli come ad esempio sir - madam - doctor - Professor - Milady - Lord - President, e anche questi valgono solo in ristretti ambienti e circostanze: è ancora (parzialmente) vero che taluni cittadini britannici e delle relative colonie, specialmente quelli appartenenti alla upper class ed alle generazioni più mature, sono quasi sempre formalisti come vengono acriticamente dipinti nei manuali d'inglese scolastici (fino all'algida ipocrisia sbertucciata da tante barzellette e parodie), ma la schiacciante maggioranza di quel miliardo circa di terrestri parlanti una qualche approssimazione della lingua inglese ha tutt'altro background socioculturale, a partire da yankees, rednecks, aussies...  Ghigno
Registrato

I Moderatori invitano tutti gli utenti a prendere visione del REGOLAMENTO e a rispettarlo.

Un blog? Io?
minghio
Newbie
*

Karma: +0/-0
Scollegato Scollegato

Messaggi: 12


Mostra profilo E-mail
« Risposta #10 inserita:: Febbraio 01, 2009, 03:13:23 pm »

Wow questa cosa di utlizzare un puntatore ad un puntatore ancora non l'avevo vista!
Sono ancora un principiante, ma mi sto impegnando..

Cerco di correggere il mio programma con questa nuova nozione.
Ti ringrazio Sorriso .
Registrato
minghio
Newbie
*

Karma: +0/-0
Scollegato Scollegato

Messaggi: 12


Mostra profilo E-mail
« Risposta #11 inserita:: Febbraio 05, 2009, 05:55:42 pm »

Andando avanti con lo studio mi è venuta un'altra idea. Non per scartare i discorsi fatti in precedenza che mi sono stati molto utili ma ve la propongo

int main(int argc,char *argv[])

Utilzzando i parametri formali della funzione main potrei acquisire la stringa con l'esecuzione del programma e dopo potrei trattare argv come un puntatore delle dimensioni esatte per contenere la stringa?
E' un errore utilizzare realloc per aumentare/diminuire lo spazio allocato per le stringhe i cui indirizzi di memora sono puntati dagli elementi del puntatore argv?
Registrato
M.A.W. 1968
** LEGGETE IL REGOLAMENTO ! **
Global Moderator
Hero Member
*****

Karma: +126/-3
Scollegato Scollegato

Messaggi: 2115


Discrete And Combinatorial Mathematics


Mostra profilo WWW
« Risposta #12 inserita:: Febbraio 05, 2009, 07:24:52 pm »

Utilzzando i parametri formali della funzione main potrei acquisire la stringa con l'esecuzione del programma e dopo potrei trattare argv come un puntatore delle dimensioni esatte per contenere la stringa?
E' un errore utilizzare realloc per aumentare/diminuire lo spazio allocato per le stringhe i cui indirizzi di memora sono puntati dagli elementi del puntatore argv?

Sarebbe un errore madornale. Il passaggio dei parametri, a carico del Sistema Operativo o environment alternativo e gestito in background da codice pre-main generato dal compilatore (avente i files di startup c0x come parte del processo), non è operazione influenzabile a livello di normale programmazione.
Registrato

I Moderatori invitano tutti gli utenti a prendere visione del REGOLAMENTO e a rispettarlo.

Un blog? Io?
Pagine: [1]   Vai su
  Stampa  
 
Vai a:  

Copyright © 2009 Edizioni Master SpA. p.iva : 02105820787

Tutti i diritti di proprietà letteraria e artistica riservati. - Privacy



Links to Page