【问题标题】:When to use unsigned char pointer何时使用无符号字符指针
【发布时间】:2013-01-24 01:29:45
【问题描述】:

unsigned char 指针有什么用?我在很多地方都看到过指针类型转换为指向unsinged char 的指针,我们为什么要这样做?

我们收到一个指向int 的指针,然后将它类型转换为unsigned char*。但是,如果我们尝试使用 cout 打印该数组中的元素,它不会打印任何内容。为什么?我不明白。我是 C++ 新手。

编辑下面的示例代码

int Stash::add(void* element)
{
    if(next >= quantity)
    // Enough space left?
        inflate(increment);

    // Copy element into storage, starting at next empty space:
    int startBytes = next * size; 
    unsigned char* e = (unsigned char*)element;
    for(int i = 0; i < size; i++)
        storage[startBytes + i] = e[i];
    next++;
    return(next - 1); // Index number
}

【问题讨论】:

  • 转换为字符指针时,第一个字节可能为零,与字符串终止符相同,因此不会打印任何内容。如果你能展示你真正在做什么,例如发布一些代码,它会更有帮助。请发送SSCCE 并添加到问题中。
  • 但是我认为如果第一个字节为零并且实际上我正在尝试打印所有四个字节但它没有打印任何内容。
  • 您的问题似乎更多是关于“为什么”而不是“何时”。很多时候,unsigned char * 被用作字节级访问方法,以访问其他更正式类型的变量或内存地址。它有很多优点,其中包括对严格的别名规则的免疫,以及与你扔给它的任何地址的标准保证对齐。如果您对 C 相当熟悉,那么 C++ 的新手应该不会觉得这很困难。编程 的新手,我认为这是一个难以理解的挑战。也许你有一些代码和背后的想法你有疑问?
  • 我将您的代码移到了您的问题中。您可以在问题中发布任何关于它的 cmets,或对其他 cmets 的回复,都可以在此处发布。

标签: c++ pointers


【解决方案1】:

你其实是在找pointer arithmetic:

unsigned char* bytes = (unsigned char*)ptr;
for(int i = 0; i < size; i++)
    // work with bytes[i]

在本例中,bytes[i] 等于*(bytes + i),用于访问地址为bytes + (i* sizeof(*bytes)) 的内存。换句话说:如果您有int* intPtr 并尝试访问intPtr[1],您实际上是在访问存储在字节:4 到 7 处的整数:

0  1  2  3
4  5  6  7 <-- 

指针指向的类型的大小会影响它在递增/递减后指向的位置。因此,如果您想逐字节迭代数据,则需要有一个指向大小为 1 字节的类型的指针(这就是 unsigned char* 的原因)。


unsigned char 通常用于保存二进制数据,其中0 是有效值并且仍然是数据的一部分。在使用“naked”unsigned char* 时,您可能必须保持缓冲区的长度。

char 通常用于保存表示字符串的字符,0 等于'\0'(终止字符)。如果您的字符缓冲区始终以'\0' 结尾,则您不需要知道它的长度,因为终止字符准确地指定了数据的结尾。

请注意,在这两种情况下,最好使用一些对象来隐藏数据的内部表示并为您处理内存管理(请参阅RAII idiom)。所以最好使用std::vector&lt;unsigned char&gt;(二进制数据)或std::string(字符串)。

【讨论】:

  • 有时我看到他们将unsigned char* 组合到另一个 sizeof 结构中,如下所示:return (unsigned char *)data + sizeof(Header); (data is void pointer),这是为了计算 void 指针的长度和标题的大小吗?
【解决方案2】:

在 C 中,unsigned char 是唯一保证没有陷印值的类型,它保证复制将产生精确的按位图像。 (C++ 也将此保证扩展到 char。)因此,它传统上用于“原始内存”(例如,memcpy 的语义是根据 unsigned char 定义的)。

此外,当要使用按位运算(&amp;|&gt;&gt; 等)时,通常会使用无符号整数类型。 unsigned char 是最小的无符号整数类型,可以在处理使用按位运算的小值数组时使用。有时,它也被使用,因为在溢出的情况下需要取模行为,尽管这对于较大的类型更常见(例如,在计算哈希值时)。这两个原因通常都适用于无符号类型。 unsigned char 通常仅在需要减少内存使用时才用于它们。

