【问题标题】:Cleaner pointer arithmetic syntax for manipulation with byte offsets使用字节偏移量进行操作的更简洁的指针算术语法
【发布时间】:2009-10-26 04:04:28
【问题描述】:

在下面的代码行中,我需要将指针 pm 调整为其中一个字段中的字节偏移量。有没有比在char *PartitionMap * 不停地来回转换以使指针算术仍然有效的更好/更简单的方法?

PartitionMap *pm(reinterpret_cast<PartitionMap *>(partitionMaps));
for ( ; index > 0 ; --index)
{
    pm = (PartitionMap *)(((char *)pm) + pm->partitionMapLength);
}
return pm;

对于那些无法从代码中理解的人,它正在循环通过缓冲区中的可变长度描述符,该缓冲区继承自 PartitionMap

同样对于相关人员,partitionMapLength 始终返回运行该系统的系统支持的长度。我正在遍历的数据符合UDF 规范。

【问题讨论】:

  • 循环实际上在做什么?似乎 pm 被分配了具有相同值的“索引”时间
  • 它将 pm 的值更改为在 pm->partitionMapLength 中找到的值,然后再次这样做,再一次……大概,数据结构有韵律和原因,但它似乎有点不寻常。它允许通过数据进行可变大小的步骤。
  • 是的,乔纳森,我这样做是为了逐步检查从设备读取的扇区大小的原始字节块。

标签: c++ c pointers char pointer-arithmetic


【解决方案1】:

我经常为此使用这些模板:

    template<typename T>
    T *add_pointer(T *p, unsigned int n) {
            return reinterpret_cast<T *>(reinterpret_cast<char *>(p) + n);
    }

    template<typename T>
    const T *add_pointer(const T *p, unsigned int n) {
            return reinterpret_cast<const T *>(reinterpret_cast<const char *>(p) + n);
    }

它们维护类型,但向它们添加单个字节,例如:

T *x = add_pointer(x, 1); // increments x by one byte, regardless of the type of x

【讨论】:

  • 一般来说,向结构指针添加一个字节会导致 SIGBUS 错误(核心转储)——至少在结构和指针必须正确对齐的机器上是这样。这并没有阻止这些函数变得有用 - 但该示例可能有些不足之处。
  • 好主意,我经常为其他事情编写这样的模板包装器,C++ 有时是一个 PITA。
  • 不过,乔纳森,我很困惑你会做什么。
  • GMan:您可以使用 memcpy 将未对齐缓冲区中的字节复制到正确对齐的结构变量中。
  • 处理原始套接字的网络代码是一个很好的例子,当你想要前进一组字节而不是整个结构时。如果您有一个指向 IP 标头的指针,并且您想要访问 TCP 标头,则需要提前特定数量的字节(由于 IP 选项,这可能是可变的)。在处理低级压缩结构时,有时指针算法只是正确的解决方案。
【解决方案2】:

强制转换是唯一的方法,无论是转换为 char* 还是 intptr_t 或其他类似类型,然后转换为您的最终类型。

【讨论】:

  • +1。你可以编写一个宏来清理你的代码的外观,如果那是你所追求的。
  • 或者一个函数,正如 Evan 演示的那样。并且函数通常比宏更受欢迎。
【解决方案3】:

您当然可以只保留两个变量:char * 用于单步通过缓冲区,PartitionMap * 用于访问它。让事情变得更加清晰。

for (char *ptr = ??, pm = (PartitionMap *)ptr ; index > 0 ; --index)
{
    ptr += pm->partitionMapLength;
    pm = (PartitionMap *)ptr;
}
return pm;

【讨论】:

  • 除了你给的代码是错误的,这比我有的更整洁。
【解决方案4】:

正如其他人所提到的,您需要演员表,但您可以在宏或函数中隐藏丑陋。但是,要记住的另一件事是对齐要求。在大多数处理器上,您不能简单地将指向类型的指针增加任意字节数并将结果转换回指向原始类型的指针,而不会因为未对齐而通过新指针访问结构。

x86 架构是少数几个可以让您摆脱它的架构之一(即使它是最流行的)。但是,即使您是为 Windows 编写代码,也需要考虑到这个问题 - Win64 确实强制要求对齐。

因此,即使通过指针访问partitionMapLength 成员也可能会使您的程序崩溃。

您或许可以在 Windows 上使用像 __unaligned 这样的编译器扩展轻松解决此问题:

PartitionMap __unaliged *pm(reinterpret_cast<PartitionMap *>(partitionMaps));
for ( ; index > 0 ; --index)
{
    pm = (PartitionMap __unaligned *)(((char *)pm) + pm->partitionMapLength);
}
return pm;

或者您可以将可能未对齐的数据复制到正确对齐的结构中:

PartitionMap *pm(reinterpret_cast<PartitionMap *>(partitionMaps));

char* p = reinterpret_cast<char*>( pm);

ParititionMap tmpMap;
for ( ; index > 0 ; --index)
{

    p += pm->partitionMapLength;

    memcpy( &tmpMap, p, sizeof( newMap));
    pm = &tmpMap;
}

// you may need a more spohisticated copy to return something useful
size_t siz = pm->partitionMapLength;
pm = reinterpret_cast<PartitionMap*>( malloc( siz));
if (pm) {
    memcpy( pm, p, siz);
}
return pm;

【讨论】:

  • 据我所知,Windows 默认不会在 x64 上强制执行它?它在 Itanium 上确实如此,但这是一个相当罕见的情况。不过,对齐检查可以在 x86 和 x64 上强制执行,当然,未对齐的访问会严重影响性能。
  • 看起来你是对的 - 前段时间在将东西移植到安腾时,我学会了让我的代码对齐清晰。我认为 MS 在 x64 上默认保持严格的对齐要求,但看起来我错了。
【解决方案5】:

必须进行强制转换,但这会使代码几乎不可读。为便于阅读,请将其隔离在 static inline 函数中。

【讨论】:

    【解决方案6】:

    让我感到困惑的是为什么你有 'partitionMapLength' 以字节为单位?

    如果它在'partitionMap'单位中不是更好吗,因为你无论如何都会施放它?

    PartitionMap *pmBase(reinterpret_cast<PartitionMap *>(partitionMaps));
    PartitionMap *pm;
    ...
    pm = pmBase + index; // just guessing about your 'index' variable here
    

    【讨论】:

    • 不,它们的长度各不相同。除非你知道一种神奇的方法来计算长度:)
    • 我没有编写分区映射规范,我只是将其转换为 C++ 结构,现在我正在处理这些值。
    • 结构以一些可变长度数据结尾的情况并不少见。这段代码将遍历此类结构的“打包数组”。
    【解决方案7】:

    C 和 C++ 都允许您通过指针和 ++ 遍历数组:

    #include <iostream>
    
    int[] arry = { 0, 1, 2, 3 };
    int* ptr = arry;
    while (*ptr != 3) {
        std::cout << *ptr << '\n';
        ++ptr;
    }
    

    为此,添加到指针被定义为获取存储在指针中的内存地址,然后添加任何类型的大小乘以添加的值。例如,在我们的示例中,++ptr1 * sizeof(int) 添加到存储在ptr 中的内存地址。

    如果您有一个指向类型的指针,并且想要从该位置推进特定数量的字节,那么这样做的唯一方法是强制转换为char*(因为sizeof(char) 被定义为一个)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-04-21
      • 1970-01-01
      • 1970-01-01
      • 2023-03-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多