【问题标题】:Does snprintf/sprintf overwrite the terminating-null of the penultimate argument, just like strcat does?snprintf/sprintf 是否会覆盖倒数第二个参数的终止空值,就像 strcat 一样?
【发布时间】:2020-09-08 15:06:49
【问题描述】:

显然是写给strcat,例如herehere 以防万一,

char *strcat(char *s1, const char *s2);

那么,

s2 的起始字符覆盖 s1 末尾的空字符。

但显然在这里搜索“在 C 中连接字符串/文字”时,我偶然发现了 this,其中指出,

避免在 C 代码中使用 strcat。最干净,最重要的是,最安全的方法是使用 snprintf:

那么,对于snprintf/sprintf下一个参数的第一个字符覆盖前一个参数的空终止符是否也是如此?我在documentation 中看不到这样的参考。

经验证据似乎表明,strcat 和 snprintf 的行为方式相同。还是我的假设错了?

#include <string.h>
#include <stdio.h>

int main(int argc, char const *argv[])
{
    printf( "Test Program Started\n");
    const char* first = "a";
    const char* second = "b";
    const char* third = "c";
    const int merged_length = (strlen(first) + strlen(second) + strlen(third) + 1); // +1 for null-termination

    char* catResult;
    catResult = malloc( merged_length * sizeof(char));
    strcpy(catResult, first);
    strcat(catResult, second);
    strcat(catResult, third);
    catResult[merged_length] = '\0';
    printf("catResult:%s \tstrlen(catResult):%d \t sizeof(catResult):%d\n", 
            catResult, 
            strlen(catResult), 
            sizeof(catResult));
    free(catResult);

    char* snprintfResult;
    snprintfResult = malloc( merged_length * sizeof(char));
    snprintf(snprintfResult, merged_length, "%s%s%s", first, second, third);
    // catResult[merged_length] = '\0'; // not necessary as per documentation
    printf("snprintfResult:%s \tstrlen(snprintfResult):%d \tsizeof(snprintfResult):%d\n", 
            snprintfResult, 
            strlen(snprintfResult), 
            sizeof(snprintfResult));
    free(snprintfResult);
} 

测试程序已启动
catResult:abc strlen(catResult):3 sizeof(catResult):4
snprintfResult:abc strlen(snprintfResult):3 sizeof(snprintfResult):4

【问题讨论】:

  • @user694733,哎呀!我的错!我会编辑和更正它。一秒钟。
  • @user694733,在我使用的 PC 上的原始测试代码中,我使用了 strlen。然后我想用sizeof 测试并忘记在粘贴之前撤消该更改。现在好点了吗?
  • snprintf(类似于strcat)的错误用法是snprintf(buf, buflen, "%s%s", buf, more_stuff);。这将适用于某些 C 库实现而不适用于其他库实现,但根据标准,它是未定义的行为。
  • @rici,好的。我明白。正如您在评论中的 sn-p 中显示的那样,源和目标不能重叠。

标签: c concatenation null-terminated


【解决方案1】:

snprintfsprintf 不会像 strcat 那样附加到前面的字符串。它们从传递给它们的缓冲区的开头开始写入。

在一次调用中写入多个字符串时,与格式字符串"%s%s%s" 一样,它们将连续写入字符串,它们之间没有空字符,并且以空字符结尾。

如果您希望它们附加到名为buffer 的缓冲区中的现有字符串,则确定字符串的长度,例如n,并传递buffer + n 作为第一个参数而不是buffer。 (对于snprintf,注意n 也应该从第二个参数中减去,它指定缓冲区中可用的字节数。)

【讨论】:

  • When writing multiple strings in a single call, as with the format string "%s%s%s", they will write the strings consecutively, with no null characters between them --> 这正是我的问题。感谢您的快速回答。
【解决方案2】:

你不能这样使用sizeof。虽然sizeof("string") 可以正常工作,但sizeof(string pointer) 在任何给定平台上总是返回相同的值(通常为 4 或 8)。

const int merged_length = (sizeof(first) + sizeof(second) + sizeof(third) + 1);

应该是

const int merged_length = (strlen(first) + strlen(second) + strlen(third) + 1);

当您将%s 写入snprintf 时,它会将字符串复制到目标字符串,而不会出现任何尾随空值。当到达格式字符串末尾的空终止符时,输出字符串也为空终止。

因此,您的问题的实际答案是否定的,因为 null 从未在 first 之后写入,但最终效果更像是我们回答“是”,因为这两个代码片段做同样的事情。

【讨论】:

  • When you write %s to snprintf, it copies the string to the target string without any trailing null. --> 这正是我的问题。 So, the actual answer to your question is no, because the null was never written after first --> 感谢您的澄清。
猜你喜欢
  • 2012-08-03
  • 1970-01-01
  • 2020-12-04
  • 1970-01-01
  • 2011-06-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多