【讨论】:

  • “C++ 也将此保证扩展到char。” — 我们能找到这个的来源吗?
  • @emlai 这是不言而喻的/容易证明的。 stackoverflow.com/a/24052128/1874170 如果您愿意,您可以破坏内存并遍历所有 2^8 个可能的值(由于sizeof(char) 保证全面)并为自己证明。
【解决方案3】:

unsinged char 类型通常用作二进制数据的单个byte 的表示。因此,数组通常用作二进制数据缓冲区,其中每个元素都是一个字节。

unsigned char* 构造将是一个指向二进制数据缓冲区(或其第一个元素)的指针。

我不能 100% 确定 c++ 标准对 unsigned char 的大小的确切说明,无论它是否固定为 8 位。 通常是的。我会尝试找到并发布它。

看到你的代码后

当您使用void* input 之类的东西作为函数的参数时,您会故意删除有关输入原始类型的信息。这是一个非常强烈的建议,即输入将以非常一般的方式处理。 IE。作为任意字节串。另一方面,int* input 建议将其视为单整数的“字符串”。

void* 主要用于输入被编码或出于任何原因明智地对待bit/byte 的情况,因为您无法就其内容得出结论。

然后在您的函数中,您似乎希望将输入视为一串字节。 对对象进行操作,例如执行operator=(赋值)编译器需要知道该做什么。由于您将输入声明为 void* 赋值,例如 *input = something 将没有意义,因为 *input 属于 void 类型。要使编译器将 input 元素视为“最小的原始内存块”,请将其转换为适当的类型,即 unsigned int

cout 可能由于错误或意外的类型转换而无法工作。 char* 被认为是一个以空字符结尾的字符串,在代码中很容易混淆 singedunsigned 版本。如果您将unsinged char* 作为char* 传递给ostream::operator&lt;&lt;,它会将byte 输入视为普通ASCII 字符,其中0 是字符串的结尾,而不是0 的整数值。当你想打印内存内容时,最好显式地转换指针。

还要注意,要打印缓冲区的内存内容,您需要使用循环,否则打印函数将不知道何时停止。

【讨论】:

  • C 和 C++ 将字符类型(charunsigned charsigned char)定义为 1 字节大小,并要求它们至少具有 8 位。有,或者至少直到最近才有一台 9 位 char 的机器,还有一些带有 32 位字符的机器。 (当然,从历史上看,有很多机器的字节小于 8 位,但 C 不允许这样做。)
  • @James,谢谢。我提到了它,因为我记得不能保证它总是 8 位。我想澄清一下,以防万一要实现一些低级网络协议或将二进制文件从一个系统移动到另一个系统,他们可能会遇到这样的警告。
  • 很大程度上取决于您的便携性。对于大多数人来说,可移植性限制足够宽松,可以假设 char 是 8 位,但有台机器不是。
【解决方案4】:

当您要逐字节访问数据时,无符号字符指针很有用。例如,将数据从一个区域复制到另一个区域的函数可能需要这样:

void memcpy (unsigned char* dest, unsigned char* source, unsigned count)
{
    for (unsigned i = 0; i < count; i++)
        dest[i] = source[i];
}

这也与字节是内存的最小可寻址单元这一事实有关。如果要从内存中读取小于字节的任何内容,则需要获取包含该信息的字节,然后使用位操作选择信息。

您可以使用 int 指针很好地复制上述函数中的数据,但这会复制 4 字节的块,在某些情况下这可能不是正确的行为。

为什么当您尝试使用cout 时屏幕上什么也没有出现,最可能的解释是数据以零字符开头,在 C++ 中它标志着一串字符的结尾。

【讨论】:

  • 如果它仍然以 0 个字符开头,它应该打印其他 3 个字符的值。如果在代码中的 for 循环中 for(int i = 0; i (int)e[i];在上面的代码中,它打印第一次迭代中的值,然后打印 3 个垃圾值。
  • "您可以使用int 指针很好地复制上述函数中的数据" 不,您可以!除了unsigned char 之外的类型(我认为特别是 有符号类型)不能保证(A)覆盖底层内存的所有位或(B)允许尝试可能导致的捕获/无效值将任意字节重新解释为ints。在这里使用除unsigned char * 以外的任何指针本质上是非常不可移植的。实现可以将其用作依赖于平台的细节,但用户不应该。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-21
  • 1970-01-01
  • 1970-01-01
  • 2012-04-07
  • 1970-01-01
相关资源
最近更新 更多