C/ c++指针技巧修复

C/C++ pointer trick fix

本文关键字:指针 c++      更新时间:2023-10-16

我正在尝试这个指针技巧,我不知道如何修复它,我在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 << arr[3] << endl;的输出是2。我想要得到的输出既不是2也不是50,而是一个很大的数字,表明int位模式的左边部分已经改变,而存储在arr[3]中的int的右边部分(第二个2位)仍然不变。

sizeof(int*)sizeof(short*)都将是相同的——sizeof(void*)也是一样——你要求的是指针的大小,而不是指针所指向的对象的大小。

使用sizeof(int)sizeof(short)代替。


现在,对于您的代码片段,您正在对正在运行的机器的端序进行假设。在给定的平台上,int的"第一部分"可以是具有较高地址的字节,也可以是具有较低地址的字节。

例如,你的内存块可能是这样布局的。假设最低位字节的索引是0,最高位字节的索引是1。在大端序架构中,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:UsersBillyDesktop>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:UsersBillyDesktop>cl /W4 /EHsc /nologo example.cpp && example.exe
example.cpp
Size of int is 4 and size of short is 2
2
131074

您的问题是由于端序。Intel的CPU是小端序的,这意味着int类型的第一个字节存储在第一个地址中。让我来举个例子:

假设arr[3]位于地址10:

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

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

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

10:  0x02
11:  0x00

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

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

为什么不直接使用位掩码:

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

这应该设置上部16个字节而不干扰下部16个字节。

说了以上这些,标准禁止这样做:通过指向另一类型的指针设置变量是调用未定义行为。如果你知道你的机器是如何工作的(intshort的大小,字节序,…)你知道你的编译器(很可能)是如何翻译你的代码的,那么你可能会逃脱它。当机器/编译器/月相发生变化时,会发生壮观的爆炸。

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

你不希望你的实际指针的大小是两个字节;这意味着它只能访问~16k的内存地址。然而,使用对short *的强制类型转换,将允许您每两个字节访问一次内存(因为编译器将该数组视为short数组,而不是int数组)。