Domanda Risolto Rimediare ad una pessima allocazione di un Array di Char [C++]

DanyDollaro

Utente Electrum
14 Luglio 2018
148
41
58
138
Ultima modifica:
Salve a tutti, ultimamente ero alle prese con un programma che prende in input un file di configurazione (File.ini), ed inserendo il nome del parametro ne restituisce il valore assegnato, come:

File.ini:
parametro1: 123 parametro2: abc ...

C++:
string GetParametreValue("parametro2");
restituisce come valore una stringa contenete "abc╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠".

esce così perchè la stringa viene copiata in un Array di Char e visualizando la memoria nel momento dell`inizializzazione dell`Arry assomiglia a qualcosa come:
CC CC CC CC ╠╠╠╠
CC CC CC CC ╠╠╠╠
CC CC CC CC ╠╠╠╠
CC CC CC CC ╠╠╠╠
...

e quando copia la stringa ("abc") nell`Array di Char diventa:
97 98 99 CC abc╠
CC CC CC CC ╠╠╠╠
CC CC CC CC ╠╠╠╠
CC CC CC CC ╠╠╠╠
...

Avevo rimediato a questo errore usando la funzione:
C++:
ZeroMemory();
(Ripulendolo prima che la stringa venisse copiata)

Ma tutt`ora mi chiedo, Perchè quando l`Array viene inizializato è del tutto composto da CC e non da sequenze di escape come \0? e poi, esistono modi per "ripulirlo" senza usare la funzione sopra citata?

PS: l`Array l`ho inizializato così
C++:
char Array[255];
 
Se stai utilizzando C++ dovresti usare la classe std::string invece che gli array di caratteri. Comunque è difficile capire dove sbagli senza vedere il codice, non ho niente da correggere. ZeroMemory non è nemmeno nello standard C++, è roba che c'è solo nelle Windows API e ti sconsiglio di usarla.

Una zona in memoria, quando non è inizializzata, contiene dei valori non ben definiti. Immagino che l'aria di memoria in cui stai copiando il contenuto non sia inizializzata e per questo motivo contiene "roba a caso" (che tu vedi come 0xCC, ma in circostanze diverse potrebbe essere anche un altro valore).

Ti propongo una cosa di questo tipo:
C++:
std::string trim(std::string_view str) {
  static constexpr auto whitespace = " \n\r\t\f\v";
  str.remove_prefix(str.find_first_not_of(whitespace));
  str.remove_suffix(str.size() - str.find_last_not_of(whitespace) - 1);
  return std::string(str);
}

std::unordered_map<std::string, std::string> parse(std::istream &stream, char delim = ':') {
  std::unordered_map<std::string, std::string> map;

  for (std::string line; std::getline(stream, line);) {
    auto view = std::string_view(line);
    if (auto sep = view.find_first_of(delim); sep != std::string::npos)
      map.insert_or_assign(trim(view.substr(0, sep)), trim(view.substr(sep + 1)));
  }

  return map;
}
Gli passi un file (std::ifstream) e ti restituisce un dizionario in cui map["parametro1"] vale "123" e così via per ogni parametro presente nel file. Rimuove gli spazi in testa e in coda e ignora le linee che non contengono il separatore :.
 
  • Mi piace
Reazioni: DanyDollaro
Salve a tutti, ultimamente ero alle prese con un programma che prende in input un file di configurazione (File.ini), ed inserendo il nome del parametro ne restituisce il valore assegnato, come:

File.ini:
parametro1: 123 parametro2: abc ...

C++:
string GetParametreValue("parametro2");
restituisce come valore una stringa contenete "abc╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠".

esce così perchè la stringa viene copiata in un Array di Char e visualizando la memoria nel momento dell`inizializzazione dell`Arry assomiglia a qualcosa come:
CC CC CC CC ╠╠╠╠
CC CC CC CC ╠╠╠╠
CC CC CC CC ╠╠╠╠
CC CC CC CC ╠╠╠╠
...

e quando copia la stringa ("abc") nell`Array di Char diventa:
97 98 99 CC abc╠
CC CC CC CC ╠╠╠╠
CC CC CC CC ╠╠╠╠
CC CC CC CC ╠╠╠╠
...

Avevo rimediato a questo errore usando la funzione:
C++:
ZeroMemory();
(Ripulendolo prima che la stringa venisse copiata)

Ma tutt`ora mi chiedo, Perchè quando l`Array viene inizializato è del tutto composto da CC e non da sequenze di escape come \0? e poi, esistono modi per "ripulirlo" senza usare la funzione sopra citata?

PS: l`Array l`ho inizializato così
C++:
char Array[255];
Allora, seguendo il tuo lavoro conviene inizializzare l'array di char con il valore di fine linea '\0':

