【问题标题】:C/C++ pointer trick fixC/C++ 指针技巧修复
【发布时间】:2013-02-27 06:17:19
【问题描述】:

我正在尝试这个指针技巧,但我不知道如何修复它,我在 ubuntu 12.04 64 位上运行 g++ 4.6。查看下面的代码:

int arr[5];
arr[3] = 50;
((short*) arr)[6] = 2;
cout << arr[3] << endl;

逻辑是:由于short是2个字节,int是4个字节,我想改变arr[3]中的前2个字节,同时保持后2个字节的值为50。所以我'我只是弄乱了位模式。不幸的是,sizeof(int*)sizeof(short*) 都是 8 个字节。是否有返回大小为 2 字节的指针的类型转换?

更新:

我意识到这个问题写得不好,所以我会解决这个问题: 我得到的cout &lt;&lt; arr[3] &lt;&lt; endl;的输出是2。我想得到的输出既不是2也不是50,而是一个很大的数字,说明int位模式的左边部分已经改变,而右边部分存储在 arr[3] 中的 int (第二个 2 位)仍然保持不变。

【问题讨论】:

  • 您正在操作整数位,而不是指针。指针的大小无关紧要。您的问题出在其他地方。
  • 记住[] 操作员是在做一个尊重。 ((short*) arr)[6] 是一个 short 大小的值。
  • 我同意你的观点,但这是我能想到的唯一解释为什么它不起作用:s
  • @naxchange:什么“不起作用”?您看到的行为是什么,而您期望的行为是什么?
  • 如果您尝试以这种方式操作数据,您将遇到字节交换和字节序问题。

标签: c++ c pointers dereference


【解决方案1】:

sizeof(int*)sizeof(short*) 都将是相同的——就像sizeof(void*) 一样——你要求的是指针的大小,而不是指针指向的东西的大小。

请改用sizeof(int)sizeof(short)


现在,对于您的代码 sn-p,您正在对正在运行的机器的 endianness 进行假设。在给定平台上int 的“第一”部分可能是地址较高的字节,也可能是地址较低的字节。

例如,您的内存块可能是这样布置的。假设最低有效字节的索引为零,而最高有效字节的索引为一。在大端架构上,int 可能如下所示:

 <------------- 4 bytes --------------->
+---------+---------+---------+---------+
| int:3   | int:2   | int:1   | int:0   |
| short:1 | short:0 | short:1 | short:0 |
+---------+---------+---------+---------+

注意 int 中的第一个 short (在您的情况下应该是 ((short*) arr)[6])如何包含 int 的最高有效位,而不是最低有效位。因此,如果您覆盖((short*) arr)[6],您将覆盖arr[3] 的最高有效位,这似乎是您想要的。但是 x64 不是大端机器。

在小端架构上,您会看到以下内容:

 <------------- 4 bytes --------------->
+---------+---------+---------+---------+
| int:0   | int:1   | int:2   | int:3   |
| short:0 | short:1 | short:0 | short:1 |
+---------+---------+---------+---------+

导致相反的行为——((short*) arr)[6] 将是 arr[3] 的最低有效位,((short*) arr)[7] 将是最高有效位。


这是我的机器发生的事情——你的机器可能不同:

C:\Users\Billy\Desktop>type example.cpp
#include <iostream>

int main()
{
        std::cout << "Size of int is " << sizeof(int) << " and size of short is "
                  << sizeof(short) << std::endl;

        int arr[5];
        arr[3] = 50;
        ((short*) arr)[6] = 2;
        std::cout << arr[3] << std::endl;
        ((short*) arr)[7] = 2;
        std::cout << arr[3] << std::endl;
}


C:\Users\Billy\Desktop>cl /W4 /EHsc /nologo example.cpp && example.exe
example.cpp
Size of int is 4 and size of short is 2
2
131074

【讨论】:

  • 这个问题并不意味着需要sizeof(int)...short... - 只是担心索引无法按预期工作。
  • @TonyD:这个问题写得有点糟糕。我在这个问题中看到的唯一问题(即以? 结尾)是讨论sizeof(int*)sizeof(short*)。除此之外,OP 并不清楚他看到的行为与他预期的行为。
  • 那么,我可以断定((short*) arr)[7] = 2; 会解决我正在寻找的问题吗?
  • 是的,是的。谢谢一百万!
  • @naxchange:是​​的。抱歉,我把那里的所有地方都弄错了。
【解决方案2】:

您的问题是由于endianness。 Intel CPU 是 little endian,这意味着 int 的第一个字节存储在第一个地址中。让我举个例子:

假设 arr[3] 在地址 10:

然后arr[3] = 50;将以下内容写入内存

10:   0x32
11:   0x00
12:   0x00
13:   0x00

((short*) arr)[6] = 2; 将以下内容写入内存

10:  0x02
11:  0x00

【讨论】:

  • +1 -- 但是任何将int 存储在非对齐地址的人都应该被枪决:)
【解决方案3】:

当你索引一个指针时,它会加上索引乘以所指向类型的大小。所以,你不需要一个 2 字节的指针。

【讨论】:

    【解决方案4】:

    你做了很多可能站不住脚的假设。另外,指针的大小与手头的问题有什么关系?

    为什么不只使用位掩码:

    arr[3] |= (top_2_bytes << 16);
    

    这应该设置高 16 字节而不干扰低 16。(你可能会进入有符号/无符号的戏剧)

    【讨论】:

      【解决方案5】:

      综上所述,标准禁止做这样的事情:通过指向另一个类型的指针设置变量是调用undefined behaviour如果您知道您的机器是如何工作的(intshort 的大小,字节序,...)并且您知道您的编译器(可能)如何翻译你的代码,然后你可能会侥幸逃脱。适用于整洁的客厅技巧,以及当机器/编译器/月相变化时的壮观爆炸。

      如果它赢得任何性能,胜利将是最小的,甚至可能是净损失(很久以前我摆弄过一个编译器,玩“我可以比编译器更好地实现这个循环,我确切地知道发生了什么在这里”为我的`label: ... if() goto label; 生成了很多比循环编写的代码更糟糕的代码:我的“智能”代码混淆了编译器,它的“循环模式”不适用)。

      【讨论】:

        【解决方案6】:

        你不希望你的实际指针大小为两个字节;这意味着它只能访问约 16k 的内存地址。但是,使用转换为短 * 将让您每两个字节访问一次内存(因为编译器会将数组视为短数组,而不是整数)。

        【讨论】:

          猜你喜欢
          • 2011-11-20
          • 1970-01-01
          • 2013-10-09
          • 2010-11-17
          • 2010-10-13
          • 2011-01-21
          • 1970-01-01
          • 2011-04-14
          • 2011-12-08
          相关资源
          最近更新 更多