Concatenare stringhe

Forse sono OT, ma ci provo

Buonasera,
faccio qualche esercizio con le stringhe, visto che quello che per me solitamente è banale, in C diventa complicato :confused:

Aggiungere un filler ad una stringa, in pratica concatenare due stringhe

Questo è quello che ho scritto, e che sembra funzionare

char text[] = "pippo";
byte len = strlen(text);
byte num = 75; 
char filler[num+1];
memset(filler, '.', sizeof(filler));
filler[num] = '\0';
char temp[len+num+1];
strcpy(temp, text);
strcat(temp, filler);
  1. per riempire una stringa con un carattere, memset è la scelta corretta, o ad esempio è possibile riempirla già in fase di definizione?
  2. per la concatenazione, quello che ho scritto, è l'approccio corretto?

inoltre, avevo provato questo

  char text[] = "pippo";
  byte len = strlen(text);
  byte num = 8; 
  memset(text + len, '.', num);
  text[len + num] = '\0';

essendo text in pratica una costante, mi sarei aspettato che non funzionasse, o meglio, mi sarei aspettato qualche problema, visto che vado ad aggiungere bytes utilizzando un puntatore (spero di non aver detto una castronata), al contrario funziona fino a che num è <= 8, dopo di che il disastro!
Perchè proprio 8 bytes?

Scusate se le domande possono sembrare stupide, ma per me normalmente concatenare stringhe si risolve con una riga di codice e senza creare nuove variabili!

TIA
Federico

Federico66:
Forse sono OT, ma ci provo

Buonasera,

inoltre, avevo provato questo

  char text[] = "pippo";

byte len = strlen(text);
  byte num = 8;
  memset(text + len, '.', num);
  text[len + num] = '\0';





Perchè proprio 8 bytes?


Federico

non sono proprio 8

sono "casualmente" 8

tu non hai dichiarato una variabile array di dimensione sufficente, nemmeno hai riservato memoria con una malloc

pertanto quando vai con l'aritmetica dei puntatori oltre la dimensione di text può succedere di tutto, perché vai a "sporcare" la memoria

ecco a te succede quello che ti succede
magari a me, con differenti opzioni di compilazione succede altro

scusate, ma il sistema di log-out funziona?

il post precedente lo ho scritto io, dopo aver sloggato (e sloggiato) Fabio dal mio PC e dal mio laboratorio

perché il post adesso appare scritto da lui?

vabe' dai,

problema minore tanto siamo fratelli e condividiamo tutto....
ma giusto per correttezza sappiate da che sacco viene la farina

Ducembarr:
sono "casualmente" 8

tu non hai dichiarato una variabile array di dimensione sufficente, nemmeno hai riservato memoria con una malloc

Sulla casualità ho forti dubbi, anche se dipendesse dal compilatore, non sarebbe casuale ma ben motivato.

La memset scrive direttamente sulla memoria partendo dall'indirizzo puntato da text, che non è l'array, quindi non dovrebbero esserci limiti, se non quelli della memoria.

Al contrario quello che non potrei fare è assegnare il nul oltre il limite dell'array (6bytes) , ed è questo quello che non riesco a capire; perché viene generato l'errore solo se vado oltre di 8bytes?

Vista l'ora, ci penserò domani... sul tardi :wink:

Federico

Ti sembra strano ma è proprio casuale :slight_smile:
Il C non da nessun tipo di errore se scrivi sulla memoria non riservata all'array, ma appunto non è riservata può essere usata per altri scopi dal programma.
Se scrivo sulla memoria e questa è usata dal programma possono avvenire diverse cose, se la memoria contiene dati vado a corrompere i dati, se la memoria contiene istruzioni il programma non funziona.
Se fai la stessa cosa in programmi diversi, quindi con un uso diverso della memoria, vedrai che non ti da sempre errore a 8 byte superiori alla dimensione dell'array, ma è casuale.

