有没有办法仅使用 malloc 和自由函数来更改数组大小?

Is there a way to change an array size using malloc and free functions only?

作为家庭作业,我必须编写一个仅使用 mallocfree 函数来更改数组大小的函数。

我知道如何使用 realloc,但我不知道如何使用 malloc

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

我需要编写这个函数:

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

正如作业中所说:它只需要一次重新分配和一次内存释放,并且只需要从旧数组中复制一份元素。 我搜索了很多但只找到了使用 realloc.

的结果

是否可以不使用 realloc 来做到这一点?

  1. 使用 malloc 分配新大小的内存。
  2. 将旧内存中的所有字节复制到新内存中。您可以在循环中执行此操作,您实际上不需要为此使用函数(如果这是赋值)。
  3. 释放旧内存。
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

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;
    }
}

功能并不像乍看起来那么简单

给你。

#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 

该函数有一个 return 类型 int 来表示它的调用是否成功。您需要一种方法来确定函数调用是否成功。否则将无法确定这一点。所以使用类型 void 作为函数的 return 类型是一个坏主意。

该函数默认将与旧数组元素不对应的所有新元素设置为零。其实不是必须的,只是让结构体的使用更加清晰。

如果用户传递的大小等于 0,则该函数只是释放旧数组并将数据成员 tab 设置为 NULL

如果数组的新大小和旧大小相等,则函数不执行任何操作。

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

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);

您甚至可以在第一次分配时使用此功能(尽管您应该首先设置 NULL0 这两个值)。您也不需要添加免费的;输入 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,这会溢出数组)。我不想检查它,因为在任何 non-buggy 场景中都没有必要。

Is it even possible to do that without using 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;
}