1

所以我在普通 C 中实现了一个通用堆栈。它应该复制不同类型的数据,包括结构。通过结构我有问题。

所以这里是栈的结构:

/*
 * Definite genStack as a structure.
 * Pointer elems points to the objects lying on the stack
 * The variable elemSize spiecifies the size of an element
 * The variable logLength specifies the number of actually
 * lying on the stack objects
 * The variable allocLenght specifies the allocated size
 */

typedef struct{
void* elems; 
int elemSize; 
int logLength; 
int allocLength;
}genStack;

推送和弹出功能:

void GenStackPush(genStack *s, const void *elemAddr)
{
    /* if stack is full - allocates more memory */
    if (GenStackFull(s))
    {
        GenStackAlloc(s, s->elemSize);
    }
    memcpy((char*) (s->elems)+(s->logLength), elemAddr, sizeof(*elemAddr));
    s->logLength++;
}

void GenStackPop(genStack *s, void *elemAddr)
{
      if(GenStackEmpty(s))
      {
        fprintf(stderr, "Can't pop element from stack: stack is empty.\n");
      } else
      {
        s->logLength--;
        memcpy((void*) elemAddr, (s->elems)+(s->logLength), sizeof(s->elems[s->logLength]));
      }
}

简单结构测试:

gentest.h:

#ifndef GENTEST1_H
#define GENTEST1_H

typedef struct {
  char* name;
  int age;
  char gender;
}person;

#endif

gentest.c:

#include <stdio.h>
#include <stdlib.h>
#include "gentest1.h"
#include "genstacklib.h"

int main(int argc, char* argv[])
{

  genStack StructStack;
  person testPerson[5];
  person* newPerson;
    person* test;
  int i;

  newPerson = (void*) malloc (sizeof(person));

  testPerson[0].name = "Alex";
  testPerson[0].age = 21;
  testPerson[0].gender = 'm';

  testPerson[1].name = "Vanja";
  testPerson[1].age = 20;
  testPerson[1].gender = 'm';

  testPerson[2].name = "sjrgsde";
  testPerson[2].age = 11;
  testPerson[2].gender = 'w';

  testPerson[3].name = "wergsggsd";
  testPerson[3].age = 99;
  testPerson[3].gender = 'y';

  testPerson[4].name = "adaasxx";
  testPerson[4].age = 13;
  testPerson[4].gender = 'g'; 


  GenStackNew(&StructStack, sizeof(person));
    printf("sizeof(person) = %lu\n", sizeof(person));

  for (i = 0; i < 5; i++) {
    newPerson = &testPerson[i];
      GenStackPush(&StructStack, newPerson);
    printf("Pushed: %s, %d, %c\n", newPerson->name, newPerson->age, newPerson->gender);
  } 

test = (void*) malloc (sizeof(person));
test->name = "test";
test->age = 0;
test->gender = 't';
  while(!GenStackEmpty(&StructStack))
  { 
      GenStackPop(&StructStack, test);
      printf("Popped: %s, %d, %c\n", test->name, test->age, test->gender);
  }

  GenStackDispose(&StructStack);
  return 0;
}

这是我得到的输出:

./gentest1
elemSize = 16   GenStackInitialAlocationSize = 4
sizeof(person) = 16
Pushed: Alex, 21, m
Pushed: Vanja, 20, m
Pushed: sjrgsde, 11, w
Pushed: wergsggsd, 99, y
    New size of alloc = 8
Pushed: adaasxx, 13, g
Popped: adaasxx, 0, t
Popped: wergsggsd, 0, t
Popped: sjrgsde, 0, t
Popped: Vanja, 0, t
Popped: Alex, 0, t

如您所见,我可以接收姓名,但不能接收年龄或性别。我尝试了很多选项,但仍然得到 Segmentation Fault 或上面的输出。目前,上面的输出是我得到的最好的输出,但仍然不是我想要的。

问题是 - 我怎样才能得到我需要的输出?提前致谢。

为了避免一些问题: sizeof(person) = s->elemSize

它是通过创建堆栈来定义的:

genstacklib.c:

