【发布时间】:2016-09-10 11:07:19
【问题描述】:
这是一个在 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;
}
这是输出。
$ 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 是否有效,反之亦然,即使两个结构的大小不同。
如果您的回答是这是无效的,那么请考虑网络编程书籍根据套接字系列(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;
}
此代码也可以正常编译和运行。此外,这是套接字编程书籍中推荐的编写此类程序的方法。
$ gcc -std=c99 -D_POSIX_SOURCE -Wall -Wextra -pedantic foo.c
$ ./a.out
IPv6 port: 20480
【问题讨论】:
-
您正在将子指针转换为父指针,然后将其向下转换回精确对应的子指针。您要求编译器信任您(通过使用强制转换)。所以,没有警告。
-
IIRC,这仅适用于
unions。 -
@Lone Learner:虽然您在这里所做的可能是有效的(由于“通用初始序列”规则),但在一般情况下,只是因为“程序编译并运行时没有任何警告”不在任何方式都表明它在某种程度上是“有效的”。 “编译并运行良好”绝对与您的代码在 C 世界中的有效性无关。
-
@AnT 通用初始序列规则不适用,因为结构不是联合的成员。目前尚不清楚这是否是严格的别名违规:有人认为
r->type意味着(*r).type而*r违反了规则;其他人(包括我)说这不是因为r->type是唯一可以访问的东西,它是int类型并读取int。