通常在C中传递参数

时间:2011-01-11 18:01:42

标签: c vector struct generics

这与家庭作业有关,但这似乎是一个常见的问题,因此问题就在这里。

我有两个结构

typedef struct Vector {
    Element **elements;
    int top;
    int max_size;
} Vector;
typedef void Element;

向量由元素组成,元素可以是任何类型,原始或不是。

使用此函数向向量添加值(尚未编写)。

void Add_To_Vector(Vector *old_vector, Element *element, int index) {
    // create a new vector. loop throught the elements in old vector and 
    // assign each to a newly created vector. return new vector.
}

在主要部分

scanf("%d %d", &vector_value_int, &index);
Add_To_Vector(vector, vector_value_int, index);

不是将vector_value_int传递给函数,而是需要传递一个元素指针(不知何故将point元素传递给输入的值)。我尝试了很多不同的东西,但是gcc不断发出警告:

  • 赋值使整数指针 没有演员
  • 投射到指针 不同大小的整数

如何使用元素将该参数或任何类型参数传递给函数?对于“typedef void Element”的含义,我也很遗憾。它存储数据吗?它需要内存分配吗?

谢谢!

4 个答案:

答案 0 :(得分:2)

类型void只是一种说法:'它没有类型'。因此,void指针是指向内存的一部分的指针,而不知道其中的内容。所以Element =没有意义的类型。

现在,要传递指向vector_value_int的指针,您应该使用运算符&

Add_To_Vector(vector, &vector_value_int, index);

& variable =变量的l值(地址)。

如果你有一个指针,并且你想获得它所指向的内存的内容,你应该使用运算符*。

* pointer =存储在指针中包含的地址中的值。

这里类型为void,你应该将它强制转换为(int)以将其解释为整数。

如果要在结构中存储某些内容,应使用malloc分配vector_value_int,因为如果不是,则该变量将存储在堆栈中,并且当声明它的过程完成时,该地址将被释放并且不确定结果可能会出现。

实际例子:

void add_to_vec(Vector *oldvector, Element *element, int pos) {
     printf("%d\n", *((int*)element));
     //iterate over the oldvector->elements
     //and create the new one
}

int main() {
     Vector vec;
     int x = 3; // this should be done with malloc
     add_to_vec(&vec, (Element*)&x, 0);
}

答案 1 :(得分:2)

首先,让我们忽略所有的typedef;这是一种罕见的情况,它们掩盖的不仅仅是它们的照明。

以下是C语言标准{草稿n1256)中void数据类型的定义:

6.2.5类型
...
19 void类型包含一组空值;它是一种不完整的类型,无法完成。

IOW,void是一种“不属于上述”的数据类型;它没有相关的值。类型为void的函数没有返回值,不应使用void表达式的(不存在)结果。

与任何其他不完整类型一样,您可以创建指向void的指针。它们作为一种通用指针类型; void指针可用于指向任何其他类型的对象:

6.3.2.3指针

1指向void的指针可以转换为指向任何不完整或对象类型的指针。指向任何不完整或对象类型的指针可以转换为指向void并再次返回的指针;结果应该等于原始指针。

因此,例如,我可以使用void指针指向int,float或char数组等:

int x = 1;
double y = 2.0;
char *z = "three";

void *p[3];

p[0] = &x;
printf("address of x is %p\n", p[0]);
p[1] = &y;
printf("address of y is %p\n", p[1]);
p[2] = &z;
printf("address of z is %p\n", p[2]);

这是一个假设的内存映射,显示每个的值(假设为int和浮点数为32位,字符数据为ASCII):

Item        Address      0x00  0x01  0x02  0x03
----        -------      ----  ----  ----  ----
"three"     0x00001000   0x74  0x68  0x72  0x65  // string literals are stored "somewhere else"
            0x00001004   0x65  0x00  xxxx  xxxx  // xxxx indicates any random value
            ...
   x        0x10008000   0x00  0x00  0x00  0x01
   y        0x10008004   0x40  0x00  0x00  0x00  // Binary representation of 2.0 on my system
   z        0x10008008   0x00  0x00  0x10  0x00  // z contains the address of the first element of the string literal
   p        0x1000800C   0x10  0x00  0x80  0x00
            0x10008010   0x10  0x00  0x80  0x04
            0x10008014   0x10  0x00  0x80  0x08

