字符串的内存分配

Memory allocation for a string

我正在 Bus error: 10(在 运行 时间)仅通过 运行 宁以下代码:

char *x;
char *y = "the quick";
sprintf(x, "%s brown fox jumps over the lazy dog", y);

Looking around 我看过几个帖子,问题似乎是我试图修改字符串文字,但在上面的代码中,我认为,我不是。 此外,如果我尝试使用 mallocx 分配一些内存,一切正常,而且如果我分配的内存少于存储整个字符串所需的内存,即使我分配 0.

char *x = malloc(0);
char *y = "the quick";
sprintf(x, "%s brown fox jumps over the lazy dog", y);
printf("%s\n", x); // the quick brown fox jumps over the lazy dog

我也知道这在某种程度上与内存分配有关,但我想指定 x 而不告诉编译器需要多少内存,因为 y 可能很长字符串,甚至是数字...

我应该如何做我想做的事情,只使用字符指针(而不是字符数组),而不必知道 x 的最终长度,例如无需计算 x 的最大大小然后分配内存?

sprintf 将包含指定转换的字符串存储到由其第一个参数指向的数组中。程序员有责任为转换后的字符串传递一个足够大的数组,包括它的最终终止符。

在您的第一个示例中,通过未初始化的指针写入具有未定义的行为,如您所见,这可能会导致崩溃。

在第二个例子中,malloc(0) 可能 return NULL 或者一个有效的指针,因为它指向一个大小为 0 的数组,所以不应该用于写入.在这两种情况下,sprintf 都会调用未定义的行为,在这种情况下,您的机器在您测试时会产生预期的行为,因为未检测到无效写入,但可能会导致副作用,只有在以后才能看到。

这是修改后的版本:

char x[80];
char *y = "the quick";
sprintf(x, "%s brown fox jumps over the lazy dog", y);
printf("%s\n", x); // the quick brown fox jumps over the lazy dog

使用 snprintf() 并传递目标数组的实际大小以避免在碰巧太短时出现未定义的行为要安全得多。该字符串将被截断以适合数组,但 return 值将是转换生成的字节总数:

char x[25];
char *y = "the quick";
int n = snprintf(x, sizeof x, "%s brown fox jumps over the lazy dog", y);
printf("%s, needs %d bytes\n", x, n + 1); // the quick brown fox jump, needs 44 bytes

您可以通过使用空指针和零大小调用 snprintf 来确定所需的大小:

size_t minimum_size = 1 + snprintf(NULL, 0, "%s brown fox jumps over the lazy dog", y);

一些系统还有一个替代功能asprintf,它可以自动分配所需的内存,但这是一个没有广泛使用的 GNU 扩展。

坏消息是,如果不计算缓冲区的最终大小,就无法执行此操作。

好消息是您可以让 snprintf 为您计算。

const char *y = "the quick";
const char *fmt = "%s brown fox jumps over the lazy dog";

int sizeneeded = snprintf(NULL, 0, fmt, y) + 1;
char *dest = malloc(sizeneeded);
snprintf(dest, sizeneeded, fmt, y);
printf("%s\n", dest); // the quick brown fox jumps over the lazy dog

免责声明:为简洁起见,此处未检查 malloc 的 return 值 NULL

... and the problem seems to be that I am trying to modify a string literal, but in the above code, I think, I am not.

正确,您没有修改 ant 字符串文字。 y 指向字符串文字,但没有代码通过 y.

对字符串进行修改

问题(在第一个代码片段中)是 x 未初始化。

这个问题已经被 malloc 解决了,但是又出现了一个新问题。你写的时候分配的内存太少了。

... and the problem seems to be that I am trying to modify a string literal, but in the above code, I think, I am not.

那怎么可能呢?

发生的情况是您写入数组外的内存(越界访问)。 C 标准将其定义为“未定义的行为”。

“未定义的行为”意味着 任何事情 都可能发生。例如,允许程序崩溃。但是该程序也可能(看起来)像您预期的那样工作。这发生在你身上......你的代码是错误的 - 具有未定义的行为 - 但幸运的是(或者更不幸的是)它似乎可以工作。

问题是在您尝试使用 0 进行分配后,x 没有指向有效的内存块。要成功地将两个字符串连接到一个新分配的块中,您需要知道容纳两个字符串所需的字符总数。获取总数的一种便捷方法是使用:

snprintf (NULL, 0, "format-string", vars, ...);

这将 return 组合字符串所需的字符总数。然后分配 total + 1 以提供 space 作为空终止符。你可以这样做:

#include <stdio.h>
#include <stdlib.h>

int main (void) {
    
    char *x;
    char *y = "the quick";
    
    /* use snprintf (NULL, 0, "format", vars...) to get no. of chars needed */
    size_t needed = snprintf (NULL, 0, "%s brown fox jumps over the lazy dog", y);
    
    x = malloc (needed + 1);    /* allocate for x, +1 for the nul-terminating char */
    if (!x) {                   /* validate EVERY allocation */
        perror ("malloc-x");
        return 1;
    }
    
    /* now use sprintf to joins string in newly allocated block */
    sprintf (x, "%s brown fox jumps over the lazy dog", y);
    
    puts (x);       /* output result */
    free (x);       /* don't forget to free what you allocate */
}

例子Use/Output

$ ./bin/snprintfNULL0
the quick brown fox jumps over the lazy dog

内存Use/Error检查*

在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 责任:(1) 始终保留指向内存块的起始地址 因此,(2) 当不再需要它时可以释放

您必须使用内存错误检查程序来确保您不会尝试访问内存或写入 beyond/outside 您分配的块的边界,尝试读取或基于未初始化的条件跳转值,最后,确认您释放了所有已分配的内存。

对于Linux valgrind是正常的选择。每个平台都有类似的内存检查器。它们都很简单易用,只需运行你的程序就可以了。

$ valgrind ./bin/snprintfNULL0
==31830== Memcheck, a memory error detector
==31830== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==31830== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==31830== Command: ./bin/snprintfNULL0
==31830==
the quick brown fox jumps over the lazy dog
==31830==
==31830== HEAP SUMMARY:
==31830==     in use at exit: 0 bytes in 0 blocks
==31830==   total heap usage: 2 allocs, 2 frees, 1,069 bytes allocated
==31830==
==31830== All heap blocks were freed -- no leaks are possible
==31830==
==31830== For counts of detected and suppressed errors, rerun with: -v
==31830== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

始终确认您已释放所有分配的内存并且没有内存错误。

看看这个,如果您有任何问题,请告诉我。