在C语言的参数列表中,...代表什么?

39

我看到了下面的函数签名,我想知道这个(省略号或者"...")是不是一种多态?

#include <fcntl.h>
int fcntl(int fd, int cmd, ... );

提前感谢。


4
公平的问题。+1。尽管有权力机构的指示表明简单的问题是适宜的,但仍有些人认为不适合提出。 - paxdiablo
我建议将问题的标题更改为“C语言中的省略号(...)是什么?” - Hosam Aly
@Hosam:是的,这并不是关于多态性的问题... 标题已更改。 - Dave Sherohman
@Dave Sherohman:这也不是关于省略号的问题。 - Anonymous
你为什么改了标题 - 应该清楚地表明这个问题与多态有关,尽管C不支持它。 - Anonymous
1
很明显,你看到省略号就认为它意味着多态性。但实际上并不是这样的。第二个问题可能是:“既然省略号不表示多态性,那么有没有其他方法可以实现它?” 这将是一个单独的问题。 - John Saunders
14个回答

24

17

12
... 表示你可以向此函数传递任意数量的参数,正如其他评论者已经提到的。由于可选参数没有类型,编译器无法检查类型,因此你可以在技术上传递任何类型的任何参数。
那么这是否意味着你可以使用它来实现某种多态函数?(即,根据其参数的类型执行某些操作的函数。)
不是的。
之所以不能这样做,是因为你无法在运行时检查传入参数的类型。读取可变参数列表的函数需要预先了解它将要接收的可选参数的类型。
对于一个确实应该能够接受任意数量任意类型参数的函数(例如printf),参数的类型通过格式字符串传递。这意味着调用者必须在每次调用时指定它将要传递的类型,从而消除了多态函数的好处(即调用者也无需知道类型)。
比较:
// Ideal invocation
x = multiply(number_a, number_b)
y = multiply(matrix_a, matrix_b)

// Standard C invocation
x = multiply_number(number_a, number_b)
y = multiply_matrix(matrix_a, matrix_b)

// Simulated "polymorphism" with varargs
x = multiply(T_NUMBER, number_a, number_b)
y = multiply(T_MATRIX, matrix_a, matrix_b)

在可变参数函数可以正确执行之前,您必须指定类型,因此这样做并没有任何好处。


8
不,你看到的是“省略号”,假设你指的是声明的...部分。
基本上它表示该函数在前两个指定参数后接受未知数量的参数。
必须以这样的方式编写函数,以便它知道需要什么,否则会产生奇怪的结果。
对于支持此功能的其他函数,请查看printf函数及其变体。

1
此外,参数的类型也是未知的。在函数中,您只能获得第一个参数的指针,您必须自己将其转换为有用的内容。 - Aaron Digulla

6

C语言支持多态吗? 不支持。

然而,有几个库,比如Python C API,使用结构体和指针实现了多态的粗略变体。请注意,大多数情况下编译器无法执行适当的类型检查。

这种技术很简单:

typedef struct {
    char * (*to_string)();
} Type;

#define OBJ_HEADER Type *ob_type

typedef struct {
    OBJ_HEADER;
}  Object; 

typedef struct {
    OBJ_HEADER;
    long ival;        
} Integer;

typedef struct {
    OBJ_HEADER;
    char *name;
    char *surname;
} Person;

整数和人员使用适当的函数指针(例如integer_to_string和person_to_string函数)获取类型对象。

现在只需声明一个接受Object *的函数:

void print(Object *obj) {
    printf("%s", obj->type->to_string());
}

现在您可以同时使用整数和人员来调用此函数。
Integer *i = make_int(10);
print((Object *) i);

Person *p = make_person("dfa");
print((Object *) p);

编辑

或者您可以将i和p声明为Object *; 当然,make_int和make_person将为Integer和Person分配空间并进行适当的转换:

Object * 
make_integer(long i) {
     Integer *ob = malloc(sizeof(Integer));
     ob->ob_type = &integer_type;
     ob->ival = i;
     return (Object *) ob;
}