>Federico66: la memset() (... che fa parte di AVR libc) altro non è che un veloce ciclo di for ... tu gli dai il primo elemento (il nome dell'array è un puntatore al primo elemento) e gli dici quanti elementy (bytes) deve riempire, ne più ne meno. Se fai qualche ricerca ricordo che con SukkoPera ci siamo divertiti a sviscerarla ...
... se tu vuoi divertirti a leggerla, te l'allego :smiley:

Detto questo, se tu hai prima un array, poi altre variabili, il tutto è messo contiguo e, come nel caso del FOR, se sbagli il numero di elementi, vai a scrivere su quello che segue, così con memset() se sbagli il numero di elementi vai a scrivere su aree si SRAM che magari erano state allocate per altre cose ... con conseguenze casuali e disastrose :wink:

Guglielmo

P.S.: Discorso simile si può fare per la strcpy(), difatti, per evitare problemi, si consiglia sempre di usare la strncpy() a cui va passato anche il limite di lunghezza della destinazione, così, se l'array è troppo corto, almeno non va a sporcare tutto quello che segue. :wink:

memset.zip (1.48 KB)

torn24:
... Se scrivo sulla memoria e questa è usata dal programma possono avvenire diverse cose, se la memoria contiene dati vado a corrompere i dati, se la memoria contiene istruzioni il programma non funziona.

... fortunatamente la scrittura nell'area del "programma" (la Flash) NON è così semplice come la scrittura sulla SRAM e quindi ... il caso che citi è molto più complesso :wink:

Guglielmo

... e comunque, su Arduino, dove la SRAM è quella che è, ma perché vi mettere ad usare funzioni di questo tipo, con tutti i rischi del caso, e non usate un semplice FOR? Non crederete veramente che il risparmio di tempo o di memoria sia veramente apprezzabile vero? :o

Salvo non dobbiate muovere MBytes, cosa che vedo piuttosto difficile su MCU che hanno al massimo qualche KB di SRAM, usate i cicli classici del 'C' che tanto ... quello che genera il compilatore è più che compatto ed ha la stessa velocità :wink:

Guglielmo

P.S.: Quelle funzioni nascono per sistemi Linux, con, oggi, GB di RAM e necessità di muovere enormi blocchi di memoria, ma su queste MCU ... ::slight_smile:

Federico66:
... perché viene generato l'errore solo se vado oltre di 8bytes?

Federico

giusto per curiosità, che errore è?

perché ci ho pensato, ma semmai dovrebbe essere un malfunzionamento a run-time, non un errore a compile-time

ma come te, ci ho pensato ieri sera sul tardi... magari non ero lucido

Buongiorno,
non ho più l'età per una doppia pizza serale, anche se ottima e 'digeribilissima' :frowning:

Acquisiti 3/3 pareri, non mi resta che accettare la casualità come dogma, quindi capitolo chiuso :wink:

Salvorhardin:
... semmai dovrebbe essere un malfunzionamento a run-time...

si, scusa, si tratta di malfunzionamento.

gpb01:
la memset() (... che fa parte di AVR libc) altro non è che un veloce ciclo di for ...

torn24:
Il C non da nessun tipo di errore se scrivi sulla memoria non riservata all'array...

chiarissimi come sempre, ma ho usato memset con cognizione di causa, e forse mi sono spiegato male :frowning:

Quello che non mi va giù (o forse non capisco) è perchè il compilatore non mi segnali che sto utilizzando un array oltre la dimensione dichiarata, cioè questa non genera alcun warning, a meno che non dichiari l'array costante:

byte num = 4;
char text[7] = "012345";
byte len = strlen(text);
for (uint8_t i = len; i < len+num; i++) 
  text[i] = '.';
text[len+num] = '\0';
Serial.println(text); //=> 012345....

tralasciando il fatto che sto scrivendo in un'area non riservata e che questa cosa non si dovrebbe fare,
in pratica, a tutti gli effetti ne sto modificando la dimensione, senza utilizzare i puntatori (o forse si!).

Per curiosità, ho fatto la stessa cosa in Visual C++, e, succede esattamente quello che mi aspetto

Si è verificato un sovraccarico del buffer in test01.exe che ha danneggiato lo stato interno del programma.

Scusate la mia testardaggine e grazie per la pazienza

Federico

PS
Memore di una leggerezza passata, l'IDE è sempre configurato a ricevere tutti i warning del compilatore :wink:

In C standard NON c'e' nessun controllo di eventuale scrittura oltre il limite di un vettore.
Nel caso che segnali del VC l'errore è dato in esecuzione dell'exe, probabilmente è il sistema operativo windows che controlla, ma su Arduino non c'e' S.O. (oppure il VC inserisce del codice di controllo)
Il C è un linguaggio "base", pensato per sostituire il linguaggio assembly. Usato con pezzi in assembly per scrivere sistemi operativi ad esempio. Quindi non deve fare controlli, sarà il programmatore che deciderà eventualmente se implementare dei controlli, ad esempio se scrive un sistema operativo.
Il linguaggio deve essere per sua natura base ovvero "spartano"

nid69ita:
In C standard NON c'e' nessun controllo di eventuale scrittura oltre il limite di un vettore.

Perfetto, questo spiega tutto.
Uso abitualmente linguaggi "alti", dove naturalmente questo non è possibile, e faccio ancora fatica ad "accettare" certi non limiti :slight_smile:

Se hai letto tutto il thread, avrai capito che questo non è un vero problema, ma solo un modo per capire e studiare alcuni aspetti del linguaggio.

Grazie
Federico

... aggiungo che Arduino è C++ ... il problema è che, come ha già indicato Nid, NON c'è il sistema oprativo che se ne accorge e segnala errore ... il sistema operativo sei TU che scrivi il programma, se sbagli ... affari tuoi :smiley:

Guglielmo

per Federico66:
scusa se sono mancato, volevo rispondere già ieri, ma.... <-- metti qui la tua supercazzola preferita
insomma non ho giustificazioni, se non che non ci ho pensato
il 'C' non ha alcun controllo se sbordi dai limiti di un array sia verso l'alto che verso il basso
se poi lo fai tramite puntatori non ti dico, una goduria, un piedigrotta di possibili casini
inoltre da poco (C11 o forse dal futuro C20, non ricordo se è già adottato o in discussione) oltre a non avere i controli che si diceva non sarà nemmeno più garantito che l'aritmetica dei puntatori "conti giusto" oltre il primo elemento fuori dall'array

cioè:

byte array[10];
..........
array[0] // riferisce al primo elemento
array[9] // riferisce all'ultimo
array[10] // riferisce (è garantito) all'indirizzo dell'ipotetico elemento successivo all'utimo, senza dare errori formali ma incasinando discretamente la memoria
array[12] // non sarà più garantito che riferisca all'ipotetico elemento successivo al successivo dell'ultimo, potrebbe riferire ovunque, questo non migliora la situazione, anzi... 
array[-1] // riferisce all'ipotetico elemento precedente il primo
array[-2] // non sarà più garantito che riferisca all'ipotetico elemento precedente del precedente del primo, potrebbe riferire ovunque

quindi vedi che andare a scrivere (peggio poi coi puntatori) oltre la fine dell'array potrebbe incatastare la memoria, oppure no (se per puro colpo di.... dopo l'array hai solo memoria libera)

