【问题标题】:Passing arguments generically in C在 C 中一般地传递​​参数
【发布时间】:2011-06-07 08:26:01
【问题描述】:

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

我有两个结构

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 传递给函数。我尝试了很多不同的方法,但 gcc 不断发出警告:

  • 赋值使指针从整数 没有演员表
  • 从指针转换为 不同大小的整数

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

谢谢!

【问题讨论】:

  • Void 表示“任何东西”。 void * 表示指向任何东西的指针。 Vector_value_int 看起来像是一个 int 而不是指向 int 的指针。

标签: c vector struct generics


【解决方案1】:

首先,让我们忽略所有类型定义;这是它们遮蔽多于照亮的罕见情况之一。

这是来自 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]);

这是一个假设的内存映射,显示了每个的值(假设整数和浮点数为 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++ 来说不是这样,它确实需要显式转换)。这是您仍然看到人们投射 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 的一维指针数组,能够保存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);
  ...
}

我们会继续将相同的地址 (&amp;vector_value_int) 添加到数组中,这不是我们想要的。我们想复制 vector_value_int 中的内容,但同样,我们不知道如何做到这一点,因为我们不知道 objectPtr 中的内容的类型指向。

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

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-&gt;elements[i] 指向什么类型。不知何故,这些信息需要作为数组的一部分保存。

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

【讨论】:

  • 非常好的解决方案,您实际上解决了我之前的答案都没有真正解决的问题。谢谢。
【解决方案2】:

类型 void 只是一种说法:“它没有类型”。所以一个 void 指针是指向内存的一部分而不知道里面有什么的指针。所以 Element = 没有有意义的类型。

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

Add_To_Vector(vector, &vector_value_int, index);

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

当你有一个指针并且你想获取它指向的内存的内容时,你应该使用操作符 *。

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

由于类型为 void,您应该将其转换为 (int) 以将其解释为整数。

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

实际例子:

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

【讨论】:

  • 如何在 Add_To_Vector 中使用元素?当我打印它的值时,我得到一个类似于内存地址的数字。
  • 你能把代码写下来吗?我不明白你在说什么。我正在尝试您提供的解决方案,但它仍然不起作用。你能演示一个简单的“printf”调用吗?
  • 认真的吗?对不起,我无法破译。我会继续努力,直到我做对为止。
  • 现在在答案中正确格式化了它。更容易理解?
  • 并非如此。如果你刚刚写了会容易得多: Add_To_Vector(...) { // 里面的一切 }
【解决方案3】:

我将从底部开始:

typedef void Element

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

【讨论】:

    【解决方案4】:
    typedef Element void;
    

    ..是“void”类型的别名,它根本不是类型。该函数接受一个指向元素的指针,实际上是一个 void 指针。 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 进行比较。

    第一条错误信息也给了你一个重要的提示。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-18
      • 2010-10-28
      相关资源
      最近更新 更多