Lettura di stringhe con lunghezza indefinita in C

prima (come sempre) voglio scusarmi per il mio inglese, potrebbe non essere abbastanza chiaro.

Non sono così bravo in programmazione C, e mi è stato chiesto di leggere un input “stringa” con una lunghezza indefinita.

Questa è la mia soluzione

#include  #include  #include  char *newChar(); char *addChar(char *, char); char *readLine(void); int main() { char *palabra; palabra = newChar(); palabra = readLine(); printf("palabra=%s\n", palabra); return 0; } char *newChar() { char *list = (char *) malloc(0 * sizeof (char)); *list = '\0'; return list; } char *addChar(char *lst, char num) { int largo = strlen(lst) + 1; realloc(&lst, largo * sizeof (char)); *(lst + (largo - 1)) = num; *(lst + largo) = '\0'; return lst; } char *readLine() { char c; char *palabra = newChar(); c = getchar(); while (c != '\n') { if (c != '\n') { palabra = addChar(palabra, c); } c = getchar(); } return palabra; } 

Per favore, mi farebbe piacere che tu mi aiuti dicendomi se è una buona idea o dandomi qualche altra idea (e anche dicendomi se è un uso “corretto” per i puntatori).

Grazie in anticipo


EDIT: Beh, grazie per le risposte, sono stati molto utili. Ora inserisco il codice modificato (e spero meglio), forse potrebbe essere utile per qualcuno nuovo a C (come me) e ricevere un feedback di nuovo.

 #include  #include  #include  void reChar(char **, int *); void readLine(char **, int *); int main() { char *palabra = NULL; int largo = 0; reChar(&palabra, &largo); readLine(&palabra, &largo); printf("palabra=%s\n", palabra, largo); system("pause"); return 0; } void reChar(char **lst, int *largo) { (*largo) += 4; char *temp = (char*) realloc(*lst, (*largo) * sizeof (char)); if (temp != NULL) { *lst = temp; } else { free(*lst); puts("error (re)allocating memory"); exit(1); } } void readLine(char **lst, int *largo) { int c; int pos = 0; c = getchar(); while (c != '\n' && c != EOF) { if ((pos + 1) % 4 == 0) { reChar(lst, largo); } (*lst)[pos] =(char) c; pos++; c = getchar(); } (*lst)[pos] = '\0'; } 

PS:

  • Sembra abbastanza lento aumentare la dimensione della “palabra”.

  • Non sono sicuro che l’acquisizione di getchar() in un int e quindi il cast in un char sia il modo corretto di sopportare la trappola dell’EOF

  1. Cerca la definizione di POSIX getline() .

  2. Ricorda che è necessario acquisire il valore di ritorno da realloc() ; non è garantito che il nuovo blocco di memoria inizi nella stessa posizione di quello vecchio.

  3. Sapere che malloc(0) può restituire un puntatore nullo, oppure può restituire un puntatore non nullo che è inutilizzabile (perché punta a zero byte di memoria).

  4. Non puoi scrivere ‘ *list = '\0'; quando l’elenco punta a zero byte di memoria allocata; non hai il permesso di scrivere lì. Se ottieni un NULL, probabilmente otterrai un core dump. In ogni caso, stai invocando un comportamento indefinito, che è ‘ A Bad Idea ™’. ( Grazie )

  5. Il palabra = newChar(); in main() perde memoria – assumendo che si risolvano gli altri problemi già discussi.

  6. Il codice in readLine() non considera la possibilità di ottenere EOF prima di ottenere una nuova riga; questo è male e risulterà in un core dump quando l’allocazione della memoria (finalmente) fallisce.

  7. Il tuo codice mostrerà scarso rendimento perché assegna un personaggio alla volta. In genere, è necessario allocare più di un carattere in più alla volta; iniziando con una allocazione iniziale di forse 4 byte e raddoppiando l’allocazione ogni volta che hai bisogno di più spazio potrebbe essere meglio. Mantenere l’allocazione iniziale piccola in modo che il codice di riallocazione sia testato correttamente.

  8. Il valore restituito da getchar() è un int , non un char . Sulla maggior parte delle macchine, può restituire 256 diversi valori di carattere positivo (anche se char è un tipo firmato) e un valore separato, EOF, che è diverso da tutti i valori char . (Lo standard consente di restituire più di 256 caratteri diversi se la macchina ha byte più grandi di 8 bit ciascuno.) ( Grazie ) Lo standard C99 §7.19.7.1 dice di fgetc() :

    Se l’indicatore di fine file per il stream di input puntato da stream non è impostato e un carattere successivo è presente, la funzione fgetc ottiene quel carattere come un char senza segno convertito in un int e fa avanzare l’indicatore di posizione file associato per lo stream (se definito).

    (Enfasi aggiunta.) Definisce getchar() in termini di getc() e definisce getc() in termini di fgetc() .

  9. (Preso in prestito: grazie ). Il primo argomento di realloc() è il puntatore all’inizio della memoria attualmente allocata, non un puntatore al puntatore all’inizio della memoria attualmente allocata. Se non hai ricevuto un avviso di compilazione da esso, non stai compilando con abbastanza avvisi impostati sul tuo compilatore. Dovresti alzare gli avvertimenti al massimo. Dovresti prestare attenzione agli avvertimenti del compilatore – sono normalmente indicativi di errori nel codice, specialmente mentre stai ancora imparando la lingua.

  10. Spesso è più semplice mantenere la stringa senza un terminatore nullo finché non si è sicuri di aver raggiunto la fine della riga (o la fine dell’input). Quando non ci sono più caratteri da leggere (per il momento), quindi aggiungere il null in modo che la stringa sia terminata correttamente prima che venga restituita. Queste funzioni non hanno bisogno che la stringa termini correttamente mentre stanno leggendo, a patto che tu tenga traccia di dove ti trovi nella stringa. Assicurati di avere abbastanza spazio in ogni momento per aggiungere il NUL '\0' alla fine della stringa, però.

