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)
对不同结构类型的指针进行类型转换(例如 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 让你相信这一点。