考虑以下片段:
char x[100];
double *p = &x;
Run Code Online (Sandbox Code Playgroud)
正如预期的那样,这会产生以下警告:
f.c:3:15: warning: initialization of ‘double *’ from incompatible pointer type ‘char (*)[100]’
[-Wincompatible-pointer-types]
3 | double *p = &x;
| ^
Run Code Online (Sandbox Code Playgroud)
这很容易解决,只需更改为
double *p = (double*)&x;
Run Code Online (Sandbox Code Playgroud)
我的问题是,演员阵容真的有什么作用吗?如果没有演员表,代码会无效吗?或者这只是一种让编译器安静的方法?什么时候需要铸造?
我知道你可以对这样的片段产生一些影响:
int x = 666;
int y = (char)x;
Run Code Online (Sandbox Code Playgroud)
但这不和这个一样吗?
int x = 666;
char c = x;
int y = c;
Run Code Online (Sandbox Code Playgroud)
如果是相同的,那么演员表会做一些事情,但没有必要。对?
请帮助我理解这一点。
铸造可以做几种不同的事情。正如其他答案所提到的,它几乎总是会更改正在转换的值的类型(或者可能是该类型的属性,例如const)。它还可能以某种方式改变数值。但有很多种可能的解释:
另外,有时编译器试图发出的那些警告,即强制转换沉默,是无害的和/或令人讨厌的,但有时它们是非常真实的,并且代码可能会失败(也就是说,因为沉默的警告正在尝试)告诉你)。
对于一些更具体的例子:
改变类型但不改变值的指针转换:
char *p1 = ... ;
const char *p2 = (const char *)p;
Run Code Online (Sandbox Code Playgroud)
还有另一个:
unsigned char *p3 = (unsigned char *)p;
Run Code Online (Sandbox Code Playgroud)
以更重要的方式更改类型的指针转换,但这保证没问题(在某些体系结构上,这也可能会更改值):
int i;
int *ip = &i;
char *p = (char *)ip;
Run Code Online (Sandbox Code Playgroud)
一个类似的重要指针转换,但很可能不太好:
char c;
char *cp = &c;
int *ip = (int *)cp;
*ip = 5; /* likely to fail */
Run Code Online (Sandbox Code Playgroud)
指针转换毫无意义,以至于编译器拒绝执行它,即使使用显式转换也是如此:
float f = 3.14;
char *p = (char)f; /* guaranteed to fail */
Run Code Online (Sandbox Code Playgroud)
进行转换的指针转换,但编译器无论如何都会进行转换:
int *p = (int *)malloc(sizeof(int));
Run Code Online (Sandbox Code Playgroud)
(这被认为是一个坏主意,因为在您忘记包含<stdlib.h>声明的情况下malloc(),强制转换可以消除可能提醒您该问题的警告。)
由于C 语言中非常具体的特殊情况,从整数到指针的三种转换实际上是明确定义的:
void *p1 = (void *)0;
char *p2 = (void *)0;
int *p3 = (int *)0;
Run Code Online (Sandbox Code Playgroud)
从整数到指针的两次强制转换不一定有效,尽管编译器通常会做一些明显的事情,并且强制转换将使其他警告静音:
int i = 123;
char *p1 = (char *)i;
char *p2 = (char *)124;
*p1 = 5; /* very likely to fail, except when */
*p2 = 7; /* doing embedded or OS programming */
Run Code Online (Sandbox Code Playgroud)
从指针返回到 的非常可疑的转换int:
char *p = ... ;
int i = (int)p;
Run Code Online (Sandbox Code Playgroud)
从指针返回到应该足够大的整数的问题较少的转换:
char *p = ... ;
uintptr_t i = (uintptr_t)p;
Run Code Online (Sandbox Code Playgroud)
更改类型的强制转换,但“丢弃”而不是“转换”值,并且使警告静音:
(void)5;
Run Code Online (Sandbox Code Playgroud)
进行数字转换的强制转换,但编译器无论如何都会进行转换:
float f = (float)0;
Run Code Online (Sandbox Code Playgroud)
更改类型和解释值的强制转换,尽管它通常不会更改位模式:
short int si = -32760;
unsigned short us = (unsigned short)si;
Run Code Online (Sandbox Code Playgroud)
进行数字转换的强制转换,但编译器可能会发出警告:
int i = (int)1.5;
Run Code Online (Sandbox Code Playgroud)
进行编译器不会进行的转换的强制转换:
double third = (double)1 / 3;
Run Code Online (Sandbox Code Playgroud)
底线是强制转换肯定会做一些事情:有些有用,有些不必要但无害,有些危险。
如今,许多 C 程序员达成的共识是,大多数强制转换是或应该是不必要的,这意味着避免显式强制转换是一个不错的规则,除非您确定自己知道自己在做什么,并且对显式强制转换持怀疑态度是合理的。您在其他人的代码中发现的强制转换,因为它们可能是麻烦的迹象。
作为最后一个例子,在过去,这种情况确实让我在指针转换方面点亮了灯泡:
char *loc;
int val;
int size;
/* ... */
switch(size) {
case 1: *loc += val; break;
case 2: *(int16_t *)loc += val; break;
case 4: *(int32_t *)loc += val; break;
}
Run Code Online (Sandbox Code Playgroud)
这三个实例loc += val执行三件截然不同的事情:一个更新字节,一个更新 16 位 int,一个更新 32 位 int。(有问题的代码是一个动态链接器,执行符号重定位。)
| 归档时间: |
|
| 查看次数: |
222 次 |
| 最近记录: |