键入一个整数值到char指针时会发生什么?

时间:2017-12-03 10:23:45

标签: c pointers casting

例如,

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;
}

并且程序按预期工作,但是当你输入一个整数值(没有将整数赋给变量)到字符指针中时会发生什么?这个程序总能运作吗? 感谢。

3 个答案:

答案 0 :(得分:4)

C99:

  

6.3.2.3指针

     
      
  1. 一个整数常量表达式,其值为0,或者这样的表达式强制转换为类型   void *被称为空指针常量。如果将空指针常量转换为a   指针类型,结果指针,称为空指针,保证比较不等   指向任何对象或函数的指针。
  2.         

    [...]

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

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

特别是:如果它确实是一个陷阱表示,那么你的代码就会违反:

  

6.2.6类型表示

     

6.2.6.1一般

     

[...]

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

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

答案 1 :(得分: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),我可以(有时)将其用作一个sentinel非空指针值(应该 derefenced)。但这使我的代码更少portable

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

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

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

  

这个程序总能运作吗?

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

(解释为什么你的程序看起来像你想要的那样非常困难,因为你需要深入了解几个实现细节)

答案 2 :(得分:1)

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

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

更好的方法是为类型为integerToString的{​​{1}}创建一个参数,并将其用作布尔值来表示失败。然后,不会为错误处理保留一个int *值。

或者使用C ++和异常。