这两个结构声明有什么区别?

What is the difference between these two structure declarations?

我对来自不同教程的这两个结构感到困惑:

typedef struct complex {
    float real;
    float imag;
} COMPLEX;

typedef struct {
    float real;
    float imag;
} COMPLEX;

COMPLEX c1;

他们都正确吗?为什么? struct前是否需要加complex的小写?一般情况如何?

对于第一个,您可以使用类型别名 COMPLEXstruct complex

第二个你有一个匿名结构,它只能与类型别名一起使用 COMPLEX

也就是说,在 C++ 中,任何结构名称也是类型名称,可以直接用作类型:

struct complex { ... };
complex c1;
typedef struct complex{
    float  real;
    float imag;
}COMPLEX;

这是一个 struct complex,已被类型定义为 COMPLEX。您现在可以同时使用 struct complex c1;COMPLEX c1;。当你标记这个问题 C++ 时,你也可以使用 complex c1; 作为 struct (因此, typedef 来避免它)在 C++ 中不是必需的。

typedef struct {
    float  real;
    float imag;
}COMPLEX;

这是一个未命名结构,类型定义为COMPLEX。您现在可以使用 COMPLEX c1;.

到目前为止,这两个 (C) 构造之间的差异。

当你用 C++ 标记这个问题时,正确的 做法是使用 C++ header <complex>:

std::complex< float > c1;

这确实是因为类型系统在 C 和 C++ 之间发生了微妙的变化。 C 中的用户定义结构(和枚举)具有“标签”,这些标签被视为与类型不同。

struct Tag {/*some members*/};
typedef struct Tag Type; /* Tag and Type can be the same name */

如您的示例所示,这种工作方式仍然受支持,但 C++ 添加了一条新规则,即结构标记也始终是类型。因此,从 C 的角度来看,就好像 C++ 编译器为您添加了 typedef。因此,在 C++ 中,您不再需要在声明结构类型的变量之前说“struct”。

所以是的,两种方式都是“正确的”,一种是旧的 C 方式,可以在 C 和 C++ 中工作,另一种是原生 C++ 方式,不会在 C 中编译。你的例子可能是部分代码需要 C 兼容性,但不是全部。

如果您需要 C 兼容性,您应该使用上面的模式,但将它用于本机 C++ 代码并不常见。

对于初学者来说,没有其他人在他们对您的问题的回答中所写的匿名结构。有一个别名为 COMPLEX

的未命名结构(没有标签名称的结构)
typedef struct {
    float real;
    float imag;
} COMPLEX;

在 C++ 中,术语未命名 class/structure 记录为(C++ 14 标准,9 类)

A class-specifier whose class-head omits the class-head-name defines an unnamed class.

至于匿名结构的概念,它在 C 中的定义如下(6.7.2.1 结构和联合说明符,p.#1)

13 An unnamed member of structure type with no tag is called an anonymous structure; an unnamed member of union type with no tag is called an anonymous union. The members of an anonymous structure or union are considered to be members of the containing structure or union. This applies recursively if the containing structure or union is also anonymous.

这是一个匿名结构的例子

struct A
{
    struct { int x; int y; };  // <== anonymous structure
    int z;
};

请记住,在与 C 相对的 C++ 中,没有匿名结构这样的概念。 C++中只有匿名联合的概念。

关于你的问题

Are they both correct? Why? and is it necessary to add the lowercase of complex before struct? What is general situation?

那么两个 typedef 声明都是正确的。并且不需要使用完全小写的名称 complex 作为结构标记。您也可以使用大写名称 COMPLEX 作为结构标记。

typedef struct COMPLEX {
    float real;
    float imag;
} COMPLEX;

在此结构声明中

typedef struct {
    float  real;
    float imag;
}COMPLEX;

声明了一个没有标签名的结构。所以你只能通过它的别名 COMPLEX.

来引用它

在 C 结构中,标签名称有自己的名称 space,不会与其他名称中的标识符名称冲突 spaces。

来自 C 标准(6.2.3 Name spaces of identifiers)

1 If more than one declaration of a particular identifier is visible at any point in a translation unit, the syntactic context disambiguates uses that refer to different entities. Thus, there are separate name spaces for various categories of identifiers, as follows:

— the tags of structures, unions, and enumerations (disambiguated by following any32) of the keywords struct, union, or enum);

例如考虑以下程序。

#include <stdio.h>

typedef struct {
    float  real;
    float imag;
} COMPLEX;

int main(void) 
{
    int COMPLEX;
    COMPLEX c = { 0.0f, 0.0f };
    
    return 0;
}

编译器会报错,因为局部变量 COMPLEX 的声明隐藏了文件作用域中声明的未命名结构的别名。

但是如果你会写

#include <stdio.h>

typedef struct COMPLEX {
    float  real;
    float imag;
} COMPLEX;