Vedi Kernighan & Pike ‘The Practice of Programming’ per molte discussioni pertinenti. Penso anche che Maguire ‘Writing Solid Code’ abbia un consiglio pertinente da offrire, per quanto sia un po ‘datato. Tuttavia, dovresti sapere che ci sono quelli che escoriare il libro. Di conseguenza, raccomando TPOP su WSC (ma Amazon ha WSC disponibile da $ 0,01 + p & p, mentre TPOP parte da $ 20,00 + p & p – questo potrebbe essere il discorso del mercato).


In precedenza, TPOP era su http://plan9.bell-labs.com/cm/cs/tpop e http://cm.bell-labs.com/cm/cs/tpop, ma entrambi sono ora (2015-08-10) rotto. Vedi anche Wikipedia su TPOP .

  • Stai allocando sempre meno un byte di quello che stai usando. Ad esempio all’inizio si assegna spazio per zero caratteri e quindi si prova a impostare il primo carattere (non esistente) su '\0' .

  • realloc non realloc un puntatore a un puntatore come primo parametro. Dovrebbe essere usato in questo modo:

     lst = realloc(lst, largo * sizeof (char)); 
  • Se si desidera gestire le condizioni di esaurimento della memoria, è necessario verificare se malloc() o realloc() restituito NULL.

  • Sarebbe più efficiente allocare un buffer più grande all’inizio e aumentarlo in passi più grandi invece di riallocare separatamente ogni carattere aggiunto.

Il primo argomento alla chiamata a realloc in

 realloc(&lst, largo * sizeof (char)); 

dovrebbe essere lst e non &lst

Anche il puntatore restituito da realloc non deve essere sempre uguale al suo primo argomento. Se non viene trovata alcuna memoria libera adiacente alla memoria esistente, viene assegnato un chunk completamente diverso e viene restituito il suo indirizzo.

 char *new_lst = realloc(lst, largo * sizeof (char)); if(new_lst != NULL) { lst = new_lst; } 

A parte gli errori nel tuo codice, penso che sia meglio creare una stringa di lunghezza variabile in C. Una volta che hai, puoi scrivere una funzione getLine (). Questa stringa di lunghezza variabile include il concetto di capacità, quindi le sue dimensioni aumentano in blocchi di poteri di 2, invece uno per uno.

 #include  #include  typedef struct _mystring { char * native; size_t size; size_t capacity; } String; size_t String__len(String this) { return this.size; } String String__create(char native[], size_t capacity) { String this; this.size = strlen( native ); if ( capacity < ( this.size + 1 ) ) this.capacity = this.size + 1; else this.capacity = capacity; this.native = (char *) malloc( capacity * sizeof( char ) ); strcpy( this.native, native ); return this; } String * String__set(String *this, char native[]) { this->size = strlen( native ); if ( this->size >= this->capacity ) { do { this->capacity <<= 1; } while( this->size > this->capacity ); this->native = realloc( this->native, this->capacity ); } strcpy( this->native, native ); return this; } String * String__add(String *this, char ch) { ++( this->size ); if ( this->size >= this->capacity ) { do { this->capacity <<= 1; } while( this->size > this->capacity ); this->native = realloc( this->native, this->capacity ); } char * zeroPos = this->native + ( this->size -1 ); *( zeroPos++ ) = ch; *zeroPos = 0; return this; } void String__delete(String *this) { free( this->native ); } 

Una volta completata questa implementazione, utile per questo problema e molti altri, è ansible creare la funzione getLine:

 String String__getLine() { int ch; String this = String__create( "", 16 ); do { ch = fgetc( stdin ); String__add( &this, ch ); } while( ch != EOF && ch != '\n' ); size_t len = String__len( this ); this.size = len -1; *( this.native + this.size ) = 0; return this; } 

Ora puoi semplicemente usarlo:

 int main() { printf( "Enter string: " ); String str = String__getLine(); printf( "You entered: '%s'\n", str.native ); String__delete( &str ); return EXIT_SUCCESS; } 

Ecco un esempio funzionante per realloc e fgets. La sua C89 non aveva bisogno di POSIX. È ansible impostare il parametro con la propria memoria preallocato o NULL. È sempre necessario terminare “gratuitamente”.

 #include  #include  #include  char *getstringStdin(char *s) { char buffer[9]; s=realloc(s,1); *s=0; while( fgets(buffer,9,stdin) ) { s=realloc(s,strlen(s)+1+strlen(buffer)); strcat(s,buffer); if( strchr(s,'\n') ) { *strchr(s,'\n')=0; break; } } return s; } main() { char *s; while( *(s=getstringStdin(0)) ) /* a single Enter breaks */ { puts(s); free(s); } free(s); puts("end"); return 0; }