首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >strncpy到strcpy等价

strncpy到strcpy等价
EN

Code Review用户
提问于 2016-01-21 14:18:52
回答 1查看 332关注 0票数 4

我有一个丑陋的函数,我觉得整个strncpy应该只是一个strcpy

代码语言:javascript
运行
复制
void PackData(char*& cursor, const std::string& data) {
    *(reinterpret_cast<int*>(cursor)) = static_cast<short>(data.length() + 1);
    cursor += sizeof(int);
    // copy the text to the buffer
    ::strncpy(cursor, data.c_str(), data.size());
    cursor += (data.length() * sizeof(char));
    *(reinterpret_cast<char*>(cursor)) = 0;
    cursor += sizeof(char);
}

保证cursor有足够的空间来包含所有复制的数据。而data在终止时只包含一个'\0'字符。

我想更新这个函数以使用strcpy,并删除一些丑陋的内容。我现在拥有的是:

代码语言:javascript
运行
复制
void PackData(char*& cursor, const std::string& data) {
    const int size = data.size() + 1;

    std::copy_n(cursor, sizeof(int), reinterpret_cast<char*>(&size));
    cursor += sizeof(int);
    strcpy(cursor, data.c_str());
    cursor += size;
}

我的代码运行良好,但我想问是否有人看到了我可能错过的任何不当行为?

EN

回答 1

Code Review用户

回答已采纳

发布于 2016-01-21 15:03:00

新版本的您的功能没有任何实际的错误,我可以发现。您已经将对*(reinterpret_cast<int*>(cursor))的分配替换为对std::copy_n的调用,这是很好的,因为旧版本(想必)违反了严格的别名规则,因此调用了未定义的行为。(也可以考虑对齐。)在您的旧函数中,将data.length() + 1传递给short而不是int似乎也是错误的。

不过,我还是要说几句“软”的话。

避免输出参数

引用一个指针,然后修改,在我看来并不好看。相反,我会按值获取指针,并将其前进的字节数return

代码语言:javascript
运行
复制
std::size_t PackData(char * cursor, const std::string& data);

它看起来更好,并允许更灵活的使用。

选择了适当的库函数

C标准库中的字符串操作函数都对以NUL结尾的字符串进行操作。也就是说,std::strcpy必须查看字符串的每个字节,以确定它是否是字符串的末尾。您已经提到,您的字符串今天不包含NUL字节。但如果它将来会这样做呢?你会记得更新函数吗?即使您确信NUL字节永远不会是一个问题,它仍然没有效率比它可能是。因为std::string已经知道了它自己的长度,所以有几个选项可用。

  • std::memcpy --如果你喜欢用C方式做事
  • std::copy -如果你喜欢用STL的方式做事
  • std::string::copy -如果你喜欢用OO的方式做事

请注意,这些函数都不会终止复制的字符串,因此您必须自己添加NUL字节。做起来不难。

代码语言:javascript
运行
复制
cursor[size] = '\0';

使用std::memcpy也可以帮助您存储长度。因为它的参数是void指针,所以不需要转换任何东西。

代码语言:javascript
运行
复制
std::memcpy(cursor, &size, sizeof(size));

在我看来比

代码语言:javascript
运行
复制
std::copy_n(cursor, sizeof(int), reinterpret_cast<char*>(&size));

,不要重复,

您的代码三次提到size的类型。一次在声明中--这很好--然后在sizeof表达式中进行两次。通过使用sizeof(size)而不是sizeof(int),您可以消除这种冗余。如果您曾经更改过数据类型,您只需要在一个地方这样做,并且不能使其不一致。

验证您的假设

演员

代码语言:javascript
运行
复制
const int size = data.size() + 1;

看起来挺安全的。但你确定吗?整数溢出是一个令人讨厌的错误来源和一个潜在的进入门的饼干。我推荐两件事。

首先,通过拼写清楚地说明您正在缩小范围的事实。

代码语言:javascript
运行
复制
const auto size = static_cast<int>(data.size() + 1);

请注意,为了遵循上一节中的建议,我现在可以使用auto

其次,添加防御性代码(在转换之前)以验证您的假设。

代码语言:javascript
运行
复制
assert(INT_MAX > data.size());

如果只使用可信输入调用函数,则这是安全的。如果攻击者担心,assert是错误的工具,因为它可能被编译出来,并且您不希望您的调试构建是安全的,只在您的发布版本中打开安全漏洞。因此,这样的显式检查如下

代码语言:javascript
运行
复制
if (INT_MAX <= data.size())
  throw std::invalid_argument {"data too large"};

在这种情况下会更好。

只要是sizeof(int) <= sizeof(std::string::size_type),这段代码是安全的,我认为在几乎任何实现中都是正确的。但是,它仍然可能触发编译器警告比较有符号整数和无符号整数。实际上,size可能是unsigned

或者,您可以使用辅助函数checked_cast<int>(data.size() + 1)。我认为这个论点不可能溢出,因为std::string总是必须考虑到终止缓冲区的可能性。

提防数据泄露

我不知道调用函数的环境,但是cursor所指向的缓冲区是对齐的吗?如果是这样的话,请注意字符串结束后未使用的填充字节以及写入缓冲区的下一件事。他们可能泄露你不愿让别人看到的信息。

票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/117494

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档