【问题标题】:Is accessing an array element using a char undefined behaviour?是否使用 char 未定义行为访问数组元素?
【发布时间】:2016-03-04 18:37:58
【问题描述】:

由于不清楚什么是未定义的行为,什么不是 C 语言,我想知道使用 char 访问数组元素是否是未定义的行为。例如:

char c = 'A';
int a[3000];
printf("%i\n", a[c]);

我知道实际上 chars 和 int 是可以互换的,但我仍然不确定。

【问题讨论】:

  • 这取决于'A'的当前编码值是什么。如果它是 ASCII,那么值是 65,这是一个有效的索引,所以它是定义良好且行为良好的。
  • 我从来没有测试过这个,因为它很糟糕,但如果它是 ASCII 它应该可以工作。
  • 哦,intchar 并不是真正“可互换”的,在许多情况下,编译器可以将promotechar 转换为int,或者convert 和@其他场合987654330@转char
  • a[c] 被转换为 *(&a+c),在你的例子中 c 是 65,所以你会得到第 66 个,a[0] 是数组的第一个成员。但是在您的示例中,数组未初始化,因此您将从堆栈中获得一个随机整数。
  • @nbro 除非类型是 unsigned char 或等效类型,否则访问像 int a[3000] 这样的未初始化数据会引发陷阱表示。每个规格都是 UB。

标签: c undefined-behavior


【解决方案1】:

从语法上讲,a[c] 是一个有效的表达式,只要 c 是整数类型或可以提升为整数类型。

来自 C99 标准:

6.5.2.1 数组下标

1 一个表达式的类型应为“指向对象类型的指针”,另一个表达式应为整数类型,结果的类型为“类型”。

如果c的值。 after 提升为int,在数组的范围内,那么运行时应该没有问题。

【讨论】:

    【解决方案2】:

    是否使用 char 未定义行为访问数组元素?

    这不是未定义的行为。它像另一种整数类型一样工作。然而,char 的数值可能令人惊讶地是负数。


    charsigned charunsigned char 具有相同的范围。它是实现定义的。

    使用c 作为索引很好,如果提升的索引加上指针导致有效的内存地址。详细信息:char 将被提升为 int,或者可能是 unsigned

    如果c 具有负值,则可能存在以下问题。在 OP 的情况下,使用 ASCII 编码,'A' 的值是 65,所以它不存在0 <= 65 < 3000 的问题。 @Joachim Pileborg

    char c = 'A';
    int a[3000] = { 0 };
    printf("%i\n", a[c]);  // OK other than a[] not initialize in OP's code.
    

    【讨论】:

    • charunsigned char 可以容纳 0…255 , signed char 只能容纳 -128…127 。二进制数字是相同的,但分配给其他类型或转换的方式不同。甚至算术也必须仔细考虑,因为没有上溢或下溢警告或纠正,而这很容易发生,只需 8 位。 u ± s 其中 u 无符号且 s 有符号已明确定义,只需遵循二进制即可,但粗心的人会感到意外。
    • @Arif Burhan char, unsigned char, singed char 可能有你提到的范围。 C 规范不同意它们必须具有这些范围。不同意?尝试发布一个问题。由于缺少上下文,您的其余评论不清楚如何应用于此答案。
    • @Arif Burhan char 的范围是 [CHAR_MIN ... CHAR_MAX]unsigned char 的范围是 [0 ... UCHAR_MAX]signed char 的范围是 [SCHAR_MIN ... SCHAR_MAX]最小值 SCHAR_MAX 的值为 127。UCHAR_MAX最小值 值为 255。UCHAR_MAX 可以是 65,535 或 pow(2,32)-1 或其他值。它必须是power-of-2 - 1
    • @Arif Burhan 关于“即使算术也必须仔细考虑,因为没有溢出或下溢警告或更正,”。无符号数学确实有一个“更正”,因为任何操作的结果都是明确定义的,答案是“类型的最大值 + 1”的模
    • 如果 signed char 溢出少量,它会变小和负数,对于 chars 没问题,但对于 signed short 可能是致命的,可能存储在内存本身,稍后用作数组索引或内存或结构偏移量。 @chux
    【解决方案3】:

    它大部分都可以工作,但要小心非 ASCII 字符,其值 > 127

    如果char 被签名,它将被提升为一个整数,从而导致访问数组之外​​的内存!

    这是一个简单的实现中的常见错误,例如tolower()

    【讨论】:

      【解决方案4】:

      这应该自动转换为 int 并转到数组的那个元素,所以行为不是未定义的。但是,从来没有真正的理由这样做。即使您从 ' '(ASCII 十进制值 32)开始,您也不会使用它之前的其他 32 个值。

      我认为您可能正在尝试制作一个非常基本的哈希表。这可以通过一个结构和一些函数轻松完成;使用除整数类型(即使 char 可以转换为 int)以外的任何内容作为数组下标通常是不好的做法。

      【讨论】:

        【解决方案5】:

        据我所知,我会说它不是未定义的,而是定义明确的。原因:char 可以提升为integer,这是索引数组的有效方法(或者更好地说:指针,数组在该表达式中衰减为该指针)。索引与加法基本相同:

        pointer + index // same as &(pointer[index]) or &(index[pointer])
        

        并且,引用http://en.cppreference.com/w/cpp/language/implicit_cast(在“数字促销”下):

        [..] 小整数类型(如char)的纯右值可以转换为较大整数类型(如int)的纯右值。特别是,算术运算符不接受小于 int 的类型作为参数、[..]

        不过,AFAIK 编译器会发出警告,因为通常您不使用 char 作为索引,因此编译器会尝试提供额外的安全网。

        【讨论】:

          【解决方案6】:

          简短的回答是:代码片段无法编译。

          中间答案是:如果是函数定义的一部分,则代码具有未定义的行为,因为它访问了未初始化的对象。

          长答案是:使用正确初始化的数组,它仍然取决于:

          • 表达式a[c]中的c在计算数组索引之前将被提升为int,并且C标准要求'A'具有正值,无论是否输入char签名或未签名。如果 char 类型有 8 位,则行为不会未定义,但定义为 'A' 的实际值的实现取决于目标架构。

          • 1234563有未定义的行为。

          【讨论】:

            猜你喜欢
            • 2017-04-10
            • 1970-01-01
            • 2020-09-26
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-03-31
            • 2013-07-20
            • 1970-01-01
            相关资源
            最近更新 更多