int main(void) 
{
    int COMPLEX;
    struct  COMPLEX c = { 0.0f, 0.0f };
    
    return 0;
}

那么局部变量的名字COMPLEX就不会和结构体的标签名COMPLEX冲突。 C 编译器只能发出声明的变量未被使用的消息。

另一个重要区别是有时您需要在结构定义本身中引用声明的结构说明符。

例如,如果你想在 C 中声明一个单链表,你需要这样写

typedef struct Node
{
    int data,
    struct Node *next;
} Node;

没有像

这样的标签名称
typedef struct 
{
    int data,
    struct Node *next;
} Node;

编译器将此结构定义视为两个不同类型说明符的声明:别名 Node 的未命名结构和标记名称 Node 的另一个结构。这些类型不兼容。

至于 C++ 那么你可以使用结构标签的名称而不指定关键字 struct

例如

struct complex{
    float  real;
    float imag;
};

struct complex c1;
complex c2;

您也可以在其定义中引用结构说明符而不使用关键字 struct 因为(C++ 15 标准,9 类)

2 A class-name is inserted into the scope in which it is declared immediately after the class-name is seen. The class-name is also inserted into the scope of the class itself; this is known as the injected-class-name.

例如

struct Node
{
    int data,
    Node *next;
};

但是变量或函数的声明可以隐藏结构的声明。在这种情况下,您需要使用关键字 struct。这种使用带有关键字 struct 的名称在 C++ 中称为详细类型说明符。

例如

struct complex{
    float  real;
    float imag;
};

int complex;
struct complex c;

如果不指定关键字 struct,编译器将发出错误,因为整型变量的声明隐藏了结构的声明。

注意很多程序员甚至不知道C和C++中的typedef也可以这样重写

struct COMPLEX {
    float  real;
    float imag;
} typedef COMPLEX;

结构定义如下:

struct type_name {
       member_type1 member_name1;
       member_type2 member_name2;
       .
       .
                } object_names;

您对结构名称和对象名称使用同一个名称。您可以在结构定义的一侧使用对象而无需为 struct

声明名称

没有什么比 C/C++ 语言更好的了。

在C.

   typedef struct complex {
        float real;
        float imag;
    } COMPLEX;

    COMPLEX c1;
    struct complex c2;

别名 struct complex 以键入 COMPLEX

   typedef struct{
        float real;
        float imag;
    } COMPLEX;

    COMPLEX c1;

别名无标记结构以键入 COMPLEX

实际区别是什么?

如您所见,使用第一个可以使用新类型 COMPLEXstruct complex.

定义新对象

第二个类型声明只允许使用复杂类型的定义。

在 C++ 中,只有细微的差别。它是 C 的遗留物,在其中有所作为。

C 语言标准 (C89 §3.1.2.3, C99 §6.2.3, and C11 §6.2.3) 要求为不同类别的标识符使用单独的命名空间,包括标记标识符(对于 struct/union/enum)和普通标识符(对于 typedef 和其他标识符) .

如果你刚才说:

struct Foo { ... };
Foo x;

您会遇到编译器错误,因为 Foo 仅在标记命名空间中定义。

您必须将其声明为:

struct Foo x;

任何时候你想引用 Foo,你总是必须称它为 struct Foo。这很快就会变得烦人,所以你可以添加一个 typedef:

struct Foo { ... };
typedef struct Foo Foo;

现在struct Foo(在标签命名空间中)和普通的Foo(在普通标识符命名空间中)都指的是同一个东西,你可以自由声明类型为[=16的对象=] 没有 struct 关键字。

结构:

typedef struct Foo { ... } Foo;

只是声明的缩写,typedef.

最后,

typedef struct { ... } Foo;

声明一个匿名结构并为其创建一个typedef。因此,使用此构造,它在标记命名空间中没有名称,只有 typedef 命名空间中的名称。这意味着它也不能提前声明。 如果要进行前向声明,则必须在标签命名空间中为其命名

在 C++ 中,所有 struct/union/enum/class 声明的行为就像它们被隐式 typedef'ed,只要名称没有被另一个同名声明隐藏。

想想这里的“命名空间”:结构名称存在于它们自己的命名空间中。

struct complex {
    float real;
    float imag;
};

struct complex myvar; // in C and C++
complex myvar // only in C++!

C++ 还解析结构标签(如果不可能的话),而 C 不解析。 typedef 定义类型名的别名,即

typedef struct complex COMPLEX;

将定义一个 new 名称 COMPLEX,它是 struct complex.

的替代品

这个声明

typedef struct complex {
    float real;
    float imag;
} COMPLEX;

对于

是shorthand
struct complex {
    float real;
    float imag;
};
typedef struct complex COMPLEX;

您也可以完全省略 struct-tag

typedef struct {
    float real;
    float imag;
} COMPLEX;

就像在

struct {
    float real;
    float imag;
} myvar;

定义了一个未命名结构类型的变量myvar