这个(char *)& x演员的行为是否定义明确?

时间:2016-02-23 12:18:39

标签: c string casting character

在编写一些C代码时,我遇到了一个小问题,我必须将字符转换为“字符串”(一些内存块,其开头由char*指针给出)。

这个想法是,如果设置了一些sourcestr指针(不是NULL),那么我应该将它用作我的“最终字符串”,否则我应该将给定的charcode转换为另一个数组的第一个字符,并改为使用它。

出于这个问题的目的,我们假设变量的类型不能事先改变。换句话说,我不能仅将charcode存储为const char*而不是int

因为我倾向于懒惰,所以我心里想:“嘿,我不能只使用角色的地址并将该指针视为字符串吗?”。这是我写的的一小部分片段(不要把我的头撞到墙上!)

int charcode    = FOO;   /* Assume this is always valid ASCII. */

char* sourcestr = "BAR"; /* Case #1 */
char* sourcestr = NULL;  /* Case #2 */

char* finalstr  = sourcestr ? sourcestr : (char*)&charcode;

现在我当然试过了,正如我所料,它确实有效。即使有一些警告标志,编译器仍然很高兴。但是,我有这种奇怪的感觉,这实际上是未定义的行为,我不应该这样做。

我认为这种方式的原因是因为char*数组需要以空值终止才能正确打印为字符串(我想要我的!)。然而,我不确定&charcode + 1的值是否为零,因此我可能最终会出现一些缓冲区溢出的疯狂。

是否有正确的原因,为什么它可以正常工作,或者我只是幸运地在我尝试时在正确的位置获得零?

(请注意,我不是在寻找其他方法来实现转换。我可以简单地使用char tmp[2] = {0}变量,并将我的角色放在索引0处。我也可以使用类似{{ 1}}或sprintf,只要我对缓冲区溢出足够小心。有很多方法可以做到这一点,我只对这个特定的强制转换操作的行为感兴趣。)

编辑:我见过一些人称这个hackery,我们要明确:我完全同意你的看法。在释放的代码中我实际上做这个是不够的受虐狂。这只是我好奇;)

4 个答案:

答案 0 :(得分:5)

您的代码定义明确,因为您始终可以转换为<div ontap="{onTap}" ontaphold="{onTapHold}" onswipeleft="{onSwipeLeft}" ...> 。但是有些问题:

  1. 请注意,char*"BAR"字面值 - 因此不要尝试修改内容。 未定义。

  2. 不要尝试将const char*用作C标准库中任何字符串函数的参数。它将以空值终止。因此,从这个意义上说,不能将其视为字符串。

  3. (char*)&charcode 上的指针算法有效,包括标量(char*)&charcode之后的一个。但是不要尝试取消引用超出charcode本身的任何指针。表达式charcode有效的n范围取决于(char*)&charcode + n

答案 1 :(得分:3)

演员和作业char* finalstr = (char*)&charcode;已定义。

使用printf作为字符串打印finalstr %s,如果它指向charcode,则是未定义的行为。

不是诉诸hackery并在类型int中隐藏字符串,而是使用选定的转换函数将存储在整数中的值转换为字符串。一个可能的例子是:

char str[32] = { 0 };
snprintf( str , 32 , "%d" , charcode );
char* finalstr = sourcestr ? sourcestr : str;

或使用您喜欢的任何其他(已定义!)转换。

答案 2 :(得分:2)

就像其他人说的那样,它恰好起作用,因为你机器上int的内部表示是小端,而你的char小于int。你的角色的ascii值也低于128,或者你有无符号的字符(否则会有符号扩展名)。这意味着字符的值在int的表示的较低字节中,而int的其余部分将全部为零(假设int的任何正常表示)。你没有&#34;幸运&#34;,你有一台非常普通的机器。

将char指针指向任何需要字符串的函数也是完全未定义的行为。你可能现在可以使用它,但编译器可以自由地将其优化为完全不同的东西。

例如,如果您在该分配之后执行printf,编译器可以自由地假设您始终将有效字符串传递给printf,这意味着检查{{ 1}}为NULL是不必要的,因为如果sourcestr为NULL sourcestr将使用不是字符串的东西调用,编译器可以自由地假设未定义的行为永远不会发生。这意味着在该赋值之前或之后检查printf为NULL是不必要的,因为编译器已经知道它不是NULL。允许这种假设传播到代码中的任何地方。

这很少令人担心,你可以侥幸逃脱这种伎俩,直到十年前左右,当编译器编写者开始军备竞赛时,他们可以遵循C标准到达信件的程度越来越残酷的优化。今天编译器变得越来越激进,而我推测的优化可能还不存在,如果编译人员看到这一点,他们可能只是因为他们可以实现它。

答案 3 :(得分:0)

由于以下原因,这是绝对未定义的行为:

  1. 不太可能,但在严格参考标准时要考虑:你不能在机器/系统上假设大小为int,代码将被编译
  2. 如上所述,您无法使用代码集。例如。在EBCDIC机器/系统上发生了什么?
  3. 很容易说你的机器有一个小端处理器。 在big endian计算机上,由于big-endian内存布局,代码失败
  4. 因为在许多系统char是有符号整数,int,当您的char为负值(即{8} char>127机器上的char时, 如果您按照以下代码中的值分配
  5. ,则可能因签名扩展而失败

    代码:

    char ch = FOO;
    int charcode = ch;
    

    P.S。关于第3点:你的字符串确实会在sizeof(int)>sizeof(char)char具有正值的小端机器中终止NULL,因为int的MSB将为0并且这种字节顺序的内存布局是LSB-MSB(LSB优先)。