e prima che tu lo chieda:
NO: la memset non alloca memoria, quindi non puoi "fidarti" che usi la memoria "giusta"

quanto poi al fatto che non credi sia casuale ma come dici tu "non casuale ma ben motivato"
hai pienamente ragione, si tratta pur sempre di una macchina deterministica
ma da qui a poterne prevedere il funzionamento ne passa ... a causa delle troppe variabili in gioco
in linea puramente tecnica è una cosa deterministica
all'atto pratico per noi umani quello che succede, succede solo per caso

gpb01:
... il sistema operativo sei TU ...

finora nessuno mi aveva ancora del sistema operativo ::slight_smile:
ma non so se considerarlo complimento, magari dipende da quale... :smiley: :smiley: :smiley:

Federico

Standardoil:
scusa se sono mancato, volevo rispondere già ieri, ma....

in realtà ero un po' deluso dalla tua assenza in questo thread... :wink:

Standardoil:
e prima che tu lo chieda:
NO: la memset non alloca memoria, quindi non puoi "fidarti" che usi la memoria "giusta"

non l'avrei chiesto, questo mi era chiaro da un pezzo, e immagino sia lo stesso per altre funzioni del gruppo memxxx

Standardoil:
quanto poi al fatto che non credi sia casuale ma come dici tu "non casuale ma ben motivato"
hai pienamente ragione, si tratta pur sempre di una macchina deterministica
ma da qui a poterne prevedere il funzionamento ne passa ...

adesso mi è più chiaro, in realtà ho fatto esperimenti per capire da solo che è impossibile prevederlo...
il mio Nano clone sta quasi fumando per come l'ho trattato :slight_smile:

A parte tutto, comunque tutto questo maneggiamento della memoria, mi è servito a capire meglio l'uso dei puntatori e della loro aritmetica. Finora li avevo usati solo per passare argomenti (byref) ai metodi.

Devo dire che in questi mesi di Arduino e C/C++ mi sono divertito molto, come avere un giocattolo nuovo, e spero di continuare a divertirmi :smiley:

Grazie
Federico

A parte le corrette considerazioni "accademiche" già fatte da altri, per tornare al tuo problema iniziale ossia "aggiungere un filler ad una stringa", diciamo che in genere un "filler" servirebbe per aggiungere un certo carattere (all'inizio o alla fine) per fare in modo che una certa stringa abbia una lunghezza totale predefinita. Ad esempio tu hai "pippo" e vuoi che diventi una stringa di esattamente 20 caratteri, nel tuo caso hai messo come esempio un punto: "pippo..............." ad esempio perché in un file devi avere i campi tutti di lunghezza fissa.
Il codice che hai postato però aggiunge (almeno apparentemente) un numero FISSO di caratteri alla stringa originale.

Per realizzare invece un vero filler, intanto in genere si prealloca la stringa con il numero corretto di caratteri, sia per averne sempre contezza, sia per maggiore linearità di gestione (non devi fare allocazioni o deallocazioni dinamiche che in una MCU piccolina è sempre rischioso...).
Ad esempio non:

char test[] = "pippo";

ma, prendendo l'esempio dei 20 caratteri:

char test[21] = "pippo";

Detto questo, una funzione filler() diventa semplice:

void filler(char *str, int totale, char c)
{
  for (int i=strlen(str); i<totale; ++i)
    str[i] = c;
  str[totale] = '\0';
}

oppure:

void filler(char *str, int totale, char c)
{
  int i=strlen(str);
  memset(str+i, '.', totale-i);
  str[totale] = '\0';
}

Esempio:

  char testo[21] = "pippo";
  Serial.print("\"");
  Serial.print(testo);
  Serial.println("\"");
  filler(testo, 20, '.');
  Serial.print("\"");
  Serial.print(testo);
  Serial.println("\"");

docdoc:
"aggiungere un filler ad una stringa", diciamo che in genere un "filler" servirebbe per aggiungere un certo carattere (all'inizio o alla fine) per fare in modo che una certa stringa abbia una lunghezza totale predefinita.

Hai ragione, ho usato il termine impropriamente, in realtà stavo pensando ad entrambe le cose.
In ogni caso, grazie

Federico