注意:我现在无法编译这些示例,请仔细检查。

我遇到了下面的函数签名,我想知道这个省略号(或“...”)是一种多态吗?

是的,它是一种原始形式的多态。只有一个函数签名,您就可以传递各种结构。但是编译器无法帮助您检测类型错误。


你的函数 print 需要接受一个 void* 类型的参数,然后你需要显式地将它转换为 Object* 类型,以使你的示例正常工作。 - Konrad Rudolph
-1,因为我认为这并不是原问题的实质,尽管标题中有“多态性”一词。这个答案更适合一个问题:我们如何在C中实现多态性。 - John Saunders

4
补充之前的内容:C通过其他方式支持多态性。例如,考虑标准库中的 qsort 函数,它可以对任意类型的数据进行排序。
它通过使用无类型(void)指针来完成这个功能。同时,需要知道要排序的数据的大小(通过 sizeof 提供),以及比较对象顺序的逻辑。这通过向 qsort 函数传递函数指针来实现。
这是运行时多态性的典型例子。
还有其他方法可以实现面向对象的行为(特别是虚函数调用),通过手动管理虚函数表来完成。这可以通过将函数指针存储在结构中并传递它们来完成。许多API都会这样做,例如WinAPI,甚至使用高级的面向对象方面,例如基类调用分派( DefWindowProc ),以模拟调用基类的虚方法。

-1:Konrad,我认为这是对多态标准定义的极大扩展。它实际上只是如何处理未经类型化的数据。这是我们回归到黑暗、未经类型化的过去——在面向对象出现之前,我们需要这样做的方式。 - John Saunders
2
John,抱歉但你是错的,我是正确的。这里没有扩展,也没有所谓的“标准”定义,多态的通常定义在实现它的技术方面是完全不具体的。 - Konrad Rudolph
有许多不同形式的多态性,不仅仅是通过继承实现的子类型多态性。参数多态性(例如模板)和我认为传递函数指针也是参数多态性,尽管我对这些术语不太熟悉。然后还有重载函数的静态多态性。我认为Konrad是正确的。 - Johannes Schaub - litb

2
我猜您指的是省略号(...)? 如果是,则表示将跟随0个或多个参数。这被称为varargs,在stdarg.h中定义。 printf使用此功能。如果没有它,您将无法继续向函数末尾添加参数。 http://msdn.microsoft.com/en-us/library/kb57fad8.aspx

1
我建议从URL中删除“(VS.71)”后缀,这样它将始终链接到文档的最新版本。 - Hosam Aly

1

C语言支持一种简单的多态形式,即一种类型能够以另一种类型的方式出现和表现。它在底层上与C++类似(依赖于内存对齐),但您需要通过强制转换来帮助编译器。例如,您可以定义一个结构体:

typedef struct {
     char forename[20];
     char surname[20];
     } Person;

然后是另一个结构体:

    typedef struct {
             char forename[20];
             char surname[20];
             float salary;
             char managername[20];
             } Employee;

那么

int main (int argc, int *argv)
{
    Employee Ben;
    setpersonname((Person *) &Ben);
}

void setpersonname(Person *person)
{
   strcpy(person->forename,"Ben");
}

以上示例展示了将Employee用作Person的情况。

我仍然认为这真的是在拉伸术语。是的,Employee 像 Person 一样,因为 C 中的每个东西都像一系列字节,上面有一些小的语义映射。你也可以说 FORTRAN 中的 COMMON 块代表多态性,或者系统的完全无类型地址空间,由汇编器访问。 - John Saunders
是的,它不支持编译器的“类型多态”,根据计算机科学词典的定义,可以有不同的争议。我想我展示了C语言的最佳实践。 - monibius
不要忘记,如果您要以这种方式进行操作,可以使用联合结构定义Employee。这将强制执行常见的字节顺序! - maxwellb

0
不是,它是一个接受可变数量参数的函数。

0

那不是技术上的多态性。fcntl接受可变数量的参数,这就是为什么它与printf函数类似的原因。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接