如您所见,p[0](地址0x10008010)中存储的值是x0x10008000)的地址。同样,p[1]存储y的地址,p[3]存储z的地址。 void指针的这个属性为我们提供了一种从代码中分离类型信息的方法,尽管不像C ++模板或Java泛型那样简单。因此,我们可以使用void指针数组来构建一个通用容器,该容器可以容纳任何类型的对象(技术上,指向对象的指针)。

注意:在采用1989标准之前,你会使用 char * 作为“通用”指针类型;但是,您必须显式地将指针值强制转换为目标类型,而不必使用void指针(在C中;对于C ++而言,这不是真的,它确实需要explcit强制转换)。这是你仍然看到人们投射 malloc/calloc/realloc 的结果的部分原因;曾几何时,你到。

那么让我们看看你的数据结构和代码,但最初没有typedef:

struct Vector {                               
  void **elements;                               
  int top;
  int max_size;
};

void **elementselements声明为指向void的指针;在这种情况下,我们将动态分配void指针的向量。这与上面示例中的数组p类似。

void allocateVector(struct Vector *vector, int maxSize)
{
  vector->elements = malloc(sizeof *vector->elements * maxSize);
  if (vector->elements)
    vector->maxSize = maxSize;
}

我们现在有一个指向void的1-d指针数组,能够容纳maxSize个元素。现在我们可以编写Add_To_Vector函数:

void Add_To_Vector(struct Vector *vector, void *objectPtr, int index)
{
  if (vector->elements)
  {
    if (index < vector->maxSize)
    {
      vector->elements[index] = objectPtr;
    }
  }
}
然而,有两个非常大的问题。首先,我们无法确定指针本身所指向的对象的类型;它只是一个地址,没有描述被指向对象的信息。如果我们想要打印矢量中所有对象的值,我们就不知道要使用哪个转换说明符。其次,如果我们按如下方式构造代码:

while (!done)
{
  scanf("%d %d", &vector_value_int, &index);
  Add_To_Vector(&vector, &vector_value_int, index);
  ...
}

我们会继续向数组添加相同的地址(&vector_value_int),这不是我们想要的。我们想要对vector_value_int中的内容进行复制,但是我们还没有设置知道如何执行此操作,因为我们不知道objectPtr的类型{1}}指向。

解决这个问题的一个常见方法是使用指针信息传递所谓的回调函数;回调知道如何制作指向值的副本。例如:

void *copyIntValue(void *objectPtr)
{
  int *copy = malloc(sizeof *copy);
  if (copy)
    *copy = *(int *) objectPtr;
  return copy;
}

void Add_To_Vector(struct Vector *vector, void *objectPtr, void *(*cpyFunc)(void *), int index)
{
  if (vector->elements)
  {
    if (index < vector->maxSize)
    {
      vector->elements[index] = cpyFunc(objectPtr);
    }
  }
}

...
while (!done)
{
  scanf("%d %d", &vector_value_int, &index);
  AddToVector(&vector, &vector_value_int, copyIntValue, index);
}

我们已经消除了第二个问题,但不是第一个问题;我们仍然不知道vector->elements[i]指向哪种类型。不知何故,该信息需要作为数组的一部分保留。

我想我现在打算破解论坛软件,所以我将把它留作以后的练习。

答案 2 :(得分:1)

我将从底部开始:

typedef void Element

为现有数据类型(void)创建新名称(Element)。实际上,在编译之前,代码中“Element”的任何位置实际上都替换为“void”。你需要做的就是解决你的问题就是把你的(可能是自动/堆栈存储)vector_value_int变量,用它来创建一个新的(基于堆的,由你的向量管理)整数,然后使用指向它的指针新创建的整数,转换为Element *,以传递到Add_To_Vector函数。

答案 3 :(得分:0)

typedef Element void;

..是“void”类型的别名,它根本就没有类型。该函数采用指向Element的指针,实际上是 void指针。无效指针基本上可以指向任何东西,但有明显的警告:你丢失了类型信息。

实施例

// manipuating myint requires no knowledge of the underlying type, 
// it's right there in the declaration.
int myint = 10;
myint += 5;

// manipulating data probably requires knowledge of the underlying type.
// note: it points to the same data as myint.
void *data = &myint;
// we need to cast it to a type (usually a pointer to the original type)
int *data_as_int = (int *) data;
*data_as_int += 5;

// myint is now 20.

这意味着您需要将void / Element指针传递给Add_To_Vector。如果仔细查看对Add_To_Vector的来电,您可能会发现为什么会收到错误。与您对scanf的通话比较。

第一条错误消息也为您提供了重要的提示。