需要一些关于C语言铸造的澄清吗

需要一些关于C语言铸造的澄清吗,c,casting,C,Casting,我刚刚读到一篇关于抛出malloc返回值的糟糕做法的文章。如果我理解正确的话,让演员组保持默认状态是绝对合法的(而且应该保持这种状态,因为它可能会产生其他问题)。我的问题是,我应该在什么时候铸造我的价值观?有什么一般规则吗?例如,此代码使用gcc-W-Wall编译时没有任何错误(除了未使用的bar,但这不是重点): 我现在很困惑。铸造的良好实践和规则是什么 谢谢。我的简单指导原则——如果需要演员阵容,可能是错的。如果你不需要石膏,就不要使用 double bar = foo(); 这里发生的事

我刚刚读到一篇关于抛出
malloc
返回值的糟糕做法的文章。如果我理解正确的话,让演员组保持默认状态是绝对合法的(而且应该保持这种状态,因为它可能会产生其他问题)。我的问题是,我应该在什么时候铸造我的价值观?有什么一般规则吗?例如,此代码使用
gcc-W-Wall
编译时没有任何错误(除了未使用的
bar
,但这不是重点):

我现在很困惑。铸造的良好实践和规则是什么


谢谢。

我的简单指导原则——如果需要演员阵容,可能是错的。如果你不需要石膏,就不要使用

double bar = foo();

这里发生的事情称为升级转换,转换后保留强制转换变量的值。反之亦然,即
float->double
。唯一的答案是只有在你真正需要的时候才投。大量铸造是设计不好的标志。

在您的示例中,精度有所下降,但铸造是隐含的。有时强制转换是绝对必要的,例如,当您从字节流读取数据时,或者当您所拥有的只是通过
void*
指针输入的数据时,但您知道它代表什么数据。但在大多数情况下,铸造应该避免,并保留在这些极端情况下。

如果您需要铸造,我总是建议您明确地这样做,以向其他人或将来的您自己表明您打算进行这种行为

顺便说一下,gcc对此的警告是
-Wconversion
。不幸的是,Wall和Wextra仍然留下了很多好的警告

以下是我希望gcc非常像棉绒时使用的标志

-pedantic -std=c99 -ggdb3 -O0 -Wall -Wextra -Wformat=2 -Wmissing-include-dirs -Winit-self -Wswitch-default -Wswitch-enum -Wunused-parameter -Wfloat-equal -Wundef -Wshadow -Wlarger-than-1000 -Wunsafe-loop-optimizations -Wbad-function-cast -Wcast-qual -Wcast-align -Wconversion -Wlogical-op -Waggregate-return -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wmissing-declarations -Wpacked -Wpadded -Wredundant-decls -Wnested-externs -Wunreachable-code -Winline -Winvalid-pch -Wvolatile-register-var -Wstrict-aliasing=2 -Wstrict-overflow=2 -Wtraditional-conversion -Wwrite-strings -pedantic-std=c99-ggdb3-O0-Wall-Wextra-Wformat=2-Wmissing include dirs-Winit self-Wswitch default-Wswitch enum-Wunused parameter-Wfloat equal-Wundef-Wshadow-Wlarger-than-1000-Wunsafe循环优化-Wbad函数cast-Wcast qual-Wcast align-Wconversion-Wlogical op-waggreggate return-ricwstt原型-Wold样式定义-Wmissing原型-Wmissing声明-Wpacked-wppadded-Wredundant decls-ownested externs-Wunreachable code-Winline-Winvalid pch-Wvolatile register var-Wstrict aliasing=2-Wstrict overflow=2-wtradional conversion-Wwrite strings
<>我也先检查我的代码,它是一个用于C和C++的免费静态代码分析器。强烈推荐。

只有在严格必要时才应铸造结果;如果您使用的代码是由两个不同的人开发的(例如静态库或动态库),并且两个函数不使用兼容的值,那么强制转换是唯一的解决方案(只要您不尝试将字符串强制转换为整数)


在使用转换之前,最好验证使用的数据类型是否正确。在示例代码(其目的是提供示例)中,当函数返回double时,将返回值声明为浮点值是没有意义的

您看到的是隐式类型转换。如果您开始使用的类型的范围比最终使用的类型的范围更受限制,即短到int是可以的,浮到double也是可以的,那么这被认为是安全的


我很惊讶gcc在将double转换为float时没有生成警告;我相信微软的编译器会这样做。

您不强制转换
malloc
的返回值的原因是您总是将返回值分配给指针类型,而C标准允许将
void*
隐式强制转换为任何其他指针类型。显式强制转换是多余的,因此是不必要的。

您可能会发现这两篇文章内容丰富:


有几种情况需要在C语言中进行完全有效的施法。请注意像“施法总是糟糕的设计”这样的笼统论断,因为它们显然是伪造的

严重依赖强制转换的一大类情况是算术运算。当您需要强制编译器解释不同于“默认”类型的算术表达式时,需要强制转换。如

unsigned i = ...;
unsigned long s = (unsigned long) i * i;
以避免溢出。或者

double d = (double) i / 5;
为了使编译器切换到浮点除法。或者

s = (unsigned) d * 3 + i;
以获取浮点值的全部部分。等等(例子不胜枚举)

另一组有效的用法是习语,即公认的编码实践。例如,经典的C语言习惯用法是,函数将常量指针作为输入,并返回指向相同(可能是常量)数据的非常量指针,例如标准的
strstrstr
。实现这个习惯用法通常需要使用强制转换来消除输入的常量。有人可能会说它是糟糕的设计,但实际上在C中没有更好的设计选择。否则,它就不是一个公认的习惯用法:)

另外值得一提的是,作为一个例子,在一般情况下,如果要学究般正确地使用标准的
printf
函数,可能需要对参数进行强制转换。(如
%p
格式说明符要求将
void*
指针作为参数,这意味着必须以某种方式将
int*
参数转换为
void*
。显式强制转换是执行转换的最合乎逻辑的方式。)

当然,当需要强制转换时,还有许多其他完全有效的例子

当人们不加考虑地使用强制转换时,通常会出现问题,即使在不需要强制转换的情况下(例如强制转换malloc的返回值,这是不好的,原因不止一个)。或者当人们使用强制类型转换强制编译器接受他们的错误代码时。不用说,区分一个好的演员阵容和一个坏的演员阵容需要一定程度的专业知识
s = (unsigned) d * 3 + i;
char *p;
if ((*p != EOF) && isalpha((unsigned char)*p) /* cast needed */
{
    /* ... */
}
long big;
printf("%d\n", (int)big);
int my_printf(const char *fmt, ...);
my_printf("UNIX Epoch: %t.\n", 0);         /* here, 0 is an int value */
my_printf("UNIX Epoch: %t.\n", (time_t)0); /* and here it is a time_t */
    char **ptostr = (char **p) "this is not a good idea";