Добавление чисел Фибоначчи в начало динамического массива

80
13 февраля 2022, 15:30

товарищи программисты.

В общем, нужно с помощью calloс создать одномерный массив, затем заполнить его рандомными числами, а после добавить в начало этого массива введенное с клавиатуры кол-во чисел Фибоначчи. Я написал код, но при больших значениях программа вылетает, в чём ошибка и что вы вообще посоветуете подкорректировать?

И оцените сам код, дайте максимально конструктивную критику : что я использую не так, что вообще не нужно использовать...

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
void FillArray(int *array,int size);
void OutArray(int *array,int size);
int Fib(int amount);
int main()
{
    srand(time(NULL));
    int size = 0, amount = 0;
    int* first = 0;
    cout << "Enter size : ";
    cin >> size;
    first = (int*) calloc(size, sizeof(int));
    FillArray(first, size);
    OutArray(first, size);
    cout << "How many Fib's number do you want to add : ";
    cin >> amount;
    int n_s = amount + size;
// size = 3, n_s = 7;
    first = (int*) realloc(first,n_s * sizeof(int));
    if(first == NULL){
        cout << "Memory is not available!" << endl;
        free(first);
    }
        for(int a = n_s, i = 0, j = size; i <= size; i++, j--, a--){
            *(first + a)=*(first+j);
            *(first+j) = 0;
        }
        for(int i = 0; i < amount; i++){
            *(first+i) = Fib(i);
        }
    OutArray(first, n_s);
    int count  = 0;
    free(first);
    first = NULL;
}
void FillArray(int *array,int size){
    for(int i = 0; i < size; i++){
        *(array + i ) = rand() % 100;
    }
}
void OutArray(int *array,int size){
    for(int i = 0; i < size; i++){
        cout << *(array + i) << " ";
    }
    cout << endl;
}
int Fib(int amount){
    if(amount == 0)
        return 0;
    if(amount == 1)
        return 1;
    else
        return Fib(amount - 1 ) + Fib(amount - 2);
}
Answer 1

Если закрыть глаза на разные ошибки, то программа скорее всего вылетает из-за рекурсивного вычисления чисел Фибоначчи. Да, для объяснения рекурсии она хороша, но для реальной жизни - нет.

Поэтому напишем ее по другому (я постарался сохранить стиль Вашего кода).

void FillFib(int *array, int size)
{
  if (size > 0) *(array) = 1;
  if (size > 1) *(array+1) = 1;
  for (int i = 2; i < size; i++) {
    *(array+i) = *(array+i-1) + *(array+i-2);
  }
}

если нужно хотя бы один элемент - заполним его единицей. Если хотя бы два - то их оба. А дальше - по классике - сумма двух предыдущих.

и в коде main вместо цикле вызываем

 FillFib(first, amount);

Теперь даже при больших числах "летает".

Но потом я обратил внимание на странный код "перемещения" данных.

for(int a = n_s, i = 0, j = size; i <= size; i++, j--, a--){
  *(first + a)=*(first+j);
    *(first+j) = 0;
}

и вот оно. При самом первом обращении происходит копирования элемента за пределы массива. *(first+a) => *(first+n_s). Перепишем

for(int a = n_s-1, j = size-1; j>=0; j--, a--){
  *(first + a)=*(first+j);
}

и зануление я убрал. Но в целом я бы тут на memove заменил бы (и так пишем в си стиле).

И посмотрим ещё на этот участок

first = (int*) realloc(first,n_s * sizeof(int));
if(first == NULL){
    cout << "Memory is not available!" << endl;
    free(first);
}

вначале типичная ошибка использования realloc. Если ему не удалось выделить память, то он возвратит NULL, но исходную память не тронет. И в этом случае Вы ее потеряете. И если даже так, то вызывать free для заведомо NULL указателя не имеет смысла - он ничего не сделает. Для учебного примера думаю этот if можно убрать, просто не забыв выше добавить проверки на то, что пользователь запросил нулевой размер (или, что хуже, отрицательный).

И в этом же куске вторая ошибка. Допустим, что память не выделилась. Но код продолжается. А указатель то уже NULL...

Хорошо бы его так переписать

int* temp  = (int*) realloc(first,n_s * sizeof(int));
if(temp == NULL){
    cout << "Memory is not available!" << endl;
    free(first);
    return 1;
}
first = temp;

