C snprintf追加结构成员char * [英] C snprintf to append struct member char*

查看:72
本文介绍了C snprintf追加结构成员char *的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我打印出result-> value时,我得到了垃圾(其他存储区中的文本)和一个free():无效的指针

When I print out result->value I get garbage (text from other memory areas), and a free(): invalid pointer

我正在尝试安全地进行操作将字符串追加到现有char *(结果为实例的结构的值成员)

I am trying to safely append a string to an existing char* (the value member of the struct of which result is an instance)

unsigned const NAME_BUFFER=100;
unsigned const VALUE_BUFFER=1000;

typedef struct {
    char *name;
    int  ID;
    char *value;
} props;

…
static Bool
myfunc(props *result) 
{
    unsigned char *pointer;

    result->name=malloc(NAME_BUFFER);
    result->value=malloc(VALUE_BUFFER);

// not sure I need to do this, previous instances of the code produced
// output such as the quick...(null)someoutput
// which I thought might be because the member is empty the first time?
    sprintf(result->name,"%s","\0");
    sprintf(result->value,"%s","\0");

    …

    // in a loop which sets pointer, we want to safely append the value of pointer to the
    // value of the member called value

    snprintf(result->value,VALUE_BUFFER,"the quick...%s%s",result->value,pointer);

    …

    return False;
}

static void 
my_print_func() 
{
    props *result=malloc(sizeof(props));

    if(my_func(result))
        printf("%d\t%s",result->ID,result->value);
}

更改以上内容以使用sprintf不会导致以下问题:

Changing the above to use sprintf doesn't cause these problems:

sprintf(result->value,"the quick...%s%s",result->value,pointer);

...,但事实是它会很乐意尝试插入比分配的字符更多的字符。

... other than the fact that it would happily try to insert more characters than allocated.

那么添加sprintf(或变体)并确保我们不超出范围的正确方法是什么?

So what is the correct way of appending with sprintf (or variant) whilst also making sure we don't go out of bounds?

理想情况下,它不会涉及需要多于一行的其他临时变量,因为我需要在多个位置重复此追加。

Ideally, it wouldn't involve temporary variables other constructs that need more than one line as I need to repeat this append in several places.

推荐答案

这是未定义的行为,因为输入参数不能是输出缓冲区的一部分(对于 snprintf sprintf ):

This is undefined behaviour because the input arguments cannot be part of the output buffer (both in the case of snprintf and sprintf):

snprintf(result->value,VALUE_BUFFER,"the quick...%s%s",result->value,pointer);

在C标准中以电报方式指定:

This is specified telegraphically in the C standard:


§ 7.21.6.5/para 2:…如果在重叠的对象之间进行复制,则行为是不确定的。 (关于 sprintf ,相同的句子出现在§ 7.21.6.6/2中)

§7.21.6.5/para 2: …If copying takes place between objects that overlap, the behavior is undefined. (the same sentence appears in §7.21.6.6/2 with respect to sprintf)

以及 man sprintf


…如果调用,结果不确定 sprintf() snprintf() vsprintf() vsnprintf()会导致在重叠的对象之间进行复制(例如,如果目标字符串数组和提供的输入参数之一引用了相同的缓冲区)。 (Linux版本)

…the results are undefined if a call to sprintf(), snprintf(), vsprintf(), or vsnprintf() would cause copying to take place between objects that overlap (e.g., if the target string array and one of the supplied input arguments refer to the same buffer). (Linux version)

如果在某些情况下碰巧产生了预期的结果,那么您很幸运(或不走运,因为could幸可能会导致您得出无效的结论。)

If it happens to produce the expected result in some circumstances, you were lucky (or unlucky, since the fluke could lead you to an invalid conclusion).

尽管它并不正确,但此行的工作却非常复杂:

Although it is not incorrect, this line is ridiculously complicated for what it does:

sprintf(result->name,"%s","\0");

\0 被视为一个零长度的字符串,因为字符串以第一个NUL字符终止,因此它与 的区别仅在于它占用了两个字节而不是一个字节。但是无论如何,您都可以简单地写:

"\0" is treated as a zero-length string because strings are terminated by the first NUL character, so it only differs from "" by the fact that it uses up two bytes instead of one. But in any case, you could simply write:

result->name[0] = 0; /* Or ... `\0` if you like typing */

标准库包括 strcat strncat 用于连接字符串,但是安全版本 strncat 仅允许您指定要附加的字符数的限制,而不是字符串总长度的限制。因此,您需要自己跟踪可用的字符数,如果要这样做,最好也跟踪字符串末尾的位置,也就是要复制附加字符的位置。字符串,而不是每次进行连接时都搜索结尾。因此, str(n)cat 几乎不是字符串连接的正确解决方案。

The standard library includes strcat and strncat for concatenating strings, but the "safe" version strncat only lets you specify a limit to the number of characters to append, not a limit to the total length of the string. So you need to keep track of the number of characters available yourself, and if you're going to do that, you might as well instead keep track of the position of the end of the string, which is where you want to copy the appended string, rather than searching for the end every time you do a concatenation. For this reason, str(n)cat are hardly ever the correct solution for string concatenation.

这很简单将多个块连接到输出缓冲区的轮廓:

Here's a simple outline for concatenating multiple chunks to an output buffer:

size_t used = 0;
result->value = malloc(MAX_VALUE_LEN + 1);
for (...) { /* loop which produces the strings to append */
  ...
  /* append a chunk */
  size_t chunk_len = strlen(chunk);
  if (MAX_VALUE_LEN - used >= chunk_len) {
    memcpy(result->value + used, chunk, chunk_len);
    used += chunk_len;
  }
  else {
    /* Value is too long; return an error */
  }
}
result->value[used] = 0;

不是每个人都会同意我使用memcpy而不是strcpy;之所以这样做,是因为我已经知道要复制的字符串的长度(必须检查该字符串的长度才能检查是否有足够的空间),并且复制已知数量的字节通常比复制字节要有效得多,直到复制完为止您击中了NUL。

Not everyone will agree with my use of memcpy rather than strcpy; I did it because I already knew the length of the string to copy (which I had to figure out in order to check whether there was enough space), and it is usually more efficient to copy a known number of bytes than to copy bytes until you hit a NUL.

使用 memcpy 迫使我明确NUL终止结果,但是我会否则必须在开始时插入NUL,以防循环无法添加任何内容。为了给NUL留出空间,我最初分配了 MAX_VALUE_LEN + 1 个字节。但是,在实践中,我可能会从少量分配开始,并在必要时按指数方式 realloc ,而不是施加人为限制并在通常情况下人为限制浪费内存大于实际需要的内存。

The use of memcpy forces me to explicitly NUL-terminate the result, but I would otherwise have had to insert a NUL at the beginning in case the loop didn't manage to append anything. In order to leave room for the NUL, I initially allocated MAX_VALUE_LEN + 1 bytes. However, in practice I would probably start with a small allocation and exponentially realloc if necessary, rather than imposing an artificial limit and wasting memory in the common case that the artificial limit was much greater than the memory actually needed.

如果大小限制不是人为的,即是否存在某些外部性限制了附加字符串的长度,例如输出显示框的大小-那么可能会选择只是截断字符串而不是为超大结果引发错误:

If the size limit is not artificial -- that is, if there is some externality which constrains the length of the appended string, such as the size of an output display box -- then one might choose to simply truncate the string rather than throwing an error for over-size results:

size_t used = 0;
result->value = malloc(MAX_VALUE_LEN + 1);
for (...) { /* loop which produces the strings to append */
  ...
  /* append a chunk */
  size_t chunk_len = strlen(chunk);
  if (MAX_VALUE_LEN - used < chunk_len) {
    chunk_len = MAX_VALUE_LEN - used;
  }
  memcpy(result->value + used, chunk, chunk_len);
  used += chunk_len;
}
result->value[used] = 0;

这篇关于C snprintf追加结构成员char *的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