【问题标题】:What happens when you type cast an integer value into a char pointer?当您将整数值类型转换为 char 指针时会发生什么?
【发布时间】:2018-05-16 23:29:07
【问题描述】:

例如,

char * integerToString(void);

int main() {
    char *myString;
    do {
        myString = integerToString();
    } while (myString == (char *)-1); // worked as intended
    free(myString);
    return 0;
}

char * integerToString(void) {

    int userInput;
    printf("Enter an integer: ");
    scanf("%d", &userInput);

    if (userInput < 0 || userInput > 99)
        return (char *)-1; // what happens here?

    char *myString = (char *)malloc(sizeof(char) * 2);
    myString[0] = (int)floor(userInput/10.0) + '0';
    myString[1] = userInput%10 + '0';
    return myString;
}

并且程序按预期工作,但是当您将整数值(没有将整数分配给变量)输入字符指针时究竟会发生什么?这个程序会一直有效吗? 谢谢。

【问题讨论】:

标签: c pointers casting


【解决方案1】:

当您将整数值类型转换为 char 指针时会发生什么?

一般来说,就是undefined behavior(至少在您取消引用它时)。非常scared。阅读更多关于 UB(这是一个棘手的主题)。

在某些记录的情况下,您可以将 uintptr_tintptr_t 整数值转换为有效指针。

在您的情况下,您的堆分配字符串太短(因此您有一个buffer overflow,这是 UB 的众多示例之一)。您忘记了终止 NUL 字节的空间,并且您忘记了检查 malloc 是否失败。顺便说一句,sizeof(char)总是 1。

你可以编码:

if (userInput < 0 || userInput > 99)
    return NULL;

char *myString = (char *)malloc(3);
if (!myString) { perror("malloc myString"); exit(EXIT_FAILURE); };
myString[0] = (int)floor(userInput/10.0) + '0';
myString[1] = userInput%10 + '0';
myString[2] = (char)0;
return myString;

在大多数系统(但不是全部)上,(char*)-1 永远不是有效地址(总是在virtual address space 之外),并且永远不能由系统(或标准)函数给出。在我的 Linux/x86-64 桌面上,我知道(char*)-1 不是有效地址(例如因为它是MAP_FAILED),我可以(有时)将其用作非空指针值(应该 被取消引用)。但这使我的代码更少portable

因此,您可以决定并记录您的integerToString 在非整数输入时给出(char*)-1,在堆分配失败时给出NULL。这可以在我的 Linux/x86-64 桌面上运行(所以我有时会这样做)。但这不是纯(可移植的)C11 代码。

但是,如果您坚持 C11 标准(阅读 n1570),则由实现定义什么以及 (char*)-1 是否有意义。可能是一些 trap representation 甚至不允许您进行比较(即使我不知道任何实际的 C 实现这样做)。

实际上,您的示例说明 人们从不为纯标准 C11 编写代码;他们总是(我也是)对 C 实现做出附加假设;但是您确实需要了解它们,这些假设可能会使您的代码移植到某些假设的未来机器上成为一场噩梦。

这个程序会一直有效吗?

这是一个太笼统的问题。您的原始程序甚至没有处理 malloc 的失败并且有一个 buffer overflow (因为您忘记了终止零字节的空间)。然而,对你来说可悲的是,它显然似乎经常起作用(这就是为什么 UB 是 如此 可怕的)。然而,考虑this(符合标准,但不现实)malloc 实施作为思考的食粮。

(准确解释为什么你的程序看起来像你想要的那样真的很难,因为你需要深入研究几个实现细节)

【讨论】:

  • 未定义的行为?如果您取消引用该指针,它肯定是未定义的。但只是滥用char * 作为size_t 本身应该没问题。
【解决方案2】:

C99:

6.3.2.3 指针

  1. 值为 0 的整数常量表达式,或转换为类型的此类表达式 void *,称为空指针常量。如果将空指针常量转换为 指针类型,生成的指针,称为空指针,保证比较不相等 指向任何对象或函数的指针。

[...]

  1. 整数可以转换为任何指针类型。除先前规定外, 结果是实现定义的,可能没有正确对齐,可能不指向 引用类型的实体,可能是陷阱表示。

因此,将-1 转换为指针具有实现定义的结果。因此答案是否定的:一般情况下,这不能保证有效。


特别是:如果它确实是一个陷阱表示,那么您的代码会与以下内容发生冲突:

6.2.6 类型的表示

6.2.6.1 常规

[...]

  1. 某些对象表示不需要表示对象类型的值。如果存储 对象的值具有这样的表示,并由左值表达式读取 没有字符类型,行为未定义。如果产生这样的表示 通过通过左值表达式修改对象的全部或任何部分的副作用 没有字符类型,行为未定义。这种表示称为 一个陷阱表示

即如果myString 是陷阱表示,则while (myString == (char *)-1); 具有未定义的行为。

【讨论】:

  • @Nikos C. 在一些奇怪的实现中,您可能有一些变量a,例如(char*)&amp;a == (char*)-1
  • @NikosC。如果它是陷阱表示,则myString == ... 具有未定义的行为。正如 Basile 所说,“实现定义”也可能意味着它无意中生成了指向代码中现有对象的指针。
  • @NikosC。 malloc() 的返回值可以是(char *)-1,因为(char *)-1 可以变成实现想要的任何东西。这是一个任意值。
  • 是什么让你认为malloc 不能永远(char*)-1。这在我的 Linux 桌面上当然是正确的,但在一些奇怪的微控制器上可能是错误的。
  • @NikosC。 (char *)-1 可以产生0x6f0a12。我认为您还没有意识到“任意价值”的真正含义。
【解决方案3】:

这个程序是一个错误处理不当的例子。 (char *)-1 的值似乎是实现定义的,请参阅其他答案。由于该地址可能不是从malloc 返回的有效内存地址,因此在程序中将其用作标记值。实际值并不重要,它会与另一个函数中的相同表达式进行比较。

如果你运行这个,malloc 只是可能返回 (char *)-1 计算的任何值。然后它将被解释为错误,尽管它是一个有效的内存地址。

更好的方法是为integerToString 提供int * 类型的参数,并将其用作指示失败的布尔值。那么就不会为错误处理保留一个char * 值。

或者使用 C++ 和异常。

【讨论】:

  • (char *)-1 的结果是实现定义的。它不一定是全比特的。它根本不是整数。
  • 由于它与代码中另一部分的完全相同的值进行比较,它仍然可以作为哨兵。我会修正我的答案。
  • 在脚本中”是哪个脚本?
  • @alk:我最近做的R太多了,那里的程序通常被称为脚本。
猜你喜欢
  • 1970-01-01
  • 2022-07-31
  • 1970-01-01
  • 2018-04-06
  • 2021-05-07
  • 2015-06-28
  • 1970-01-01
  • 1970-01-01
  • 2011-01-03
相关资源
最近更新 更多