中级C ++开发人员的棘手面试问题

Ser*_*i P 48 c++

面试中有人问我这个问题,我真的不明白这里发生了什么。问题是“控制台中将显示什么?”

#include <iostream>

int main()
{
    unsigned long long n = 0;
    ((char*)&n)[sizeof(unsigned long long)-1] = 0xFF;

    n >>= 7*8;

    std::cout << n;
}
Run Code Online (Sandbox Code Playgroud)

这是怎么回事,一步一步?

bol*_*lov 83

让我们一次完成这一步骤:

((char*)&n)
Run Code Online (Sandbox Code Playgroud)

这会将变量的地址n从转换unsigned long long*char*。这是合法的,实际上通过char指针访问不同类型的对象是该语言接受的少数“类型调整”情况之一。实际上,这使您可以将对象的内存n作为字节数组来访问(char在C ++中为aka )

((char*)&n)[sizeof(unsigned long long)-1]
Run Code Online (Sandbox Code Playgroud)

您访问对象的最后一个字节n。切记sizeof以字节为单位返回数据类型的维数(在C ++中char具有字节的更改自我)

((char*)&n)[sizeof(unsigned long long)-1] = 0xFF;
Run Code Online (Sandbox Code Playgroud)

您可以设置的最后一个字节n的值0xFF

由于n0最初的布局内存n现在是:

00  .. 00 FF
Run Code Online (Sandbox Code Playgroud)

现在注意...我放在中间。这不是因为我懒于复制粘贴字节数所n具有的值,而是因为unsigned long long标准未将大小设置为固定尺寸。有一些限制,但具体实现因实施而异。因此,这是第一个“未知”。但是,在大多数现代体系结构上,它sizeof (unsigned long long)是8,因此我们将继续讨论这个问题,但是在认真的采访中,您应该提到这一点。

另一个“未知”是如何解释这些字节。无符号整数只是简单地以二进制编码。但这可以是小端或大端。x86的字节序不大,因此我们以它为例进行说明。再一次,在认真的采访中,您应该提到这一点。

n >>= 7*8;
Run Code Online (Sandbox Code Playgroud)

该右移值n56次。注意,现在我们谈论的是的值n,而不是内存中的字节。与我们的假设(大小8,小端排序)的存储器中的编码的值被0xFF000000 00000000如此移位它7*8倍将导致值0xFF255

因此,假设sizeof(unsigned long long)是,8并且使用一点字节序编码的程序将打印255到控制台。


如果我们谈论的是大端系统,则将最后一个字节设置为后的内存布局0xff仍然相同:00 ... 00 FF,但是现在编码的值为0xFF。因此,结果n >>= 7*8;将为0。在大端系统中,程序将打印0到控制台。


如评论中所指出的,还有其他假设:

  • char是8位。尽管sizeof(char)可以保证为1,但不必具有8位。我所知道的所有现代系统都有以8位字节分组的位。

  • 整数不必大或小端。可能还有其他排列方式,例如中端排列。如今,除了大尾数之外,其他东西都被认为是深奥的。

  • 从技术上讲,只要单个位的位置一致,则无符号数字可以使用小端或大端的值模式。显然,某些旧系统确实在某些情况下使用了“中端”模式,但是如今,是的,您通常会找到两者之一。 (4认同)
  • 模拟设备有一系列针对音频DSP应用的芯片,其中sizeof(char)== sizeof(short)== sizeof(int)==1。存储器的最小可寻址单元为32位,这对于每种语言而言都是完全有效的。 (3认同)
  • 另一个假设:char是8位?7位或9位字符怎么样? (2认同)

Gar*_*ryO 7

将地址n转换为指向chars的指针,将第7个(假设sizeof(long long)== 8)char元素设置为0xff,然后将结果(作为long long)右移56位。