Динамическое изменение размера массива и чтение значений. (без векторов) - PullRequest
1 голос
/ 04 февраля 2020

Здравствуйте. У меня возникла следующая трудность. Я пытаюсь прочитать таблицу двойников (1 запись на строку) и сохранить ее в массиве, динамически изменяя размер этого массива (для каждой строки / записи). Это для школьного задания и запрещает использование векторов (было бы намного проще ...). Основная идея, которая у меня была, - создать основной массив, в котором хранится значение, затем сохранить предыдущие значения и следующие в новый массив и делать это итеративно. В настоящее время проблема, с которой я сталкиваюсь, заключается в том, что сохраняется только последнее значение таблицы. Я осознаю, что каким-то образом мне нужно передавать данные по ссылке на глобальную функцию и что указатели, с которыми я работаю, обнуляются после того, как они выходят из следующей итерации while. Однако, поскольку точная длина данных неизвестна, это кажется невозможным, поскольку инициализация массива в main () невозможна (точная длина неизвестна). Любая помощь будет оценена. Код размещен ниже.

РЕДАКТИРОВАТЬ: после рассмотрения двух комментариев я внес следующие изменения в код, однако я не уверен, будут ли они вести себя надлежащим образом. Я добавил новую функцию с именем add_new_datapoint, которая должна глобально изменять значения указателя / длины, и это делается путем передачи значений по ссылке. Вызывается в проблемной инструкции c else как add_new_datapoint (data_ptr, data_len, new_dp). Кроме того, я не уверен, что перераспределение новой памяти для переменной-указателя не приведет к утечке памяти. По сути (после перераспределения data_ptr освобождается ли память, на которую «указывалось»), или мне нужно удалить ее, а затем повторно инициализировать в. В таком случае, можно ли снова сослаться на указатель «data_ptr» в следующая итерация l oop?

Ответы [ 2 ]

2 голосов
/ 04 февраля 2020

Я думаю, что будет проще упростить ваш опубликованный код, чем пытаться найти все места, где вы могли бы иметь ошибки.

Если вы ожидаете увидеть в вашем файле только double значения, вы можете упростить код для чтения данных из файла в:

while ( data_file >> new_data_pt )
{
   // Use new_data_pt
}

Если вы ожидаете, что могут быть значения, отличные от double s, то вы можете использовать:

while ( getline(data_file, line) )
{
    std::istringstream str(line);
    while ( str >> new_data_pt )
    {
       // Use new_data_pt
    }
}

, но затем Вы должны понимать, что код больше не будет читать значения из строки после того, как обнаружит ошибку. Если ваша строка содержит

10.2 K 25.4

, код будет читать 10.2, возникнет ошибка в K и не будет обрабатываться 25.4.

Код для обработки new_data_pt является то, что он должен быть сохранен в динамически распределенном массиве. Я хотел бы предложить поместить это в функцию.

double* add_point(double* data_ptr, int data_len, double new_data_pt)

Вызвать эту функцию как:

data_ptr = add_point(data_ptr, data_len, new_data_pt);

Предполагая, что первое while l oop, содержимое main становится :

int main()
{   
   std::fstream data_file{ "millikan2.dat" };

   // It is possible that the file has nothing in it.
   // In that case, data_len needs to be zero.
   int data_len{ 0 };

   // There is no need to allocate memory when there is nothing in the file.
   // Allocate memory only when data_len is greater than zero.
   double* data_ptr = nullptr; 

   double new_data_pt;

   if (!data_file.good()) {
      std::cerr << "Cannot open file";
      return 1;
   }

   while ( data_file >> new_data_pt ) 
   {
      ++data_len;
      data_ptr = add_point(data_ptr, data_len, new_data_pt);
   }

   // No need of this.
   // The file will be closed when the function returns.
   // data_file.close();
}

add_point может быть реализовано как:

 double* add_point(double* data_ptr, int data_len, double new_data_pt)
 {
    double* new_data_ptr = new double[data_len];

    // This works even when data_ptr is nullptr.
    // When data_ptr is null_ptr, (data_len - 1) is zero. Hence,
    // the call to std::copy becomes a noop.
    std::copy(data_ptr, data_ptr + (data_len - 1); new_data_ptr);

    // Deallocate old memory.
    if ( data_ptr != nullptr )
    {
       delete [] data_ptr;
    }

    new_data_ptr[data_len-1] = new_data_pt;
    return new_data_ptr;
 }

Код для отслеживания количества плохих точек намного сложнее. Если вы не обязаны это делать, я бы посоветовал игнорировать это.

0 голосов
/ 04 февраля 2020

Вы уже получили отличный ответ, но я подумал, что может быть полезно указать на несколько ошибок в вашем коде, чтобы вы могли понять, почему он не работает.

Во второй области действия, которую вы объявляете data_ptr снова, даже если это видно из внешней области видимости. (delete[] не удаляет сам указатель, он просто освобождает память, на которую указывает указатель.)

else {
            double* data_temp { new double[data_len] };
            std::copy(data_ptr, data_ptr + data_len - 2, data_temp);
            *(data_temp + data_len - 1) = new_data_pt;
            delete[] data_ptr;
            double* data_ptr{ new double[data_len] }; // <- Right here
            //for (int j{1}; j < data_len; j++) *(data_ptr + j) = *(data_temp + j);
            std::cout << std::endl;
        }

Вместо этого вы можете просто написать data_ptr = new double[data_len]. Однако это само по себе не сработает.

Все ваши данные исчезают, потому что на каждой итерации вы создаете новый массив, на который указывает data_temp, и копируете данные туда, а на следующей итерации вы установите data_temp, чтобы снова указывать на новый массив. Это означает, что на каждой итерации вы теряете все данные предыдущих итераций. Это также вызывает утечку памяти, так как вы выделяете больше памяти каждый раз, когда нажимаете на эту строку:

double* data_temp { new double[data_len] };

, но вы не звоните delete[] data_temp впоследствии.

Надеюсь, это поможет понять, почему это не работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...