ASCII字符串和字节序

时间:2009-10-14 18:12:51

标签: c ascii endianness

与我一起工作的实习生向我展示了他在计算机科学方面考虑的关于字节序问题的考试。有一个问题显示ASCII字符串“My-Pizza”,学生必须显示该字符串将如何在小端计算机的内存中表示。当然,这听起来像一个技巧问题,因为ASCII字符串不受端序问题的影响。

但令人震惊的是,实习生声称他的教授坚持认为该字符串将表示为:

P-yM azzi

我知道这不可能是正确的。在任何机器上都不能像ASCII字符串那样表示ASCII字符串。但显然,教授坚持这样做。所以,我写了一个小C程序并告诉实习生把它交给他的教授。

#include <string.h>
#include <stdio.h>

int main()
{
    const char* s = "My-Pizza";
    size_t length = strlen(s);
    for (const char* it = s; it < s + length; ++it) {
        printf("%p : %c\n", it, *it);
    }
}

这清楚地表明字符串在内存中存储为“My-Pizza”。一天后,实习生回到我身边,告诉我教授现在声称C正在自动转换地址,以正确的顺序显示字符串。

我告诉他他的教授很疯狂,这显然是错的。但是为了检查我自己的理智,我决定在stackoverflow上发布这个,所以我可以让其他人确认我在说什么。

所以,我问:谁在这里?

13 个答案:

答案 0 :(得分:26)

毫无疑问,你是对的。

ANSI C标准6.1.4指定通过“连接”文字中的字符将字符串文字存储在内存中。

ANSI标准6.3.6还规定了加法对指针值的影响:

  

当向指针添加或从指针中减去具有整数类型的表达式时,结果具有指针操作数的类型。如果指针操作数指向数组对象的元素,并且数组足够大,则结果指向偏离原始元素的元素,使得结果元素和原始数组元素的下标的差异等于整数表达式。

如果归因于此人的想法是正确的,那么当整数用作数组索引时,编译器也必须使用整数数学。许多其他谬误也会产生想象力。

