在c [BEGINNER]中使用malloc的堆栈实现

时间:2016-12-11 18:59:19

标签: c stack malloc

用于学习目的我在c中实现了一个带有它的函数的堆栈。 我添加了一些小的附加功能,以便第一次使用malloc并尝试正确理解它。

我编写了一个最初创建堆栈结构的函数。函数的返回值是一个具有已分配内存的新结构。在函数中处理malloc异常的最佳方法是什么,返回值应该是struct?也许我应该设计不同的功能?我知道printf没有做它的工作;)

我的堆栈结构:

typedef struct Stack
{
    int count;
    int capacity;
    int *data;
} Stack;

创建堆栈实例:

Stack create_stack(int initialcapacity)
{
    Stack new_stack;
    new_stack.count = 0;
    new_stack.capacity = initialcapacity;

    if (!(new_stack.data = malloc(initialcapacity * sizeof(int))))
        printf("not enough memory!");

    return new_stack;
}

使用堆栈的初始容量调用该函数:

Stack stack = create_stack(10);

当我编写一个删除Stack实例的函数时,出现了第二个问题。

int delete_stack(Stack *stack)
{
    stack->count = 0;
    stack->capacity = 0;
    free(stack->data);
    stack->data = NULL;
    return 0;
}

我能否删除结构实例本身?将值设置回0并将int *指向NULL会感觉不完整。

最后但并非最不重要的是,我对我的推送功能有疑问。另外在这里我添加了一些功能,允许我在堆栈已经满了的时候推送它:

void push(int value, Stack *stack)
{   
    if (stack->count == stack->capacity)
    {   
        int *temp = malloc(stack->capacity * sizeof(int));

        int i;
        for (i = 0; i < stack->count; i++)
            temp[i] = stack->data[i];

        free(stack->data);
        stack->data = NULL;

        stack->data = malloc(stack->capacity * 2 * sizeof(int));

        for (i; i > -1; i--)
            stack->data[i] = temp[i];

        free(temp);
        temp = NULL;
        stack->data[stack->count] = value;
        stack->count++;
        stack->capacity = stack->capacity * 2;
    }
    else
    {
        stack->data[stack->count] = value;
        stack->count++;
    }
}

是否有必要&#34;免费&#34;在我分配一个大小的新数组之前,较小的数组并将指针置为NULL?

如果我的代码中有任何不必要或没有正确写入的内容,请告诉我,我感激任何让我更好的提示。

Cheeers, 我

3 个答案:

答案 0 :(得分:2)

我会用指针做。也就是说,您的create_stack()将使用malloc分配新的Stack结构,然后将值设置为struct并再次使用malloc为Stack->data分配空间。像这样:

Stack* create_stack(int initialcapacity) {
    Stack* new_stack = malloc(sizeof(Stack));

    if (new_stack == NULL)
        return NULL; // return null to tell the caller that we failed

    new_stack->count = 0;
    new_stack->capacity = initialcapacity;
    new_stack->data = malloc(initialcapacity * sizeof(int))

    if (new_stack->data == NULL)
    {
        free(new_stack);
        return NULL;
    }

    return new_stack;
}

有了这个,我们&#34;处理&#34;返回NULL会产生malloc错误,因此调用者知道我们失败了。

现在我们已经使用malloc来分配Stack结构,你可以(读:必须)使用free(stack);中的delete_stack()释放它所占用的空间。

push()中,不需要临时数组,也就是说,您可以立即分配更大的数组,将内容从原始stack->data复制到其中,免费stack->data并将其设置为新的malloc阵列:

int *temp = malloc(stack->capacity * 2 * sizeof(int));
// TODO: what if malloc fails?

int i;
for (i = 0; i < stack->count; i++)
    temp[i] = stack->data[i];

free(stack->data);
stack->data = temp;

stack->data[stack->count] = value;
stack->count++;
stack->capacity = stack->capacity * 2;

答案 1 :(得分:2)

问。在函数中处理malloc异常的最佳方法是什么,返回值应该是struct?

至少有三种方式:

1)不是返回结构本身,而是返回指向它的指针。这意味着两个malloc:一个用于结构本身,另一个用于data字段。返回NULL指针意味着在构造期间出现了问题。

