6

在我的 C 程序中,我有一行使用 '==' 运算符,两个操作数都转换为 char,如下所示:

char top_level_domain[sizeof(char) * 128];
...
if((char)top_level_domain[i] == ':' 
    || (char)top_level_domain[i] == '/')

这是推荐/安全的吗?如果不是,我应该如何检查数组中某个元素的内容?

编辑:添加声明并删除对字符文字的强制转换

4

4 回答 4

17

一般来说,尽可能避免强制转换安全有效,因为它允许编译器执行类型检查。例如,发现错误:

// Let's pretend you forgot or mixed up the type here...
char **top_level_domain;
// ...
if ((char) top_level_domain[i] == (char) ':')
    ...

或者可能...

char top_level_domain[sizeof(char) * 128];
...
// Whoops! forgot to type [i]
if((char)top_level_domain[i] == ':' 
    || (char)top_level_domain == '/')

哎呀!你忘了取消引用指针,你得到了垃圾。编译器为您提供诊断消息,但由于您使用了强制转换,因此诊断消息消失了。

注意:这实际上会在某些编译器上导致诊断消息,因为char它比 窄char *,但如果我们使用size_t而不是,char则不会出现窄化,但它仍然是一个错误。

为什么要使用演员表?

在很多情况下,C 的“整数提升”和“通常的算术转换”会导致不良行为。例如,

size_t align_to_16_bytes(size_t x)
{
    // return x & ~15u; // WRONG
    return x & ~(size_t) 15u; // RIGHT
}

int但是,一般来说,只有当您使用比int.

Java 和 C# 等较新的语言通过只允许扩大隐式转换在很大程度上避免了这个问题。

于 2013-08-24T20:01:49.013 回答
2

演员表是“安全的”但没用,而且风格非常糟糕。通常在 C 中,任何需要强制转换的东西充其量都是不好的风格,而且更常见的是调用未定义的行为,因此源文件中存在强制转换是一种“代码气味”——这表明作者可能做错了什么,并且读者需要格外小心地寻找错误。

只需删除演员表,您的代码就会非常好。

于 2013-08-24T20:02:59.700 回答
1

强制转换是对编译器的显式声明,您希望覆盖语言为您提供的默认隐式类型转换(或考虑不存在它们)。一般来说,这些默认的隐式类型转换被语言设计者深思熟虑,并且C 的类型安全一起工作,而不是反对它。

一个很好的例子是void *,根据 C11 第 6.5.16.1.1 节,它可以通过赋值隐式转换为“指向任何对象类型的指针”或从“指向任何对象类型的指针”转换。例如,这意味着您不能将其隐式转换为指向函数的指针。例如,这正是您希望它在调用时工作malloc()的方式 - 它必须转换为其他类型的指针,因为您显然无法创建 type 的对象void,但是动态分配一个块根本没有意义一个函数的内存。因此,这里的默认隐式类型转换完全符合您的要求 - 让您转换为指向任何对象类型的指针,因为这是全部目的,但如果您尝试转换为其他任何东西,则会大声抱怨。

有些人似乎认为,将 return from 转换为malloc()“明确”你正在尝试做的事情,但是(1)你从来没有看到那些人做这样的事情int i = 1; double d = (double) i;,他们似乎做了一个特殊的案例malloc();并且 (2) 它根本不这样做,因为强制转换实际上明确表明您想要覆盖 C 为您提供的类型安全和默认转换,而在这种情况下您真正想要做的是遵守他们。

或者,有时隐式类型转换不会给您想要的东西,并且强制转换是必要的。一个明显的例子是整数除法,它总是给你一个int. 制作 C 的人可以提供另一个运算符来执行整数浮点除法,如果他们愿意,但他们没有,结果是如果你想用两个整数执行除法,而整数除法不是你想要的,那么您必须将其中一个转换为浮点类型以覆盖默认行为以实现您想要的。如果整数除法您在特定情况下想要的,那么您显然不会强制转换。

因此,作为一般规则,当 C 在没有强制转换的情况下为您提供所需的结果时 - 大多数情况下 - 不要强制转换。只有当 C 的默认行为没有给你你想要的,并且你愿意明确地放弃它给你的类型安全性时才强制转换。

于 2013-08-25T01:36:25.487 回答
0

强制转换是不安全的,不仅因为 Dietrich Epp 谈到的编译器类型检查问题,还因为缩小值范围会导致误报:

int myValue = 'a' + 768;      //A number that is not equal to 'a'
assert(myValue != 'a');       //This is true...
assert((char)myValue == 'a'); //...but so is this!

当然,这是因为我构建 myValue 的方式会产生这种误报。但是,如果将整数转换为 char,则所有整数的 1/256 将等于特定字符,这很可能是误报。如果您允许编译器选择整数表示进行比较,则不会发生这种情况。

于 2013-08-24T23:44:29.453 回答