为什么不能在结构体内声明函数?

3

我阅读了很多关于在结构体中声明函数的问题,但没有得到满意的答案。

我的问题不是关于“是否可以在结构体中声明函数?”

相反,我的问题是:

为什么不能在结构体中声明函数?


3
如果你想用 C 进行编程,那么这些就是你需要遵守的规则。 - herohuyongtao
1
这句话的意思是什么?结构体是数据的模板,它不指定值。 - Barmar
你可以在结构体中使用函数指针。 - Barmar
5个回答

10
这就是 C 和 C++(在 C++ 中有效)之间的根本区别。C++ 支持类(在 C++ 中,struct 是类的一种特殊情况),而 C 不支持。
在 C 中,你需要将类实现为一个结构体,并使用显式的 this 指针来调用函数,这基本上是 C++ 在幕后所做的。再加上合理的命名约定,以便知道哪些函数属于哪个类(这也是 C++ 在幕后进行名称重整的方式),你就可以接近基于对象编程,如果不是面向对象编程的话。例如:
typedef struct temp
{
    int a;

} classTemp ;

void classTemp_GetData( classTemp* pThis )
{
    printf( "Enter value of a : " );
    scanf( "%d", &(pThis->a) );
}

classTemp T ;

int main()
{
    classTemp_GetData( &T );
}

然而,如您所见,没有对类的语言支持,实现它们可能会变得繁琐。在C中,函数和数据结构基本上是裸露的;该语言提供了最少的支持来将数据结构组合在一起,并且没有(直接)为将函数与这些数据结构一起包含而提供支持。 C的目的是拥有一种尽可能直接地转换为机器代码的语言,更像是一种可移植的汇编语言,而不是像C ++这样的高级语言(并不是所有的C ++都是高级语言)。 C让您非常接近计算机,涉及大多数语言抽象掉的细节; 这样做的缺点是,在C中必须靠计算机进行编程才能充分利用该语言。 它采用了与C ++完全不同的编程方法,表面上的相似之处隐藏了它们之间的区别。点击这里获取更多信息(那里有精彩讨论)。
P.S.: 您也可以通过使用函数指针来实现功能,即在struct内部拥有一个指向函数的指针(作为变量)。
例如:
#include <stdio.h>

struct t {
    int a;
    void (*fun) (int * a);        // <-- function pointers
} ;

void get_a (int * a) {
    printf (" input : ");
    scanf ("%d", a);
}

int main () {
    struct t test;
    test.a = 0;

    printf ("a (before): %d\n", test.a);
    test.fun = get_a;
    test.fun(&test.a);
    printf ("a (after ): %d\n", test.a);

    return 0;
}

4
由于C标准不允许在结构体内声明函数/方法,因此不能在结构体中声明函数。C语言不是面向对象的语言。
6.7.2.1 结构体和联合体说明符:
一个结构体或联合体不得包含具有不完整或函数类型的成员(故而,结构体不得包含自身的实例,但可以包含指向自身实例的指针),但是,如果一个结构体具有多个命名成员,则最后一个成员可能具有不完整的数组类型;这样的结构体(以及任何包含可能递归地包含这样一个结构体成员的联合体)不得是结构体的成员或数组的元素。

这是不诚实的。我认为他想知道为什么C标准不允许它。因为它不允许并没有多大帮助。 - Barmar
@Barmar; 那我问你为什么C语言没有对象? - haccks
因为C语言不是面向对象的语言。 - Barmar
1
因为K&R设计了一种非常简单的语言,可以轻松编译成非常高效的代码,并且从源代码到目标代码的映射相对明显。面向对象的特性会使其变得复杂。 - Barmar
如果你想让C语言拥有更多的额外功能,那么你可能会选择C++。请注意,你可以在C++中编写非常类似于C语言的代码,并且只使用你想要的那些额外功能。 - user253751
显示剩余7条评论

0

我猜是因为在C语言中这样做没有太多意义。如果你在结构体内声明一个函数,那么你期望它与该结构体有某种关联,对吧?比如说,

struct A {
  int foo;
  void hello() {
    // smth
  }
}

你会期望hello()至少能够访问foo吗?否则,hello()只能得到类似命名空间的东西,所以我们要调用它,就必须写成A.hello() - 这将是一个静态函数,在C++中与普通的C函数没有太大区别。
如果hello()可以访问foo,那么必须有一个this指针来实现这种访问,在C++中,它总是作为第一个参数隐式地传递给函数。如果一个结构体函数可以访问结构体变量,那么它必须与其他函数的访问方式有所不同,以便为结构体内部的函数增加一些意义。因此,我们有了publicprivate修饰符。
接下来,虽然在C语言中没有继承(但可以模拟),但这是声明结构体内部函数时添加很多意义的内容。因此,我们需要添加virtualprotected
现在你可以添加命名空间和类,这就是C++的发明(好吧,没有模板)。
但是C++是面向对象的,而C不是。首先,人们创造了C,然后编写了大量程序,理解了一些可以进行的改进,然后,在我之前提到的推理的基础上,他们想出了C ++。他们没有改变C,而是将概念分开 - C是过程导向的,而C++是面向对象的。

0

我想可能有很多原因,以下是其中几个:

  1. C编程语言于1972年创建,受到纯汇编语言的影响,因此struct被认为是“仅数据”的元素

  2. 由于C不是面向对象的语言,所以在“数据结构”内定义函数实际上没有意义,不存在构造函数/方法等实体

  3. 函数直接转换为纯汇编pushcall指令,并且没有像this这样的隐藏参数


0

C语言的设计是为了能够使用相对简单的编译系统进行处理。如果允许函数定义出现在其他任何地方,编译器在处理时就必须跟踪函数所出现的上下文。由于结构体、联合体或枚举声明的成员直到声明结束才进入作用域,因此在结构体内声明的函数与在其他地方声明的函数没有什么区别。

从概念上讲,允许在结构体内部声明常量可能会很好;指向文字函数的常量指针将是其中的一个特例(即使函数本身必须在其他地方声明),但这将要求C编译器为每个结构体成员跟踪更多的信息--不仅是它的类型和偏移量,还包括它是否是常量以及如果是常量,则其值应该是什么。这样的事情在今天的编译环境中并不困难,但早期的C编译器预计要运行比今天可用的内存少几个数量级。

鉴于C语言已经扩展到可以处理64K(或更小)系统上运行的编译器无法很好处理的许多功能,因此可以合理地认为它不再受到这些限制的约束。实际上,有一些C语言遗产方面我希望失去,例如整数提升规则要求即使是新的整数类型也必须遵循旧类型的不一致规则,而不允许新类型具有明确定义的行为[例如,有一个wrap32_t,可以转换为任何其他整数类型而无需进行类型转换,但当添加到任何“非包装”整数类型的任何大小时,将产生一个wrap32_t]。在结构体内定义函数可能很好,但它在我的改进列表中排名相对较低。


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