Есть ли способ изменить размер массива, используя только функции malloc и free? - PullRequest
1 голос
/ 12 мая 2019

В качестве домашнего задания я должен написать функцию, которая изменяет размер массива, используя только функции malloc и free.

Я знаю, как это сделать с realloc, но я не знаю, как это сделать с malloc.

typedef struct{
    int *tab;
    int size;
}arr;

и мне нужно написать эту функцию:

void changeSizeDyn(arr *Dyn_arr, int size){
    //
}

Как уже упоминалось в домашнем задании: требуется только одно перераспределение и только один выпуск памяти, и только одна копия элементов из старого массива. Я много искал, но нашел только результаты, используя realloc.

Возможно ли это сделать даже без использования realloc?

Ответы [ 5 ]

5 голосов
/ 12 мая 2019
  1. Выделите память нового размера, используя malloc.
  2. Копировать все байты из старой памяти в новую память. Вы можете сделать это в цикле, вам на самом деле не нужно использовать функции для этого (если это назначение).
  3. Освободите старую память.
1 голос
/ 12 мая 2019

Возможно ли это сделать без использования realloc?

Конечно, это так.

Вы можете, например, сделать этокак это:

#include <stdlib.h> /* for malloc () and free () and EXIT_xxx macros. */
#include <stdio.h> /* for perror */

typedef struct{
  int *tab;
  size_t size; /* Prefer using size_t for (memory) sizes over using a 
                  signed int. */
} arr;

void changeSizeDyn(arr *parr, size_t size)
{
  if (!parr && !parr->tab)
  {
    errno = EINVAL;
  }
  else
  {
    arr tmp = {
      malloc(size * sizeof *tmp.tab),
      size
    };

    if (tmp.size && !tmp.tab)
    {
      perror("malloc() failed");
    }
    else
    {      
      for (
        size_t m = (tmp.size < parr->size) ?tmp.size :parr->size, i = 0;
        i < m; 
        ++i)
      {
        tmp.tab[i] = parr->tab[i];
      }

      free(parr->tab);

      *parr = tmp;

      errno = 0;
    }
  }
}

/* A main() to test the above: */
int main(void)
{
  const size_t s = 42;
  arr a = {
    malloc(s * sizeof *a.tab),
    s
  };

  if (a.size && !a.tab)
  {
    perror("malloc() failed");
    exit(EXIT_FAILURE);
  }

  /* populate a.tab[0] to a.tab[a.size - 1] here. */

  changeSizeDyn(&a, 43);
  if (0 != errno)
  {
    perror("changeSizeDyn() failed");
    exit(EXIT_FAILURE);
  }

  /* Use a.tab[0] to a.tab[a.size - 1] here. */

  free(a.tab);
  a.size = 0;
}
1 голос
/ 12 мая 2019
struct Arr {
        int *tab;
        ptrdiff_t nmemb;
};

Допустим, вы изначально выделили массив следующим образом:

struct Arr x;

x.nmemb = 7;
x.tab = malloc(sizeof(*x.tab) * x.nmemb);

Вы должны перераспределить его с помощью этой функции:

void change_size_dyn(struct Arr *dyn_arr, ptrdiff_t nmemb)
{
        struct Arr old;
        ptrdiff_t cp_nmemb;

        if (!dyn_arr)
                return;
        old = *dyn_arr;

        if (nmemb <= 0)
                goto err;
        dyn_arr->tab = malloc(sizeof(*dyn_arr->tab) * nmemb);
        dyn_arr->nmemb = nmemb;

        cp_sz = MIN(old.nmemb, nmemb);
        memcpy(dyn_arr->tab, old.tab, cp_nmemb);
        free(old.tab);

        return;
err:
        dyn_arr->tab = NULL;
        dyn_arr->nmemb = 0;
        free(old.tab);
}

Которая должна быть названа следующим образом:

change_size_dyn(&x, 9);

Вы можете использовать эту функцию даже для первого распределения (хотя вы должны сначала установить NULL и 0 оба значения).Вам также не нужно добавлять бесплатно;вход 0 будет делать это за вас, точно так же, как realloc будет делать:

int main(void)
{
        struct Arr x = {0};

        change_size_dyn(&x, 5);
        /* ... */
        change_size_dyn(&x, 9);
        /* ... */

cleanup:
        change_size_dyn(&x, 0);
        return 0;
}

Если вы передадите структуру, где dyn_arr->nmemb уже является отрицательным значением (не должно происходить), поведениене определено (это отрицательное значение попадет в memcpy, которое будет заключено в очень высокое значение size_t, которое приведет к переполнению массивов).Я не хотел проверять это, потому что это было бы ненужным в любом не ошибочном сценарии.