struct Stack* create_stack(int initialcapacity) {
    struct Stack* stack = malloc(sizeof(struct Stack));
    ...
    return stack;
}

2)更灵活的方法是将指针传递给已分配的结构。灵活性来自于调用代码控制在何处分配结构:堆栈或动态内存中的想法。函数的返回值可以仅用于通知调用代码有关错误的信息:

bool create_stack(int initialcapacity, struct Stack* stack) {
  ...
}

// if calling code wants structure on stack (yeah, "stack" on stack)
struct Stack stack;
if (!create_stack(50, &stack)) {
  die();
}

// if calling code wants it in dynamic memory
struct Stack* stack = malloc(sizeof(struct Stack));
if (!stack) {
  die();
}
if (!create_stack(50, stack)) {
  die();
}

3)如果您的程序不是10,000+ LOC生产代码,最简单的方法可能是简单地打印错误消息并在分配失败时立即中止程序。通常分配错误是致命的:如果内存不足,则无法以任何有意义的方式恢复。您甚至可以在malloc上创建一个包装函数来自动捕获此类错误并退出:

void* my_malloc(size_t count) {
    void* ptr = malloc(count);
    if (ptr == NULL) {
      fprintf(stderr, "Allocation failed");
      exit(EXIT_FAILURE);
    }
    return ptr;
}

问。我能够删除结构实例本身吗?

不,你不能。因为它是在堆栈上分配的(结构本身,而不是data)。如果要删除结构本身,则需要使用上面的方法#1。

顺便说一句,没有必要为字段设置零和NULL。它不会删除任何内容。这种方法很少使用,只是为了捕获错误(当调用代码时首先删除一些结构,然后尝试使用它)。

问。是否有必要“释放”较小的数组并将指针设置为NULL,然后再分配一个大小翻倍的新数组?

再次,您不需要对任何内容进行NULL化 - 它不会删除任何内容。而不是两个malloc和手动复制使用realloc,这将为您完成大部分工作。

答案 2 :(得分:0)

通常,您应该能够声明一个结构,然后有一个64个数组的数组,并带一个整数来表示哪个条目位于顶部。很简单,没有动态分配。但64是相当低的,因为堆栈,递归和嵌套级别密切相关。通常应该可以看到64是一个疯狂的嵌套级别,没有合法的输入甚至可以接近它。然后,您可能需要一个防护来防止恶意或损坏的输入,但这只会终止程序或子程序。

如果你不能在堆栈上建立一个低理智界限,那么你可能还需要一个。它是一种罕见的情况,其中嵌套非常深,或者您没有以最佳方式解决问题,但仍然有效的次优程序优于无程序。

所以你使用相同的结构,但是通过调用来设置堆栈 malloc(),如果超出界限,则重新调用realloc()。 你可能还想要理智地检查它,但现在进行健全性检查 比64更高,一百万左右。你还必须检查 realloc不会失败。

typedef struct
{
    int x;
    char astring[32];
} ENTRY;

static ENTRY *stack = 0;;
static int top = -1;
static int N = 0; 

void push(const ENTRY *e)
{
   /* grow logic like this */
   ENTRY *temp = realloc(stack, newsize * sizeof(ENTRY));
   if(temp)
     stack = temp;
   else
   {
      /* reallocation has failed but stack still valid */
      free(stack);
      N = 0;
      top = -1;
      /* for the sake of argument do this. We need temp to avoid
         a memory leak */
      fprintf(stderr, "out of memory\n");
   }

    /* push here, trivial */
}

int pop(ENTRY *e)
{
    /* e is a non-const pointer. Fill and reset stack top */ 
}

您可能希望堆栈全局,如示例中所示,或者您可能想要 将它包裹在你传递的结构中。通常你会想要指针 或堆栈上的结构,但有时您可能需要堆栈 整数或浮点值。

特别是在C中处理内存分配错误没有好方法 那些不会发生的事情(安装了几GB内存的计算机) 更容易发生电气故障而不是耗尽 当被要求几千字节时的内存)。通常的规则是 分手了。但这使得推送难度很大,因为每次推动都是如此 可能会使计算机内存不足(但事实并非如此) 只是你的封装允许功能失败)。