我正在编写一个程序,该程序读取二进制文件(特别是Windows PE文件Wikipedia page和detailed PE structure)的内容。
由于文件中存在二进制数据,因此这些字符通常会“落在” ASCII范围(0-127)之内,并导致出现负值。
为确保我不会使用不必要的负值,我可以传递const unsigned char *
或将计算中的结果字符转换为unsigned char
。
一方面,传递const unsigned char *
很有意义,因为数据是非数字的,具有数值,因此应将其视为正数。
另外,它可以让我执行计算而无需将结果强制转换为unsigned char
。
另一方面,如果不先将常量字符串强制转换为{{1},就不能将常量字符串(const char *
,例如预定义字符串“ MZ”,“ PE \ 0 \ 0”等)传递给函数。 }。
在这种情况下,什么是更好的方法或最佳实践?
答案 0 :(得分:2)
我认为我会使用unsigned char
,但要避免进行强制转换,而是定义一个名为ustring
的小类(或类似的东西)。您有两个选择。一种是在std::basic_string
上实例化unsigned char
。这可能很有用(它为您提供了std::string
的所有功能,但使用了unsigned char
而不是char
。明显的缺点是它可能过大了,并且基本上没有兼容性std::string
,即使它几乎是完全一样的东西。
另一个明显的可能性是定义自己的类。由于您显然主要关心字符串文字,因此我可能会采用这种方式。该类将以字符串文字形式初始化,并且只保存指向该字符串的指针,但以unsigned char *
而不是char *
开头。
然后还有一个步骤来使生活变得更好:定义一个用户定义的文字运算符,其名称类似_us
,因此从字符串文字中创建您类型的对象将类似于以下内容:auto DOS_sig = "MZ"_us;
class ustring {
unsigned char const *data;
unsigned long long len;
public:
ustring(unsigned char const *s, unsigned long long len)
: data(s)
, len(len)
{}
operator char const *() const { return data; }
bool operator==(ustring const &other) const {
// note: memcmp treats what you pass it as unsigned chars.
return len == other.len && 0 == memcmp(data, other.data, len);
}
// you probably want to add more stuff here.
};
ustring operator"" _us(char const * const s, unsigned long long len) {
return ustring((unsigned char const *)s, len);
}
如果我没记错的话,这应该很容易使用。例如,假设您已经对您认为是PE文件的内存进行了映射,其基址为mapped_file
。要查看它是否具有DOS签名,您可以执行以下操作:
if (ustring(&mapped_file[0], 2) == "MZ"_us)
std::cerr << "File appears to be an executable.\n";
else
std::cerr << "file does not appear to be an executable.\n";
警告:我还没有对此进行测试,因此击剑杆错误和类似错误很可能发生-例如,我不记得传递给用户定义的文字运算符的长度是否包含NUL终止符。这并不是要表示完成的代码,而只是概述可能有助于探索的一般方向的草图。