1

As homework, I have to write a function that changes an array's size using malloc and free functions only.

I know how to do it with realloc, but I don't know how to do it with malloc.

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

and I need to write this function :

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

As mentioned in the homework: it requires only one reallocation and only one memory release, and only one copy of the elements from the old array. I searched a lot but only found results using realloc.

Is it even possible to do that without using realloc?

4

5 回答 5

5
  1. 使用 malloc 分配新大小的内存。
  2. 将旧内存中的所有字节复制到新内存。您可以在循环中执行此操作,您实际上不需要为此使用函数(如果这是分配)。
  3. 释放旧记忆。
于 2019-05-12T14:23:38.587 回答
1

该功能并不像乍一看那样简单。

给你。

#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,则该函数只是释放旧数组并将数据成员设置tabNULL.

如果数组的新大小和旧大小相等,则函数什么也不做。

size考虑到将结构的数据成员声明为具有类型要好得多size_t。否则,您需要在函数中添加一个size非负数的检查。我没有这样做,因为我想您确实会在结构(和函数)声明中使用类型size_t而不是类型。int

于 2019-05-12T14:59:23.107 回答
1
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,这会溢出数组)。我不想检查这一点,因为在任何非错误的情况下都是不必要的。

于 2019-05-12T15:00:24.613 回答
1

甚至可以在不使用的情况下做到这一点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;
}
于 2019-05-12T15:01:26.433 回答
-2
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;
    }
}
于 2019-05-12T14:24:41.720 回答