强制转换不同结构类型的指针是否合法(例如struct sockaddr *到struct sockaddr_in6 *)? [英] Is it legal to type-cast pointers of different struct types (e.g. struct sockaddr * to struct sockaddr_in6 *)?

查看:85
本文介绍了强制转换不同结构类型的指针是否合法(例如struct sockaddr *到struct sockaddr_in6 *)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个在struct shapestruct rectanglestruct triangle类型的指针之间进行类型转换的程序.

Here is a program that type-casts between pointers of type struct shape, struct rectangle and 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;
}

这是输出.

$ 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

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

You can see above that the program compiled and ran without any warnings. I am trying to understand if it is valid to type-cast a pointer of struct shape into struct rectangle and vice-versa even though both the structs are of different sizes.

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

If your answer is that this is not valid, then please consider that network programming books routinely typecast between struct sockaddr *, struct sockaddr_in * and struct sockaddr_in6 * pointers depending on the socket family (AF_INET vs. AF_INET6), and then explain why such type cast is okay in case of struct sockaddr * but not in the above case of struct shape *. Here is an example of type cast with 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;
}

此代码也可以编译并正常运行.此外,这是按照套接字编程书籍编写此类程序的推荐方式.

This code compiles and runs fine as well. Moreover, this is the recommended way of writing such programs as per books on socket programming.

$ gcc -std=c99 -D_POSIX_SOURCE -Wall -Wextra -pedantic foo.c
$ ./a.out 
IPv6 port: 20480

推荐答案

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

Is it legal to type-cast pointers of different struct types (e.g. struct sockaddr * to struct sockaddr_in6 *)?

是的. C明确提供了它:

Yes. C explicitly provides for it:

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

A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer.

(C2011,6.3.2.3/7)

(C2011, 6.3.2.3/7)

正如其他答案所指出的那样,问题不是问题本身,而是结果如何处理.这归结为严格的别名规则:

As other answers have pointed out, it is not the cast itself that is the problem, but what you do with the result. And that comes down to the Strict Aliasing Rule:

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

An object shall have its stored value accessed only by an lvalue expression that has one of the following types:

  • 与对象的有效类型兼容的类型
  • a type compatible with the effective type of the object,

[...以及在此情况下无法应用的其他几种替代方法...]

[... plus several other alternatives that cannot apply in this case ...]

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

(C2011, 6.5/7; emphasis added)

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

The main question, therefore, is what is the effective type of the object to which the struct sockaddr * points? It's important here to understand that we can't tell from the declaration of getaddrinfo(), nor that of struct addrinfo. In particular, there is no reason to assume that the effective type is struct sockaddr.

实际上,考虑到您所要求的类型转换是访问地址详细信息的标准方法和预期方法,因此我们有充分的理由假设getaddrinfo()通过确保有效类型为以下指示来支持该类型:关联的ai_family代码.然后,相应的转换产生一个与地址信息的有效类型匹配的指针.在那种情况下,通过通过强制转换获得的指针访问地址信息就不会存在固有的问题.

In fact, given that the cast you've asked about is the standard and intended method for accessing the address details, there is every reason to suppose that getaddrinfo() supports that by ensuring that the effective type is the one indicated by the associated ai_family code. Then the corresponding cast yields a pointer matching the effective type of the address info. In that case, there is no problem inherent in accessing the address info via the pointer obtained via the cast.

为支持上述观点,我观察到合理的假设是,所讨论的指针指向动态分配的对象.此类对象的有效类型取决于上一次设置其存储值的方式(C2011,6.5/6). getaddrinfo()可能以给它想要的有效类型的方式设置该值,这不仅是合理的,而且是可能的.例如,使用与形状示例相同的代码进行编码.

I observe in support of the above that it is reasonable to suppose that the pointer in question points to a dynamically allocated object. The effective type of such an object depends on the means by which its stored value was last set (C2011, 6.5/6). It is not only plausible but likely that getaddrinfo() would set that value in a manner that gives it the wanted effective type. For example, code along the same lines as your shape example would do so.

最终,将struct sockaddr *与指向地址族的特定结构的指针进行强制转换是预期的用途,并且没有理由认为提供getaddrinfo()的环境实际上将允许这些行为值得怀疑.如果有必要,POSIX(由其指定功能的人)可以加入允许转换的特殊规则.但是,尽管POSIX使您相信这一点,但在这种情况下并不需要这样的规则.

Ultimately, casting the struct sockaddr * to and from pointers to the address-family-specific structs is the intended use, and there is no reason to suppose that an environment that provides getaddrinfo() would, in practice, allow those behaviors to be dubious. If it had been necessary, POSIX (by whom the function is specified) could have incorporated a special rule allowing the casts. But no such rule is needed in this case, although POSIX makes you take that on faith.

这篇关于强制转换不同结构类型的指针是否合法(例如struct sockaddr *到struct sockaddr_in6 *)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