对不同结构类型的指针进行类型转换(例如 struct sockaddr * 到 struct sockaddr_in6 *)是否合法?

Lon*_*ner 6 c struct pointers

struct shape这是一个在 、struct rectangle和类型的指针之间进行类型转换的程序struct triangle

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

enum { RECTANGLE, TRIANGLE, MAX };

struct shape {
    int type;
};

struct rectangle {
    int type;
    int x;
    int y;
};

struct triangle {
    int type;
    int x;
    int y;
    int z;
};

struct shape *get_random_shape()
{
    int type = rand() % MAX;
    if (type == RECTANGLE) {
        struct rectangle *r = malloc(sizeof (struct rectangle));
        r->type = type;
        r->x = rand() % 10 + 1;
        r->y = rand() % 10 + 1;
        return (struct shape *) r;
    } else if (type == TRIANGLE) {
        struct triangle *t = malloc(sizeof (struct triangle));
        t->type = type;
        t->x = rand() % 10 + 1;
        t->y = rand() % 10 + 1;
        t->z = rand() % 10 + 1;
        return (struct shape *) t;
    } else {
        return NULL;
    }
}

int main()
{
    srand(time(NULL));

    struct shape *s = get_random_shape();

    if (s->type == RECTANGLE) {
        struct rectangle *r = (struct rectangle *) s;
        printf("perimeter of rectangle: %d\n", r->x + r->y);
    } else if (s->type == TRIANGLE) {
        struct triangle *t = (struct triangle *) s;
        printf("perimeter of triangle: %d\n", t->x + t->y + t->z);
    } else {
        printf("unknown shape\n");
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这是输出。

$ gcc -std=c99 -Wall -Wextra -pedantic main.c
$ ./a.out 
perimeter of triangle: 22
$ ./a.out 
perimeter of triangle: 24
$ ./a.out 
perimeter of rectangle: 8
Run Code Online (Sandbox Code Playgroud)

您可以在上面看到该程序编译并运行,没有任何警告。struct shape我试图了解将类型转换为into的指针是否有效struct rectangle,反之亦然,即使这两个结构的大小不同。

如果您的答案是这是无效的,那么请考虑网络编程书籍通常根据套接字系列(AF_INET 与 AF_INET6)在struct sockaddr *,struct sockaddr_in *和指针之间进行类型转换,然后解释为什么这种类型转换在 的情况下是可以的,但在上述案例的. 这是使用 进行类型转换的示例。struct sockaddr_in6 *struct sockaddr *struct shape *struct sockaddr *

#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

int main()
{
    struct addrinfo *ai;

    if (getaddrinfo("localhost", "http", NULL, &ai) != 0) {
        printf("error\n");
        return EXIT_FAILURE;
    }

    if (ai->ai_family == AF_INET) {
        struct sockaddr_in *addr = (struct sockaddr_in *) ai->ai_addr;
        printf("IPv4 port: %d\n", addr->sin_port);
    } else if (ai->ai_family == AF_INET6) {
        struct sockaddr_in6 *addr = (struct sockaddr_in6 *) ai->ai_addr;
        printf("IPv6 port: %d\n", addr->sin6_port);
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这段代码编译并运行得很好。此外,这是根据套接字编程书籍编写此类程序的推荐方法。

$ gcc -std=c99 -D_POSIX_SOURCE -Wall -Wextra -pedantic foo.c
$ ./a.out 
IPv6 port: 20480
Run Code Online (Sandbox Code Playgroud)

Joh*_*ger 3

对不同结构类型的指针进行类型转换(例如 struct sockaddr * 到 struct sockaddr_in6 *)是否合法?

是的。C明确规定:

指向对象类型的指针可以转换为指向不同对象类型的指针。如果生成的指针未针对引用类型正确对齐,则行为未定义。否则,当再次转换回来时,结果应等于原始指针。

(C2011,6.3.2.3/7)

正如其他答案所指出的那样,问题不是演员本身,而是您对结果的处理方式。这归结为严格的别名规则:

对象的存储值只能由具有以下类型之一的左值表达式访问:

  • 与对象的有效类型兼容的类型,

[...加上其他几种不能适用于这种情况的替代方案...]

(C2011,6.5/7;已添加重点)

因此,主要问题是这些指向的对象的有效类型是什么struct sockaddr *?这里重要的是要理解我们无法从 的声明中辨别出来getaddrinfo(),也不能从 的声明中辨别出来struct addrinfo。特别是,没有理由假设有效类型是struct sockaddr

事实上,鉴于您所询问的强制转换是访问地址详细信息的标准和预期方法,因此有充分的理由认为getaddrinfo()通过确保有效类型是相关代码指示的类型来支持这一点ai_family。然后相应的转换产生一个与地址信息的有效类型匹配的指针。在这种情况下,通过强制转换获得的指针访问地址信息没有固有的问题。

为了支持上述观点,我观察到,假设所讨论的指针指向动态分配的对象是合理的。此类对象的有效类型取决于上次设置其存储值的方式(C2011,6.5/6)。这不仅是合理的,而且很可能会getaddrinfo()以赋予其所需的有效类型的方式设置该值。例如,沿着与形状示例相同的行进行编码就可以做到这一点。

最终,将struct sockaddr *to 和 from 指针强制转换为特定于地址族的结构是预期用途,并且没有理由认为提供的环境getaddrinfo()实际上会允许这些行为是可疑的。如果有必要,POSIX(由其指定函数)可以合并一条允许强制转换的特殊规则。但在这种情况下不需要这样的规则,尽管 POSIX 让你相信这一点。