По поводу отсутствия return в конце main - стандарт позволяет это делать. Это легально.

Answer 2

1) У вас метка C++ - тут лучше использовать RAII, а не указатели (их уже лет 15 не используют в явном виде, это же не С) для работы с массивами данных есть множество контейнеров (std::vector и т.д.);

1.1) В С++ есть new и delete, использовать С аллокаторы - не оч...

1.2) В С++ есть nullptr для указателей а не интовый NULL;

2) Генерация "случайных чисел" - тут лучше взять не rand() + time, а ПДСЧ по типу std::mt19937_64, а лучше /dev/random и /dev/urandom в Linux или рандом из CryptoAPI в Win32;

3) Между оператором и скобкой ставится проел (не if(..), а if (..)) к for это тоже относится;

4) using namespace std - на взгляд многих людей - моветон, всегда можно обратиться к этому namespace через std::..

5) Для приведения типов в C++ есть касты https://www.tutorialspoint.com/cplusplus/cpp_casting_operators.htm , надо использовать их, а не С_стайл_каст((int*)ptr);

6) В Вашей функции int Fib(int amount) else вообще лишний, перед финальным return else не пишется;

7) У вас плавает codestyle, в main { с новой строки, а тут void FillArray(int *array,int size) прилипла и нет пробела между аргами функции.

8) У вас определено int main(), но она ничего не возвращает, нужно хотя бы return exitcode;

Вот пример Вашей задачи в мое видении..

#include <algorithm>
#include <iostream>
#include <iterator>
#include <random>
#include <limits>
#include <cstdint>

std::vector<uint64_t> RandomUintDataArray(const std::size_t size, const std::size_t low_bound,
                                                                  const std::size_t hight_bound) noexcept {
    static std::random_device r_device{};
    static std::mt19937_64 entropy_generator{r_device()};
    static std::uniform_int_distribution<> ranc_distribution(low_bound, hight_bound);
    static auto PRNG{std::bind(ranc_distribution, entropy_generator)};
    std::vector<uint64_t> random_data;
    try {
        random_data.resize(size);
        std::generate(std::begin(random_data), std::end(random_data), PRNG);
    } catch (std::bad_alloc& exc) {
        std::cerr << exc.what() << std::endl;
        random_data.clear();
        random_data.shrink_to_fit();
    }
    return random_data;
}

uint64_t FibonacciValueOfIndex(std::size_t n) noexcept {
    if (n == 0) {
        return 0;
    }
    if (n == 1) {
        return 1;
    }
    uint64_t v_curr{1}, v_priv{0};
    while (n-- > 1) {
        v_curr += v_priv;
        v_priv = v_curr - v_priv;
    }
    return v_curr;
}

int main() {
    constexpr uint64_t data_size{40};
    auto data{RandomUintDataArray(data_size, 0, 100)};
    if (!data.size()) {
        return -1;
    }
    std::cout << "Data before insert : " << std::endl;
    std::copy(std::begin(data), std::end(data), std::ostream_iterator<uint64_t>{std::cout, "; "});
    std::cout << std::endl;
    uint64_t n_for_fib{20};
    if (n_for_fib > data.size()) {
        return -2;
    }
    for (auto i{0}; i < n_for_fib; ++i) {
        data.at(i) = FibonacciValueOfIndex(i);
    }

    std::cout << "Data after insert : " << std::endl;
    std::copy(std::begin(data), std::end(data), std::ostream_iterator<uint64_t>{std::cout, "; "});
    std::cout << std::endl;
    return 0;
}
READ ALSO
thread.join() Почему потоки вызываются в случайном порядке? С++

thread.join() Почему потоки вызываются в случайном порядке? С++

Пытаюсь разобраться в многопоточности и mutexПо задумке сначала должен выполниться thread Calc(Stream), после его окончания должен выполниться thread...

162
Ругается компилятор

Ругается компилятор

Ошибка компиляции на cols во вложенном цикле:

88
Выводится лишний текст в окне winapi

Выводится лишний текст в окне winapi

Подскажите, Как исправить ввод лишних символов?

77
UrlDownloadToFile ошибка E_ABORT Operation aborted

UrlDownloadToFile ошибка E_ABORT Operation aborted

Код C++, Visual Studio 2019, urlmon подключен, вызываю так:

85