1 голос
/ 12 мая 2019

Функция не так проста, как кажется на первый взгляд.

Вот и вы.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>

typedef struct
{
    int *tab;
    int size;
} arr;

int changeSizeDyn( arr *Dyn_arr, int size )
{
    int success = 1;

    if ( Dyn_arr->size != size )
    {
        int *tmp = NULL;

        if ( size != 0 )
        {
            tmp = malloc( size * sizeof( int ) );
            success = tmp != NULL;

            if ( success )
            {
                int n = 0;

                if ( Dyn_arr->size != 0 )
                {
                    n = size < Dyn_arr->size ? size : Dyn_arr->size;

                    memcpy( tmp, Dyn_arr->tab, n * sizeof( int ) );
                }

                if ( n < size ) memset( tmp + n, 0, ( size - n ) * sizeof( int ) );
            }
        }

        if ( success )
        {
            free( Dyn_arr->tab );

            Dyn_arr->tab = tmp;
            Dyn_arr->size = size;
        }
    }

    return success;
}

int main( void )
{
    arr a = { NULL, 0 };
    int size = 10;

    if ( changeSizeDyn( &a, size ) )
    {
        for ( int i = 0; i < size; i++ ) a.tab[i] = i;

        for ( int i = 0; i < size; i++ ) printf( "%d ", a.tab[i] );
        putchar( '\n' );
    }

    size = 5;

    if ( changeSizeDyn( &a, size ) )
    {
        for ( int i = 0; i < size; i++ ) a.tab[i] = i;

        for ( int i = 0; i < size; i++ ) printf( "%d ", a.tab[i] );
        putchar( '\n' );
    }

    size = 0;

    if ( changeSizeDyn( &a, size ) )
    {
        for ( int i = 0; i < size; i++ ) a.tab[i] = i;

        for ( int i = 0; i < size; i++ ) printf( "%d ", a.tab[i] );
        putchar( '\n' );
    }

    assert( a.tab == NULL && a.size == 0 );
}

Выход программы:

0 1 2 3 4 5 6 7 8 9 
0 1 2 3 4 

Функция имееттип возврата int, чтобы указать, был ли его вызов успешным или нет.Вам нужен метод, чтобы определить, был ли вызов функции успешным.Иначе это невозможно будет определить.Поэтому использовать тип void в качестве возвращаемого типа функции - плохая идея.

Функция по умолчанию устанавливает все новые элементы, которые не соответствуют элементам старого массива, на ноль.На самом деле это не является необходимым, но делает использование структуры более понятным.

Если пользователь передает размер, равный 0, то функция просто освобождает старый массив и устанавливает элемент данных tab равным NULL.

Если новый размер и старый размермассив равен друг другу, функция ничего не делает.

Примите во внимание, что намного лучше объявить элемент данных size структуры как имеющий тип size_t.В противном случае вам нужно добавить в функцию проверку, что size не является отрицательным.Я этого не делал, потому что полагаю, что вы действительно будете использовать тип size_t вместо типа int в объявлении структуры (и функции).

0 голосов
/ 12 мая 2019
void *onlymallocrealloc(void *ptr, size_t oldsize, size_t newsize)
{
    void *newmem = malloc(newsize);

    if(ptr && newmem)
    {
        memcpy(newmem, ptr, oldsize);
        free(ptr);
    }
    return newmem;
}

и для вашего типа (слегка изменено)

typedef struct{
    int size;
    int tab[];
}arr;


arr *onlymalloc(arr *ptr, size_t newsize)
{
    ptr = onlymallocrealloc(ptr, ptr -> size * sizoef(ptr -> tab[0]) + sizeof(*ptr), newsize * sizoef(ptr -> tab[0]) + sizeof(*ptr));

    if(ptr)
    {
        ptr -> size = newsize;
    }
    return ptr;
}

изменить, если функция должна быть недействительной

void onlymallocrealloc(void **ptr, size_t oldsize, size_t newsize)
{
    void *newmem = malloc(newsize);

    if(*ptr && newmem)
    {
        memcpy(newmem, *ptr, oldsize);
        free(*ptr);
        *ptr = newmem;
    }
}

void onlymalloc(arr **ptr, size_t newsize)
{
    onlymallocrealloc(&ptr, *ptr -> size * sizoef(*ptr -> tab[0]) + sizeof(**ptr), newsize * sizoef(*ptr -> tab[0]) + sizeof(**ptr));

    if(*ptr)
    {
        *ptr -> size = newsize;
    }
}
...