c++ 标准化了std::byte之后,什么时候使用void*,什么时候使用byte*?

bqf10yzr  于 2023-02-17  发布在  其他
关注(0)|答案(4)|浏览(162)

C++17将包含std::byte,一种用于一个原子可寻址内存单元的类型,在典型计算机上具有8位。
在这个标准化之前,当指向"原始"内存时,已经存在一点两难选择--一方面使用char*/unsigned char*,另一方面使用void *。现在,首选void *的原因之一被删除了--std::bytechar没有相同的含义;这是关于原始记忆,不是字符
所以,我的问题是:对于std::byte的时代,关于什么时候比void *更喜欢它,什么时候相反,有什么好的经验法则?
当然,当你处理旧代码或C代码时,你会受到它接受的内容的限制;我主要是指新代码,您可以选择所有类型。

2guxujil

2guxujil1#

(This这是一个“潜在的”经验法则,它来自我的头顶,任何人都不会宽恕。)

经验法则:何时使用哪种指针?

*char *用于文本字符序列,而不是其他任何内容。
*在类型擦除场景中使用void *,即当指向的数据是类型化的,但由于某种原因不能使用类型化指针,或者不能确定它是否是类型化的。
*使用byte *作为原始内存,没有任何迹象表明它持有任何类型化数据。

上述情况的例外:

  • 当代码较旧时,也使用void */unsigned char */char *;或者当非C代码强制您使用byte *时,您仍然可以使用基于byte *的接口 Package 这种用法,从而不会将这种情况暴露给C代码的其余部分。

示例

void * my_custom_malloc(size_t size)-错误
byte * my_custom_malloc(size_t size)-右侧
struct buffer_t { byte* data; size_t length; my_type_t data_type; }-错误
struct buffer_t { void* data; size_t length; my_type_t data_type; }-右侧

cclgggtu

cclgggtu2#

std::byte的动机是什么?
引自X1 E0 F1 X
许多程序需要面向字节的内存访问。今天,这类程序必须使用charsigned charunsigned char类型来实现此目的。然而,这些类型执行“三重任务”。它们不仅用于字节寻址,还用作算术类型。角色的多样性为程序员的错误打开了大门--例如,意外地对应该被视为字节值的内存执行了算术运算--以及程序员和工具的困惑。
从本质上讲,std::byte是用来“取代”char类型的使用,当需要将原始内存作为字节来处理时,可以安全地Assert这在按值、按引用、指针和容器中使用时适用。
std::byte与char的含义不同;它是关于原始内存,而不是字符
正确,所以在处理内存中的字节(例如,字节数组)时,std::byte应该优先于char类型。
对于std::byte的时代,关于什么时候比void *更喜欢它,什么时候相反,有什么好的经验法则?
我认为类似的指南现在也适用,当处理原始内存块时,需要字节寻址能力,char *等会比void *更受欢迎,我认为同样的逻辑现在也适用,但是比char *更受欢迎byte *char *更适合字符序列。
如果希望不透明地传递指针,void *可能仍然最适合这个问题。void *本质上意味着“指向任何东西”,但任何东西仍然是东西,我们只是还没有说什么。
此外,类型uintptr_t(和intptr_t)可能会作为替代因素,当然这取决于所需的应用。
...我主要是指新的代码,你可以选择所有的类型。
新的代码通常在兼容性之外(你不能选择类型)非常有限地使用void *。如果你需要基于字节的处理,那么推荐byte *

blpfk2vs

blpfk2vs3#

首先,当您必须使用C库函数或一般来说使用任何其他extern "C"兼容函数时,void *仍然有意义。
接下来std::byte数组仍然允许单独访问它的任何元素。换句话说,这是法律的的:

std::byte *arr = ...;
arr[i] = std::byte{0x2a};

如果您希望能够允许这种低级别访问,例如,如果您希望手动复制全部或部分数组,则这是有意义的。
另一方面,void *实际上是一个 opaque 指针,从某种意义上说,在能够访问它的各个元素之前,您必须将它强制转换(转换为charbyte)。
因此,我的观点是,只要您希望能够寻址数组元素或移动指针,就应该使用std::byte,并且void *仍然有意义,它表示一个不透明的区域,该区域仅作为一个整体传递(实际上很难处理void *)。
但是void *的真实的用例在现代C中应该变得越来越不寻常,至少在高层,因为那些不透明区域通常应该隐藏在更高级别的类中,这些类带有处理它们的方法。因此,恕我直言,void *最终应该限于C(和更老的C版本)兼容性和低级代码(如分配代码)。

pjngdqdw

pjngdqdw4#

std::byte不仅仅是"raw memory",它是字节可寻址的原始存储器 *,具有为其定义的逐位操作 *。
您不应该盲目地使用std::byte来替换void*void*保留其用途。void*对处理代码来说意味着“这是一个数据块,但我不知道 * 这个数据是什么 *,也不知道 * 如何操作它 *。
当您需要内存块的字节地址 * 并且只需要定义位操作 * 来操作该数据时,请使用std::byte
std::byte没有没有定义常规的基本数学运算,如operator+operator-operator*。没错,以下代码是非法的:

std::byte a{0b11},b{0b11000};
std::byte c = a+b; // fails, operator+ not defined for std::byte

换句话说,当void*不是 * 内容的处理代码 * 时,请使用void*

标准::字节示例

就像我上面说的,你能在std::byte上做的就是像|&~这样的位操作。下面是一个使用std::byte的例子,注意你需要一个C17编译器来编译这个例子,有一个here,但是你必须从右上角的下拉列表中选择C17

#include <iostream>
#include <cstddef>
#include <bitset>

using namespace std;

void print(const byte& b)
{
  bitset<8> p( to_integer<int>( b ) );
  cout << p << endl;
}

int main()
{
  byte a{0b11},b{0b11000};
  byte c=a|b;
  //byte d = a+b; // fails
  print(c);
  return 0;
}

相关问题