1
#include <iostream>
using namespace std;

struct Packet {
    int a;
    char b[17];
    int c;
};

// char* dest has form char dest[n], src has length <= n and is null-terminated
// After the function, dest should satisfy:
// - If strlen(src)==n, dest is not null terminated
// - If strlen(src) < n, dest[n-1] = dest[strlen(src)] = '\0'
static void strncpy_faster(char* dest, const char* src, size_t n) {
    size_t i;
    for (i = 0; i < n; i++) {
      dest[i] = src[i];
      if (src[i] == '\0')
        break;
    }
    //while (i < n) {dest[i] = '\0'; i++;} // C standard strncpy do this
    if (i < n)
      dest[n - 1] = '\0';
 }
 
 string charArrayToString(const char* a, size_t n) {
    size_t len = 0;
    while (len < n && a[len]!='\0') len++;
    return std::string(a, a+len);
 }

int main()
{
    string s = "12341234123412345";
    Packet packet;
    
    strncpy_faster(packet.b, s.c_str(), 17);
    cout << charArrayToString(packet.b, sizeof(packet.b)) << "\n";
    
    s = "12345";
    strncpy_faster(packet.b, s.c_str(), 17);
    cout << charArrayToString(packet.b, sizeof(packet.b));
    return 0;
}

我正在处理具有固定大小字符数组的结构。假设我真的非常想保持结构的大小很小(或者我需要通过网络发送它们),所以std::string在我的结构中没有使用(它花费 32 个字节,而我有多个大小为 4-20 的小字符数组)。我对 strncpy 有 2 个问题:

  • strncpy具有相同长度的 2 个字符数组将发出有关“可能没有以空字符结尾的字符”的警告,这是有意的,但我不需要该警告。
  • strncpy用 .填充所有剩余字符(来自dest[strlen(src) -> n-1]'\0'。这在我的程序中浪费了处理。

所以,我strncpy_faster最多只能为 '\0' 分配 2 个位置:数组的最后一个元素,以及strlen(src). 由于std::string()需要一个以空字符结尾的字符数组 (NTCA),因此我使用charArrayToString()将非 NTCA 转换为字符串。

这个版本strncpy安全吗?是否有任何 C/C++ 函数需要strncpy用 填充所有剩余字节'\0',或者它们只需要一个空终止符?我不知道为什么标准要求strncpy将剩余字节清零。

4

1 回答 1

1

这个版本的 strncpy 安全吗?

是的。

它和现在一样安全strncpy。所以....不安全。

是否有任何 C/C++ 函数需要 strncpy 用 '\0' 填充所有剩余字节,或者它们只需要一个空终止符?

没有功能需要它。

Linux手册页的注释man strcpy

笔记

一些程序员认为 strncpy() 效率低下且容易出错。如果程序员知道(即包含要测试的代码!)dest 的大小大于 src 的长度,则可以使用 strcpy()。

strncpy() 的一种有效(和预期)用途是将 C 字符串复制到固定长度的缓冲区,同时确保缓冲区不会溢出并且目标缓冲区中未使用的字节被清零(也许是为了防止信息泄漏,如果缓冲区将被写入媒体或通过进程间通信技术传输到另一个进程)。

考虑使用(和/或实施)strlcpy。记住优化的第一条规则

于 2022-01-03T12:09:09.717 回答