g ++ / gcc中char的签名及其历史

时间:2015-01-06 17:15:36

标签: c++ gcc char g++

首先让我先说我知道charsigned charunsigned char是C ++中的不同类型。通过快速阅读标准,似乎charsigned是否是实现定义的。为了让事情变得更有趣,我们会g++确定char是否基于每个平台signed

所以无论如何,在这个背景下,让我介绍一下我使用这个玩具程序遇到的一个错误:

#include <stdio.h>

int main(int argc, char* argv[])
{
    char array[512];
    int i;
    char* aptr = array + 256;

    for(i=0; i != 512; i++) {
        array[i] = 0;
    }

    aptr[0] = 0xFF;
    aptr[-1] = -1;
    aptr[0xFF] = 1;
    printf("%d\n", aptr[aptr[0]]);
    printf("%d\n", aptr[(unsigned char)aptr[0]]);

    return 0;
}

预期行为是对printf的两次调用都应该输出1.当然,在gcc上运行的g++ 4.6.3linux/x86_64上发生的事情是第一个printf {1}}输出-1而第二个输出1.这与被签名的字符和g++明确地解释-1的负数组索引(这是技术上未定义的行为)一致。

该错误似乎很容易修复,我只需要将char转换为unsigned,如上所示。我想知道的是,是否希望此代码在使用gcc/g++的x86或x86_64计算机上正常工作?看起来这可能在ARM平台上有效,显然字符是无符号的,但是我想知道这个代码在使用g++的x86机器上是否总是有问题?

4 个答案:

答案 0 :(得分:4)

我在程序中看不到任何未定义的行为。负数组索引不一定无效,只要将索引添加到前缀的结果是指有效的内存位置即可。 (如果前缀是数组对象的名称或指向数组对象的第0个元素的指针,则负数组索引无效(即,具有未定义的行为),但这不是这种情况。)

在这种情况下,aptr指向512个元素数组的元素256,因此有效索引从-256到+255(+256产生一个有效的地址,刚好超过数组的末尾,但是它不能被解除引用)。假设CHAR_BIT==8signed charunsigned char或普通char中的任何一个都有一个范围,该范围是数组有效索引范围的子集。

如果签署了普通char,那么:

aptr[0] = 0xFF;

会隐式将int0xFF255)转换为char,并且该转换的结果是实现定义的 - 但它将在普通char的范围,几乎肯定是-1。如果普通char未签名,则会将值255分配给aptr[0]。因此,代码的行为取决于普通char的签名(可能还有实现定义的超出范围值转换为有符号类型的结果),但没有未定义的行为。

(将一个超出范围的值转换为带符号的类型也可以从C99开始,引发一个实现定义的信号,但我知道没有实现这样做的实现。在{{的转换上引发一个信号1}}到0xFF可能会破坏现有的代码,因此编译器编写者非常积极地不这样做。)

答案 1 :(得分:1)

数组的类型与索引无关(底层内存访问除外)。

例如:

signed int a[25];
unsigned int b[25];

int value = a[-1];
unsigned int u_value = b[-5];

两种情况的索引公式为:

memory_address = starting_address_of_array
               + index * sizeof(array_type);

char而言,无论是(根据语言规范的定义),它的大小都是1。

算术表达式中char的用法可能取决于它是有符号还是无符号。

答案 2 :(得分:0)

  

预期行为是对printf的两次调用都应该输出1

你确定吗?

aptr [0]的rval是一个带符号的char,它是-1,它再次用于索引到aptr [],因此第一个printf()得到的是-1。

同样适用于第二个printf但是在那里,使用类型转换确保它被解释为unsigned char,因此你最终得到255,并使用它来索引到aptr []你得到1来自第二个printf()。

我相信你对预期行为的假设是不正确的。

编辑1:

  

看起来这显然可以在ARM平台上运行   字符是无符号的,但我想知道这段代码是否总是如此   在使用g ++的x86机器上有错误吗?

基于此声明,您似乎知道x86上的char已签名(与某些人假设您所假设的内容相对)。因此我提供的解释应该是好的,即将char视为x86上的signed char。

编辑2:

  

只要指针使用负数组索引就完全没问题了   操作数是一个内部元素:   stackoverflow.com/questions/3473675/negative-array-indexes-in-c -   ecatmur

这是@ecatmur对问题的评论之一。这说明负面指数与某些人的想法相反是好的。

答案 3 :(得分:0)

您的printf语句与以下内容相同:

printf("%d\n", aptr[(char)255]);
printf("%d\n", aptr[(unsigned char)(char)255]);

因此显然取决于平台在这些转换中的行为。

  

我想知道的是,这个代码是否可以在使用gcc / g ++的x86或x86_64机器上正常工作?

正确地考虑&#39;表示您描述的行为,不,这应该永远不会在char签名的平台上以这种方式表现。

char签名(并且不能代表255)时,您将获得一个实现定义的值,并且在可表示的范围内。对于8位,2的补码表示,这意味着您在[-128,127]范围内得到一些值。这意味着唯一可能的输出:

printf("%d\n", aptr[(char)255]);

&#34; 0&#34;和&#34; -1&#34; (忽略printf失败的情况)。常见的实现定义了转换结果打印&#34; -1&#34;。


代码定义良好但在定义不同char签名的实现之间无法移植。编写可移植代码包括不依赖于char被签名或无符号,这反过来意味着如果索引被限制在[0,127]范围内,则只应使用char值作为数组索引。