这个人可能会感到困惑,因为(与字符串初始化程序不同),多字节字符常量(如'ABCD'以字节顺序存储。

一个人可能会对此感到困惑的原因有很多。正如其他人在这里建议的那样,他可能误读了他在调试器窗口中看到的内容,其中内容已被字节交换以便读取int值。

答案 1 :(得分:16)

教授很困惑。为了看到像'P-yM azzi'这样的东西你需要采用一些内存检查工具,以'4字节整数'模式显示内存,同时给你一个高阶中每个整数的“字符解释”字节到低位字节模式。

当然,这与字符串本身无关。并且说字符串本身在小端机器上表示是完全无稽之谈。

答案 2 :(得分:10)

如果我们谈论的是每个字符使用8位的系统,那么教授就错了。

我经常使用实际使用16位字符的嵌入式系统,每个字都是little-endian。在这样的系统上,字符串“My-Pizza”确实会被存储为“yMP-ziaz”。

但只要它是一个每字符8位的系统,该字符串将始终存储为“My-Pizza”,与高级架构的字节序无关。

答案 3 :(得分:9)

Endianness定义多字节值中的字节顺序。字符串是单字节值的数组。因此,每个值(字符串中的字符)在小端和大端架构上都是相同的,并且字节序不会影响结构中值的顺序。

答案 4 :(得分:8)

你可以很容易地证明编译器没有进行这种“神奇”的转换,通过在一个不知道它传递了字符串的函数中进行打印:

int foo(const void *mem, int n)
{
    const char *cptr, *end;
    for (cptr = mem, end = cptr + n; cptr < end; cptr++)
        printf("%p : %c\n", cptr, *cptr);
}

int main()
{
    const char* s = "My-Pizza";

    foo(s, strlen(s));
    foo(s + 1, strlen(s) - 1);
}

哎呀,你甚至可以用gcc -S编译成汇编并最终确定没有魔法。

答案 5 :(得分:2)

  

但令人震惊的是,实习生声称他的   教授坚持认为弦乐   将表示为:

     

P-yM azzi

它将被表示为,表示为什么?用户表示为32位整数转储?或者在计算机的记忆中表示/布局为P-yM azzi?

如果教授说“My-Pizza”会在计算机内存中表示/布局为“P-yM azzi”,因为计算机是小端架构,请有人教授教授如何使用调试器!我认为这是所有教授的困惑源于此,我有一个暗示教授不是编码器(不是我看不起教授),我认为他没有一种在代码中证明他对endian-ness的了解的方法。

也许教授在大约一个星期前就学会了这些东西,然后他只是错误地使用调试器,很快就对他对计算机的新独特见解感到高兴,然后立即将它传授给他的学生。

如果教授说机器的结束与ascii字符串在记忆中的表现有关,他需要清理他的行为,有人应该纠正他。

如果教授给出了一个例子,而不是根据机器的字节顺序,机器中的整数是如何表示/布局的,那么他的学生就可以了解他所教的内容。

答案 6 :(得分:1)

我认为教授试图通过类比关于endian / NUXI问题来说明一点,但是当你将它应用于实际字符串时你是对的。不要因为他试图教会学生一个观点以及如何以某种方式思考问题而使这种情况脱轨。

答案 7 :(得分:1)

您可能感兴趣,可以在大端机器上模拟小端架构,反之亦然。编译器必须发出代码,当它解除引用时,它们会自动地用char*指针的最低有效位混乱:在32位机器上你映射00&lt; - &gt; 11和01&lt; - &gt; 10。

所以,如果你在big-endian机器上写下数字0x01020304,并用这个地址重新读回它的“第一”字节,那么你得到最不重要的字节,{{1} }。即使硬件是big-endian,C实现也是小端的。

短途访问需要类似的技巧。未对齐访问(如果支持)可能不引用相邻字节。您也不能将本机存储用于大于单词的类型,因为它们在一次读回一个字节时会出现字交换。

然而,显然,小端机器不会一直这样做,这是非常专家要求,它会阻止你使用本机ABI。对我来说听起来好像教授认为实际数字是“实际上”的大端,并且对于小端架构究竟是什么和/或它的记忆如何被表示深感困惑。

确实,字符串在32位le机器上“表示为”0x04,但只有“表示”是指“按地址增加的顺序读取表示的字词,但打印字节的字节”每个单词big-endian“。正如其他人所说,这是一些调试器内存视图可能会做的事情,因此它实际上是内存内容的 a 表示。但是如果你要表示单个字节,那么更常见的是按照增加地址的顺序列出它们,无论是否存储了b-e或l-e,而不是将每个单词表示为多字符文字。当然没有指针摆弄,如果教授选择的代表使他认为有一些,那就误导了他。

答案 8 :(得分:0)

另外,(我在很长一段时间没有玩过这个,所以我可能错了)他可能会想到pascol,其中字符串表示为“压缩数组”,IIRC是填充为4字节的字符整数?

答案 9 :(得分:0)

很难读懂教授的想法,当然编译器除了在BE和LE系统上将字节存储到相邻增加的地址之外没有做任何其他事情,但是 正常显示存储器中的字 - 大小的数字,无论字大小是什么,我们写一千个为1000。不是000,1。

$ cat > /tmp/pizza
My-Pizza^D
$ od -X /tmp/pizza
0000000 502d794d 617a7a69
0000010
$ 

记录中,y == 79,M == 4d。

答案 10 :(得分:0)

AFAIK,只有在想要将大值分解为小值时,字节序才有意义。因此我不认为C风格的字符串会受到影响。因为它们毕竟只是字符数组。当你只读一个字节时,如果你从左边或右边读它会怎么重要?

答案 11 :(得分:0)

我遇到了这个,觉得有必要清理它。这里没有人似乎已经解决了byteword s或如何address这些概念。 byte是8位。 word是一个字节集合。

如果电脑是:

  • 字节可寻址
  • 使用4字节(32位)字
  • 字对齐
  • 记忆和#34;物理&#34; (不倾倒和字节交换)
确实,教授是正确的。他没有表明这一点,证明他并不确切地知道他在说什么,但他确实理解了这个基本概念。

单词内的字节顺序:(a)Big Endian,(b)Little Endian

Byte Order Within Words: (a) Big Endian, (b) Little Endian

单词和整数数据:(a)Big Endian,(b)Little Endian

Character and Integer Data in Words: (a) Big Endian, (b) Little Endian

<强>参考

答案 12 :(得分:-1)

教授的“C”代码看起来像这样吗?如果是这样,他需要更新他的编译器。

main() {
    extrn putchar;
    putchar('Hell');
    putchar('o, W');
    putchar('orld');
    putchar('!*n');
}