1

本质上,我正在标记一个字符串并将strncpy找到的字符串添加到结构成员,即 stringid。它当然存在缺少终止的问题,我为它添加了一个额外的数组空间,我不知道如何正确添加它。

我是这样做的:

my_struct[iteration].stringID[ID_SIZE-1] = '\0' //updated

我不确定这是否真的有效,IMO 看起来很糟糕。

Str(n)cping 空字符或 0 会导致 GCC 和 MinGW 生成警告:

warning: null argument where non-null required (arg 2)

我是否对如何以干净的方式做到这一点视而不见?我正在考虑将成员数组设置为零,然后将字符串复制到非常适合空终止。您有什么建议或做法吗?

4

4 回答 4

3

两件事情:

  1. 请注意,它strncpy()具有非常意外的语义,如果没有完全被字符串填充,它将始终 0 填充缓冲区,如果完全填充缓冲区,它将不会终止字符串。这两个都很奇怪,我建议不要使用它。
  2. 永远不要用它的大小来索引一个数组,就像stringID[ID_SIZE]似乎正在做的那样;这是超出范围的。

最好的解决方案是编写一个strncpy()不那么奇怪的自定义版本,或者(如果您知道输入的长度)只需使用strcpy().

更新:如果您的输入标记的长度是静态的,但由于您的标记化过程,它们在源缓冲区中没有以 0 结尾,那么只需使用memcpy()并手动终止:

const char * token = ...; /* Extract from tokenization somehow. Not 0-terminated. */
const size_t token_length = ... /* Perhaps from tokenization step. */
memcpy(my_struct[iteration].stringID, token, token_length);
my_struct[iteration].stringID[token_length] = '\0';

我认为不需要将上述内容“包装”在宏中。

于 2011-05-04T12:08:37.737 回答
1

实际上,您建议的 null 终止方式一点也不可怕,我个人非常喜欢它。

在我看来,最好的方法是以类似的方式将其定义为宏:

// for char* blah;
#define TERMINATE_DYNAMIC_STRING(str, len) str[len] = '\0';
// for char mytext[] = "hello";
#define TERMINATE_STRING(str) str[sizeof(str)/sizeof(str[0]) - 1] = '\0';

然后,您可以随心所欲地在您的代码中使用它。

在 Windows 上,Microsoft 为您提供以下函数,这些函数在复制字符串时会以 null 终止:StringCchCopy

于 2011-05-04T12:14:29.830 回答
0

正如其他人所指出的,strncpy具有奇怪的语义。进行有界字符串复制的惯用方法是复制strncat到一个空字符串上:

my_struct[iteration].stringID[0] = '\0';
strncat(my_struct[iteration].stringID, src, ID_SIZE-1);

这总是附加一个终止 NUL,(并且最多填充 ID_SIZE 个字符,包括 NUL)。

于 2011-05-04T12:32:29.527 回答
0

我最终编写了一个 strncpyz(char* pszTo, char* pszTo, size_t lSize) 函数来强制 NULL 终止。如果您有一个库可以放入它,这将非常有效。使用它还需要最少的代码更改。

我不热衷于宏方法,因为有人会将指针传递给错误的宏。

于 2011-05-04T17:13:00.597 回答