这个指向结构定义类型的指针在C语言中是什么意思?

22

在K&R第六章中,提到了一个声明:

struct{
    int len;
    char *str;
} *p;

我无法理解指针p指向的是哪种结构体,也不确定这种指针定义是否有效,因为在书中提供的所有其他示例和我见过的其他示例中,当定义指向结构体的指针时,需要提到结构体的名称,即正在定义的类型。例如,

struct example{
    int a;
    ...
}s1;

然后,

struct example *ptr = &s1;

因此,提到了ptr指向一个类型为struct example而不仅仅是struct。

另外,尤其有趣的是:

* p-> str获取str指向的任何内容; * p-> str ++在访问它所指向的内容之后增加str(就像* s ++一样);

我无法理解p首先是什么,因此也不会递增和取消引用。

这里发生了什么?

谢谢!

P.S.我是新来的,因此对问题格式的任何反馈也将不胜感激。


10
在第一个示例中,这个结构体是一个匿名结构体,而p是指向该匿名结构体的指针。 - Some programmer dude
2
这只是一个没有名称的结构,就像所示的那个一样。它并不经常使用,因为它的用途有限。 - Some programmer dude
6
这是一种没有名称的结构类型,因此您无法使用相同的结构类型声明其他变量。 - Barmar
2
可能是在C语言中使用指向未命名结构体的指针?的重复问题。 - Raymond Chen
1
@RaymondChen 显然,那个问题并不是关于匿名结构体的,而是关于指针类型而不仅仅是指向这种类型的指针。 - Eugene Sh.
显示剩余5条评论
4个回答

12
struct关键字类似于typedef,但您创建的是一个复杂的自定义类型(称为结构),而不是别名现有类型。如果只有一个需要使用已声明类型的内容,则无需为该类型提供显式名称。
第一条语句声明了一个具有两个字段但未命名的结构。这称为匿名结构。声明提供了该类型的指针。
这样声明的一个可能用例是当您为外部库制作头文件时,可能甚至不是用C语言编写的库。在这种情况下,结构的类型可能是不透明或不完整的,并且您只需要方便地引用其中的某些部分。匿名结构可以防止您轻松地分配它,但允许您通过指针与其交互。
更常见的是,您将看到此符号与命名或至少别名结构一起使用。第二条语句可以重写为
struct example { ... } s1, *ptr;
在这种情况下,struct example *ptr = &s1; 可以简写为 ptr = &s1;
更常见的情况是使用匿名结构体和typedef一起使用,创建不包含struct关键字的自定义类型名称。你的第二个例子可以重写为:
typedef struct { ... } example, *pexample;
example s1;
pexample ptr; // alternatively example *ptr;
ptr = &s1;

请注意,s1 的类型在此情况下为 example 而不是 struct example


“提供指向该类型的指针”这个短语应该更准确地写成“提供该类型的指针”吗?我误读了你的答案,以为我们有一个指向类型本身的指针。 - Dan
@Dan。已修复。感谢你的发现。 - Mad Physicist

3
首先考虑以下简单程序:
#include <stdlib.h>
#include <stdio.h>
#include <string.h> 

int main(void) 
{
    struct {
        int len;
        char *str;
    } *p;

    p = malloc( sizeof( *p ) );

    p->str = "Hello Nihal Jain";
    p->len = strlen( p->str );

    while ( *p->str ) putchar( *p->str++ );
    putchar( '\n' );

    free( p );

    return 0;
}

它的输出是

Hello Nihal Jain

所以在这个声明中
    struct {
        int len;
        char *str;
    } *p;

这里声明了一个未命名结构体类型的指针。指针本身没有被初始化。例如,您可以写

    struct {
        int len;
        char *str;
    } *p = malloc( sizeof( *p ) );

对于这个简单的程序,结构体的名称并不是必需的,因为程序中不存在或不需要声明结构体类型的对象。
所以在这种情况下,您无法声明结构体类型的对象,但这并不是必需的。
根据C标准,结构体或联合体声明如下:
struct-or-union-specifier: struct-or-union identifieropt { struct-declaration-list } struct-or-union identifier 可以看出,如果存在struct-declaration-list,则标识符是可选的。因此,可以使用未命名的结构体作为类型说明符。
再举一个使用枚举的例子。您可以声明枚举器而不声明枚举类型。例如:
enum { EXIT_SUCCESS = 0, EXIT_FAILURE = -1 }; 

如果程序没有这样的要求,您可以使用具有类型为int的枚举器而无需声明枚举类型的对象。


今天我学到了一个新技巧,可以通过 sizeof(*p) 来提取大小。非常实用。 - Mad Physicist
@MadPhysicist 或 sizeof *p。不需要 ()。(风格问题) - chux - Reinstate Monica
@chux。收到了。我只是偏好在任何地方都加上括号。 - Mad Physicist
2
@MadPhysicist 你可能会喜欢 (LISP) - chux - Reinstate Monica
字符串没有被分配,那怎么可能工作呢?我认为在处理未初始化的指向字符的指针的LHS表达式时,对该代码使用应该有一个警告,因为RHS使用了常量字符串。 - t0mm13b

2
另一个使用匿名结构的方法是将它们用于联合或其他结构中,这基本上将该结构的使用限制在特定父级联合或结构中,而不是其他任何地方。从程序员的角度来看,这是非常有用的,因为查看像这样的代码时,它向我们提供了信息,即它仅在其内部的结构或联合的上下文中使用-不再多余。 (也可以省去命名)。
此外,如果您使用此匿名结构,则除已存在的实例之外,将无法分配另一个实例。
例如:-(stark的评论:如何使用它?)
struct {
  int a;
  int b;
} p;

scanf("%d",&p.a);

或者意外地分配了我们自己的一个。 - Mad Physicist
@MadPhysicist:是的,没错。当它是匿名的时候,你不能分配内存...就像一次性使用一样... - user2736738
你会如何引用匿名结构体中的成员? - stark
1
@stark:像这样 - user2736738
@coderredoc p=malloc(sizeof(*p))。(p是指针的原始情况)。更新 - 哦,我看到它在其他答案中提到了。 - Eugene Sh.
@EugeneSh:这只是一个示例,供用户参考。如果有任何错误,请告诉我...谢谢。 - user2736738

0
如果您需要一个临时的、一次性的结构体(或联合体)定义,它在声明它的作用域之外将没有用处,那么您将使用所谓的匿名或未命名的结构体和联合体。这样可以避免在使用结构体之前声明它,在复杂类型的情况下,也可以避免声明内部类型,例如这里

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