【问题标题】:ting to Generic stack implementation in C with void pointers使用 void 指针在 C 中使用通用堆栈实现
【发布时间】:2017-01-19 10:42:16
【问题描述】:

试图实现一个简单的通用堆栈我经历了一堆堆栈溢出和分段错误我写了这个:

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

#define nullptr (void *)0

struct S {
    void *content;
    struct S *next;
};
typedef struct S Stack;
Stack *createStack() {
    Stack *tmp = (Stack *) malloc(sizeof(Stack));

    tmp->content = nullptr;
    tmp->next    = nullptr;

    return tmp;
}

Stack *pushStack(Stack *ptr, void *content) {
    Stack *newStr = createStack();
    newStr->content = content;
    newStr->next = ptr;
    ptr = newStr;
    return (ptr);
}

Stack *popStack(Stack *ptr, void *value) {
    Stack *toDelete = ptr;
    value = ptr->content;
    ptr = ptr->next;
    free(toDelete);
    return (ptr);
}

Stack *stackHandle = nullptr;

void printStack(Stack *ptr) {
    int *element;
    int i;
    for(i=0; i <= 20; i++) {
        ptr = popStack(ptr, element);
        printf("%d ", *element);
    }
    printf("\n");
}

int main() {
    stackHandle = createStack();
    int i;
    for(i=0; i <= 21; i++) {
        stackHandle = pushStack(stackHandle, &i);
    }
    printStack(stackHandle);
}

一切正常,但无法从 void 指针恢复我的 int ...... 分析其他实现我仍然不明白为什么我的不工作。 在这里,我期待看到21 i 次,但我却得到了

0 8240 842539056 842539056 842539056 842539056 842539056 842539056 842539056 842539056 842539056 842539056 842539056 842539056 842539056 842539056 842539056 842539056 842539056 842539056 842539056

对于每个大于 20 的 i 值,如果 i 低于 20,则为漂亮的分段错误

【问题讨论】:

  • 不要定义自己的空指针符号,使用标准的NULL。虽然您的宏对于NULL 来说是最常见的,但它不是可移植的,而且您的代码的读者也不会期望看到它。不要重新定义已经存在的东西。
  • 至于你的问题,你堆栈中的所有节点都会有它的数据指针指向同一个变量。如果您在调试器中单步执行代码,应该会非常明显。
  • 另外,如果代码被移植,nullptr 会与 C++ 发生冲突。
  • 您必须分配空间以将int 存储在content 中。并且还存储存储数据的大小。
  • 您还需要搜索并阅读有关在 c 中通过引用模拟调用。因为现在你的popStack 函数并没有按照你的想法做。当您在 printStack 函数中取消引用 element 时,这将导致 未定义的行为

标签: c stack void-pointers


【解决方案1】:

您实际上并没有分配任何空间来保存实际数据,您只是为指向其他地方的指针分配空间。这不是一个非常有用的 ADT。

我会做的是创建一个堆栈来保存数据的硬拷贝:

typedef struct S {
    void*     data;
    size_t    size;
    struct S* next;
} Stack;

然后必须更改功能:

Stack *pushStack(Stack *ptr, void *data, size_t size);
Stack *popStack(Stack *ptr, void *data, size_t* size); // return data and size

您将改为创建这样的项目:

Stack *createStack (void* data, size_t size) {
    Stack *tmp = malloc(sizeof(Stack));

    tmp->data = malloc(size);
    memcpy(tmp->data, data, size);
    tmp->size = size;
    tmp->next = NULL;

    return tmp;
}

一旦您将设计更改为上述内容,您就可以开始担心清除错误了。

【讨论】:

  • 好的,但是如果我不想memcpy,为什么我不能只存储指针?
  • @Zhigalin 因为指针不是数据。假设调用者将一个指向局部变量的指针传递给您的堆栈,然后分配该变量的函数超出范围。堆栈将指向垃圾。在编写这样的容器类时,它们应该始终使用硬拷贝的数据,除非您有一些非常特殊的情况。
  • @Zhigalin 在您的具体示例中,因为所有指针都指向完全相同的内存。
【解决方案2】:

在这段代码中:

for(i=0; i <= 21; i++) {
    stackHandle = pushStack(stackHandle, &i);
}

您将变量“i”的地址(而不是值)推送 21 次。当你执行 pop 时,你会检索到这个地址,当你打印它时,你可能会达到 22(“i”的当前值)。

【讨论】:

  • 是的,我知道......但我没有得到“变量“i”21次”,我得到的东西完全没有意义!
【解决方案3】:

您会得到一些奇怪的值,因为您的 ptr-&gt;content 指向的值超出了范围。您应该为您的内容值分配内存。 试试这个:

    for(i=0; i <= 21; i++) {
        int* pi = new int;
        *pi = i;
        stackHandle = pushStack(stackHandle, pi);
    }

更新: 以下是更正后的源代码(我测试过,它可以工作):

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

    #define nullptr NULL;

   struct S {
     void *content;
     struct S *next;
   };
   typedef struct S Stack;
   Stack *createStack() {
      Stack *tmp = (Stack *)malloc(sizeof(Stack));

       tmp->content = nullptr;
       tmp->next = nullptr;

       return tmp;
   }

   Stack *pushStack(Stack *ptr, void *content) {
      Stack *newStr = createStack();
      newStr->content = content;
      newStr->next = ptr;
      ptr = newStr;
      return (ptr);
    }
    // parameter value is of type (void**) !!!
    Stack *popStack(Stack *ptr, void **value) {
       Stack *toDelete = ptr;

       // In such a way we can return 'content' from the function.
       // 'content' is a pointer and to return it from the function
       // we should dereference pointer to a pointer 'value'
       *value = (ptr->content); 

       ptr = ptr->next;
       free(toDelete);
      return (ptr);
   }

   Stack *stackHandle = nullptr;

   void printStack(Stack *ptr) {
       int *element;
       int i;
       for (i = 0; i <= 20; i++) {
          ptr = popStack(ptr, (void**)&element); // pointer to apointer !!!
          if (element) {
             printf("%d ", *element);
             // deallocate memory and escape memory leaks !!!
             delete(element);
          }
       }
       printf("\n");
  }

  int main() {
     stackHandle = createStack();
     int i;
     for (i = 0; i <= 21; i++) {
         int* pi = new int(i); // allocate memory !!!
         stackHandle = pushStack(stackHandle, pi);
     }
     printStack(stackHandle);
 }

关键修复。

  1. 我们为 i 分配内存为int* pi = new int(i);
  2. popStack函数的value参数类型由(void*)改为(void**)。

【讨论】:

  • @Zhigalin,请用你的调试器确认错误发生在哪里。
  • 我用固定的源代码更新我的答案。此代码按预期编译并输出:21 22 20 ... 4 3 2 1
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-06
  • 1970-01-01
  • 1970-01-01
  • 2016-05-07
  • 2014-06-21
相关资源
最近更新 更多