void GenStackNew(genStack *s, int elemSize)
{
    void* newElems;

    /* Allocate a new array to hold the contents. */
    newElems = (void*) malloc(elemSize * GenStackInitialAlocationSize);
    printf("elemSize = %d\tGenStackInitialAlocationSize = %d\n",
              elemSize, GenStackInitialAlocationSize);
    if (newElems == NULL)
    {
        fprintf(stderr, "Error with allocating the stack.\n");
        exit(1); /* Exit, returning error code. */
    }
    s->elems = newElems;
    s->elemSize = elemSize;
    s->allocLength = GenStackInitialAlocationSize;
    s->logLength = 0; /*is empty*/

}

gentest.c:

GenStackNew(&StructStack, sizeof(person));
printf("sizeof(person) = %lu\n", sizeof(person));
4

4 回答 4

1

您的 push 函数正在复制sizeof(*elemAddr),即 a void *,因此它具有指针的大小而不是结构的 inteded 大小person。所以你可能只复制前 4 个字节

于 2012-04-09T13:16:15.163 回答
0

你没有elemSize在所有相关的地方使用......

于 2012-04-09T13:15:12.027 回答
0

如上所述,推送正在复制错误大小的数据。应该是elemSizememcpy也覆盖了自己的数据。像这样的东西应该工作。

memcpy((char*) (s->elems)+(s->logLength)*elemSize, elemAddr, elemSize); s->logLength++;

于 2012-04-09T15:51:12.667 回答
0
void GenStackPush(genStack *s, const void *elemAddr) 
{
  ...     
  memcpy((char*) (s->elems)+(s->logLength), elemAddr, sizeof(*elemAddr));
                                                      ^^^^^^^^^^^^^^^^^

这是非常错误的;表达式的类型*elemAddrvoid,这是违反约束的(sizeof不能在不完整类型的表达式上调用,并且void是不完整类型)。您将需要提高编译器的警告级别。我编写了一个测试程序来计算和sizeof类型的表达式,我收到了一个警告。如果我放弃我不会收到警告,但我得到的结果是 1,我很确定这不是a 的大小。你为什么不在这里使用? void *voidgcc -pedantic-pedanticsizeof (void)persons->elemSize

其次,你为什么要s->elems投到char *

编辑

如果我可以提供一些建议,我过去曾使用过一些通用容器,以下是我得到的经验教训:

首先,将所有类型感知操作(分配、解除分配、复制、比较、显示等)委托给单独的函数,这些函数通过作为参数传递给通用容器函数的函数指针调用;即,推送将被定义为

GenStackPush(genStack *stack, const void *data, void *(*copy)(const void *))
{
  stack->elems[++stack->logLength] = copy(data);
}
...
void *myIntCopyFunc(const void *data)
{
  const int *inputData = (const int *) data;
  int *copy = malloc(sizeof *copy);
  if (copy)
    *copy = *inputData;
  return copy;
}
...
GenStackPush(&myIntStack, &intVal, myIntCopyFunc);

您的类型存在的一个问题person是您没有对成员进行深层复制name;您只是将指针值复制到堆栈中。在这种情况下,这没什么大不了的,因为您使用的是字符串文字,但是如果您使用的是 local char [],那么您就会遇到问题。通过为每种类型编写单独的复制函数,您可以处理这类问题,而不是尝试在容器函数本身中进行一刀切的分配。

其次,不要直接调用你的通用容器函数;在你和容器之间放置一个类型感知接口(基本上是穷人版本的函数重载):

void pushInt(GenStack *stack, int intVal)
{
  GenStackPush(stack, &intVal, myIntCopyFunc);
}
...
genStack myIntStack;
...
pushInt(&myIntStack, 5);

这给您带来两个好处;首先,它允许您将文字值作为参数传递(对于 type 的参数,您不能这样做void *)。其次,它为您提供了一种在容器上强制执行类型安全的方法。您不能以这种方式意外推送错误类型的值。

这是很多额外的工作吗?哦,是的。要使通用容器类型正常工作,必须在幕后发生很多魔术。如果您尝试复制与 C++std::stack容器类型相同的功能,您将编写大量代码。

于 2012-04-09T15:58:21.087 回答