Char foo[256] = { '\0' };

perché probabilmente il tuo programma scrive i caratteri nell'array di char ma non inserisce il fine linea e quindi stampa anche gli altri indirizzi nell'array non inizializzati.

Altrimenti devi utilizzare string "che conviene" come l'utente qui sopra ti ha descritto
 
  • Mi piace
Reazioni: DanyDollaro
Ultima modifica:
Vi ringrazio per le vostre risposte, non mi capita spesso di usare delle stringhe o array di char di conseguenza non ne sono tanto pratico, la funzione che avevo creato è:
C++:
#include <Windows.h>
#include <iostream>
#include <fstream>
#include <string>

ifstream ReadConfig;
string line[10]; //La stringa line ha un`array che uso in un`altra parte del codice, ma in questa funzione uso solo il primo array (Che sarebbe 0)

string GetParametreValue(string Parametre)
{
    char look[2048];
    //In look viene salvata la prima riga del file

    char* Plook = &look[0];
   //Plook è il puntatore su look che uso nella funzione ZeroMemory

    char ValueC[2048];
    //ValueC sarebbe il valore di ritorno della funzione ma salvato in un Array di Char che poi viene spostato in una stringa


    while (getline(ReadConfig, line[0]))
    {
        ZeroMemory(Plook, 2048);
        //Con ZeroMemory "Ripulisco" la variabile look


        line[0].copy(look, Parametre.size(), 0);
        //Copio look nel primo array di line, mentre "Parametre.size()" fa in modo che il nome Del parametro sul file e quello inserito nella funzione siano della stessa lunghezza


        if (Parametre.compare(look) == 0) //La stringa inserita come parametro viene comparata con ogni singola linea del file
        {

            int Start = line[0].find(':') + 2, StringSize = line[0].size(), ByteToRead = StringSize - Start;
            //Start indica il byte su cui è scritto il primo valore del parametro (nel caso di "abc" indica "a")
            //StringSize ha il suo medesimo significato
            //ByteToRead sarebbe la lunghezza in byte del parametro

            ZeroMemory(&ValueC, 2048);
            //"Ripulisco" ValueC
            line[0].copy(ValueC, ByteToRead, Start);
            //Copio il valore del parametro dentro in ValueC

            string Value(ValueC); //Copio ValueC in una stringa

            return Value;
        }
    }
}

Ogni volta inizializato un Array di Char l`ho poi ripulito, il motivo per cui non ho usato solo stringhe è che a quanto ho notato non potevo usare la funzione .copy(); su 2 stringhe ma doveva avere come buffer un Char (Il buffer in cui viene copiata la stringa) dato che non conosco altri modi per estrarre una precisa parte della stringa.

Probabilmente avrei potuto creare questa funzione in modi migliori (Come consigliato da St3ve)
 
Date due stringhe a e b, per copiare il valore di una nell'altra puoi fare a = b (invece che copy) e per controllare se sono uguali puoi fare a == b (invece che compare). Se vuoi creare una stringa contenente i primi 3 caratteri di un'altra stringa puoi fare a.substr(0,3) e se vuoi la sottostringa che va dal terzo carattere in poi puoi fare a.substr(2). Per passare da stringa in C++ a stringa in C (const char *) si usa a.c_str(). E così via per molte altre cose...
Ti consiglio di documentarti sulle stringhe prima di ripiegare sugli array di char o cose simili.
 
  • Mi piace
Reazioni: DanyDollaro
Ultima modifica:
Date due stringhe a e b, per copiare il valore di una nell'altra puoi fare a = b (invece che copy) e per controllare se sono uguali puoi fare a == b (invece che compare). Se vuoi creare una stringa contenente i primi 3 caratteri di un'altra stringa puoi fare a.substr(0,3) e se vuoi la sottostringa che va dal terzo carattere in poi puoi fare a.substr(2). Per passare da stringa in C++ a stringa in C (const char *) si usa a.c_str(). E così via per molte altre cose...
Ti consiglio di documentarti sulle stringhe prima di ripiegare sugli array di char o cose simili.

Grazie St3ve, alla fine ho riaggiornato il codice :D.

C++:
string GetParametreValue(string ParametreToRead)
{

    if (ReadConfig.is_open())
    {

        string look, Value;


        while (getline(ReadConfig, line))
        {

            look = line.substr(0, ParametreToRead.size());


            if (ParametreToRead == look)
            {

                int Start = line.find(':') + 2, StringSize = line.size(), ByteToRead = StringSize - Start;

                Value = line.substr(Start, ByteToRead);

                return Value;

            }

        }

        return "Error: Parametre Not Found";

    }
    else
    {
        return "Error: No Configuration File Found";
    }


}