【问题标题】:Bug trying to add two strings with snprintf尝试使用 snprintf 添加两个字符串的错误
【发布时间】:2009-07-20 19:11:47
【问题描述】:

我正在尝试使用 snprintf 添加两个字符串,但显然我不知道自己在做什么。

这里是代码块:

char * filename = NULL;

(void)snprintf (filename, sizeof(filename), "%s/%s",
        PATH, FILE);  

我也试过了:

char * filename = NULL;

(void)snprintf (filename, sizeof(PATH)+sizeof(FILE)+1, "%s/%s",
        PATH, FILE);  

PATH 和 FILE 是标题定义的字符串。有时,此代码有效,有时则无效。我确定这是某种内存问题,我做错了什么?

编辑: 我的问题是出于某种原因认为 snprintf 为您分配了内存。我接受了解决这个问题的答案,因为这是我真正的问题,但我决定使用编译时字符串连接,因为这是一个非常好的技巧。

【问题讨论】:

  • 你没有分配任何内存来放置格式化的字符串。

标签: c string memory-management


【解决方案1】:

如果 FILE 和 PATH 在标头中定义为字符串文字,那么您可以在编译时连接:

#include <stdio.h>
/* elsewhere in your headers */
#define FILE "foo.ext"
#define PATH "/dir/subdir"

/* After including those headers */
#define FULLPATH (PATH "/" FILE)

int main(int argc, char *argv[]) {
  printf("%s\n", FULLPATH);
}

或者直接在声明变量时直接做,并在代码的其他地方引用它:

#include <stdio.h>

#define FILE "foo.ext"
#define PATH "/dir/subdir"

char fullpath[] = PATH "/" FILE;

int main(int argc, char *argv[]) {
  printf("%s occupies %d bytes\n", fullpath, sizeof(fullpath));
}

【讨论】:

  • 我决定采用这个解决方案,但我感谢大家对我最初的问题的意见,我的印象是 snprintf 分配了内存。
  • @Bob:为此,有asprintf(),这是一个GNU?扩展名,但自己实现它很简单:snprintf(NULL,0,...) 将返回结果字符串的长度而不产生任何输出,因此您确切知道要分配多少字节
  • @João: 是的,但是 MS CRT 提供 _scprintf() - defined _MSC_VER 是你的朋友...
【解决方案2】:

你应该先分配内存。

char * filename = NULL;
filename = malloc(sizeof(PATH) + sizeof(FILE) + 1);
snprintf (filename, sizeof(PATH) + sizeof(FILE) + 1, "%s/%s", PATH, FILE);

【讨论】:

  • sizeof 在字符串文字上是危险的,因为它产生 sizeof 指针而不是 strlen
  • 嗯...它产生包含空终止符的字符串文字的大小。至少在 Microsoft 编译器上的 C++ 中。
  • ... 仅当它在编译时是常量时。如果它只是一个char*,它将返回 4。
【解决方案3】:

您在编译时就知道字符串的长度,因此不需要动态分配。

static char filename[sizeof(PATH) + sizeof(FILE)];
snprintf(filename, sizeof(filename), "%s/%s", PATH, FILE);

但由于您要加入的字符串很可能以文字形式给出,因此根本不需要 snprintf()

static const char filename[] = PATH "/" FILE;

另外,因为对字符串的长度有些混淆:

strlen(PATH) + strlen("/") + strlen(FILE) + 1
= (sizeof(PATH)-1) + 1 + (sizeof(FILE)-1) + 1
= sizeof(PATH) + sizeof(FILE)

【讨论】:

  • +1 表示“常量”。尽管您的第一个 sn-p 有两个细微的错误,在这种情况下无关紧要,但是如果这两行是分离的,请设置一个值得怀疑的使用示例:filename[] 比需要的大一个字节并且在 snprintf() 中使用 sizeof() ) 的第二个参数不正确,可能导致不写入空终止字符。
  • @Andrew:你错了:我在 cmets 中对this answer 做了数学计算;另外,来自 glibc 关于snprintf() 的手动条目:“尾随空字符计入此限制,因此您应该为字符串 s 分配至少 size 个字符。”
  • @Christoph:啊,确实!扩展我对那些“错误”的看法:在考虑第一个“错误”时,我错过了“/”;并在第二个中将“snprintf”与“strncpy”混合在一起。如果这两条线相距足够远,那将是一个合理的疏忽,但在这种情况下,这是我这边不可原谅的故障:-)
【解决方案4】:

你的代码有几个问题:

  • 你声明了一个指针,char * filename,但没有为其分配内存。

    1. 将 sizeof() 用于指针(指向 char*)非常危险:

char hello[] = "world";  /* yield 6 */
char *hello2 = hello;    /* yield 4 or 8 */

所以你必须首先分配足够的内存来保存文件名:

size_t filesize = strlen(PATH) + strlen(FILE) + 2; /* \0 + / */
char *filename = malloc(filesize);

if (filename == NULL) {
     /* error handling */
}

/* now you can safely write to filename: */
snprintf(filename, filesize, "%s/%s", FILE, PATH);

或者你可以分配一个大的char数组:

char filename[1024]; // or #define a MAXPATH lenght
snprintf(filename, filesize, "%s/%s", FILE, PATH);
/* hoping that filename lenght is less than 1024 or MAXPATH... */      

【讨论】:

    【解决方案5】:

    在某些系统上,您可以使用asprintf

    char *filename = NULL;
    asprintf(&filename, "%s/%s", PATH, FILE);
    

    据我所知,asprintfGlibc 的发明,但已被证明非常有用,以至于其他几个 libcs 也实现了它。

    【讨论】:

      【解决方案6】:

      我会将此作为评论添加到 GMan 的答案中,但在他们合并我的帐户之前,我没有代表这样做。 (现在 GMan 的答案显然已经被删除了,所以这没有意义。)

      我认为 GMan 的意思是

      unsigned bufferSize = strlen(PATH) + strlen(FILE) + 2; // "/" and null-terminator
      

      获取这些常量的长度,而不是滑牛指出的指针大小。 (编辑:或者,如果 sizeof(STRINGLITERAL) 给出了数据的大小,包括结尾的 null,那么 GMan 的代码对于字符串常量仍然有效。)

      不过,我认为如果您知道 PATH 和 FILE(作为 bufferSize)的确切长度,则不需要 snprintf(),只需 sprintf() 就可以了。但是使用 snprintf() 并没有什么坏处,因为无论如何您都需要 bufferSize 进行分配。

      已编辑:或者,Andrew Y 可能有更好的方法(用于编译时字符串常量),如果这适用于您的话。

      【讨论】:

        【解决方案7】:

        添加此代码:

        if (!(filename = malloc((LEN + 1) * sizeof(char))))
                return 1;
        

        将 LEN 设置为某个值 >= PATH 和 FILE 长度。完成后不要忘记释放。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-12-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-06-20
          • 1970-01-01
          • 2015-09-25
          相关资源
          最近更新 更多