如何为 C 多数组中的新字符串分配内存?
How do I allocate memory for a new string in a C Multiarray?
我正在尝试找到一种创建动态分配的 C 字符串数组的方法。到目前为止,我已经有了以下代码,它允许我初始化一个字符串数组并更改一个已经存在的索引的值。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void replace_index(char *array[], int index, char *value) {
array[index] = malloc(strlen(value) + 1);
memmove(array[index], value, strlen(value) + 1);
}
int main(int argc, const char * argv[]) {
char *strs[] = {"help", "me", "learn", "dynamic", "strings"};
replace_index(strs, 2, "new_value");
// The above code works fine, but I can not use it to add a value
// beyond index 4.
// The following line will not add the string to index 5.
replace_index(strs, 5, "second_value");
}
函数replace_index
将用于更改已包含在初始化程序中的字符串的值,但不能用于添加超出初始化程序中最大索引的字符串。有没有办法分配更多内存并添加新索引?
首先,如果您想进行严格的字符串操作,使用几乎任何其他语言或获取一个库来为您做这件事会容易得多。
无论如何,进入答案。
replace_index(strs, 5, "second_value");
在您的代码中不起作用的原因是因为 5 超出范围 - 该函数将写入与 strs
无关的内存。那不是你的问题,但如果你不问的话,知道这一点很重要。相反,您似乎想要附加一个字符串。下面的代码应该可以解决问题。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char **content;
int len;
} string_array;
void free_string_array(string_array *s) {
for (int i = 0; i < s->len; i++) {
free(s->content[i]);
}
free(s->content);
free(s);
}
int append_string(string_array *s, char *value) {
value = strdup(value);
if (!value) {
return -1;
}
s->len++;
char **resized = realloc(s->content, sizeof(char *)*s->len);
if (!resized) {
s->len--;
free(value);
return -1;
}
resized[s->len-1] = value;
s->content = resized;
return 0;
}
string_array* new_string_array(char *init[]) {
string_array *s = calloc(1, sizeof(string_array));
if (!s || !init) {
return s;
}
while (*init) {
if (append_string(s, *init)) {
free_string_array(s);
return NULL;
}
init++;
}
return s;
}
// Note: It's up to the caller to free what was in s->content[index]
int replace_index(string_array *s, int index, char *value) {
value = strdup(value);
if (!value) {
return -1;
}
s->content[index] = value;
return 0;
}
int main() {
string_array *s = new_string_array((char *[]) {"help", "me", "learn", "dynamic", "strings", NULL});
if (!s) {
printf("out of memory\n");
exit(1);
}
free(s->content[2]);
// Note: No error checking for the following two calls
replace_index(s, 2, "new_value");
append_string(s, "second value");
for (int i = 0; i < s->len; i++) {
printf("%s\n", s->content[i]);
}
free_string_array(s);
return 0;
}
此外,您 没有 将 char **
和 int
放在一个结构中,但如果这样做会更好。
如果您不想使用此代码,关键要点是必须动态分配字符串数组(char **
,如果您愿意)。意思是,您需要使用 malloc()
或类似的方法来获取所需的内存,并且您将使用 realloc()
来获取更多(或更少)的内存。不要忘记 free()
当您使用完后会得到什么。
我的示例使用 strdup()
来制作 char *
的副本,以便您随时可以根据需要更改它们。如果您无意这样做,删除 strdup()
ing 部分及其 free()
ing 部分可能更容易。
静态数组
char *strs[] = {"help", "me", "learn", "dynamic", "strings"};
这将 strs
声明为 array of pointer to char 并用 5 个元素对其进行初始化,因此隐含的 []
是 [5]
。如果不打算修改字符串,则更严格的 const char *strs[]
会更合适。
最大长度
char strs[][32] = {"help", "me", "learn", "dynamic", "strings"};
这将 strs
声明为一个 array of array 32 of char,它用 5 个元素初始化。这 5 个元素在字符串之外 zero-filled。最多可以修改 32 个字符,但不能添加更多。
常量字符串的最大单例容量
static struct str_array { size_t size; const char *data[1024]; } strs;
这将 pre-allocate 启动时的最大容量并使用它来满足请求。其中,capacity为1024,但size
可以是任意数字,不超过capacity。我做这个 static
的原因是通常要放置很多堆栈。没理由不能按要求做动态内存。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
static struct { size_t size; const char *data[1024]; } strs;
static const size_t strs_capacity = sizeof strs.data / sizeof *strs.data;
/** Will reserve `n` pointers to strings. A null return indicates that the size
is overflowed, and sets `errno`, otherwise it returns the first string. */
static const char **str_array_append(const size_t n) {
const char **r;
if(n > strs_capacity - strs.size) { errno = ERANGE; return 0; }
r = strs.data + strs.size;
strs.size += n;
return r;
}
/** Will reserve one pointer to a string, null indicates the string buffer is
overflowed. */
static const char **str_array_new(void) { return str_array_append(1); }
int main(void) {
const char **s;
size_t i;
int success = EXIT_FAILURE;
if(!(s = str_array_append(5))) goto catch;
s[0] = "help";
s[1] = "me";
s[2] = "learn";
s[3] = "dynamic";
s[4] = "strings";
strs.data[2] = "new_value";
if(!(s = str_array_new())) goto catch;
s[0] = "second_value";
for(i = 0; i < strs.size; i++) printf("->%s\n", strs.data[i]);
{ success = EXIT_SUCCESS; goto finally; }
catch:
perror("strings");
finally:
return success;
}
动态数组
struct str_array { const char **data; size_t size, capacity; };
我认为您要求的是 const char *
的动态数组。 Language-level 标准不支持动态数组 C
run-time;一个人必须自己写。这是完全可能的,但涉及更多。因为大小是可变的,所以它可能会更慢,但随着问题的增长,在极限内,以恒定的平均值。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
/** A dynamic array of constant strings. */
struct str_array { const char **data; size_t size, capacity; };
/** Returns success allocating `min` elements of `a`. This is a dynamic array,
with the capacity going up exponentially, suitable for amortized analysis. On
resizing, any pointers in `a` may become stale. */
static int str_array_reserve(struct str_array *const a, const size_t min) {
size_t c0;
const char **data;
const size_t max_size = ~(size_t)0 / sizeof *a->data;
if(a->data) {
if(min <= a->capacity) return 1;
c0 = a->capacity < 5 ? 5 : a->capacity;
} else {
if(!min) return 1;
c0 = 5;
}
if(min > max_size) return errno = ERANGE, 0;
/* `c_n = a1.625^n`, approximation golden ratio `\phi ~ 1.618`. */
while(c0 < min) {
size_t c1 = c0 + (c0 >> 1) + (c0 >> 3);
if(c0 >= c1) { c0 = max_size; break; } /* Unlikely. */
c0 = c1;
}
if(!(data = realloc(a->data, sizeof *a->data * c0)))
{ if(!errno) errno = ERANGE; return 0; }
a->data = data, a->capacity = c0;
return 1;
}
/** Returns a pointer to the `n` buffered strings in `a`, that is,
`a + [a.size, a.size + n)`, or null on error, (`errno` will be set.) */
static const char **str_array_buffer(struct str_array *const a,
const size_t n) {
if(a->size > ~(size_t)0 - n) { errno = ERANGE; return 0; }
return str_array_reserve(a, a->size + n)
&& a->data ? a->data + a->size : 0;
}
/** Makes any buffered strings in `a` and beyond if `n` is greater then the
buffer, (containing uninitialized values) part of the size. A null on error
will only be possible if the buffer is exhausted. */
static const char **str_array_append(struct str_array *const a,
const size_t n) {
const char **b;
if(!(b = str_array_buffer(a, n))) return 0;
return a->size += n, b;
}
/** Returns a pointer to a string that has been buffered and created from `a`,
or null on error. */
static const char **str_array_new(struct str_array *const a) {
return str_array_append(a, 1);
}
/** Returns a string array that has been zeroed, with zero strings and idle,
not taking up any dynamic memory. */
static struct str_array str_array(void) {
struct str_array a;
a.data = 0, a.capacity = a.size = 0;
return a;
}
/** Erases `a`, if not null, and returns it to idle, not taking up dynamic
memory. */
static void str_array_(struct str_array *const a) {
if(a) free(a->data), *a = str_array();
}
int main(void) {
struct str_array strs = str_array();
const char **s;
size_t i;
int success = EXIT_FAILURE;
if(!(s = str_array_append(&strs, 5))) goto catch;
s[0] = "help";
s[1] = "me";
s[2] = "learn";
s[3] = "dynamic";
s[4] = "strings";
strs.data[2] = "new_value";
if(!(s = str_array_new(&strs))) goto catch;
s[0] = "second_value";
for(i = 0; i < strs.size; i++) printf("->%s\n", strs.data[i]);
{ success = EXIT_SUCCESS; goto finally; }
catch:
perror("strings");
finally:
str_array_(&strs);
return success;
}
but will not work to add strings beyond the maximum index in the initializer
为此,您还需要指针数组是动态的。创建动态字符串数组是为数不多的可以使用 pointer-to-pointer 模拟二维数组的地方之一:
size_t n = 5;
char** str_array = malloc(5 * sizeof *str_array);
...
size_t size = strlen(some_string)+1;
str_array[i] = malloc(size);
memcpy(str_array[i], some_string, size);
您必须手动跟踪已用大小 n
,并且当您 运行 超出 str_array
时 realloc
有更多空间。 realloc
保证保留以前的值。
这非常灵活,但代价是分散分配,相对较慢。如果您使用 fixed-size 二维数组,代码会执行得更快,但您无法调整它们的大小。
请注意,我使用的是 memcpy
,而不是 memmove
- 前者是您通常应该使用的,因为它是最快的。 memmove
适用于您怀疑正在复制的两个数组可能重叠的特殊情况。
作为side-note,strlen
+malloc
+memcpy
可以替换为strdup
,目前是non-standard功能(但得到广泛支持)。 strdup
似乎有可能成为即将推出的 C23 版本 C 的标准,因此使用它会成为推荐的做法。
我正在尝试找到一种创建动态分配的 C 字符串数组的方法。到目前为止,我已经有了以下代码,它允许我初始化一个字符串数组并更改一个已经存在的索引的值。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void replace_index(char *array[], int index, char *value) {
array[index] = malloc(strlen(value) + 1);
memmove(array[index], value, strlen(value) + 1);
}
int main(int argc, const char * argv[]) {
char *strs[] = {"help", "me", "learn", "dynamic", "strings"};
replace_index(strs, 2, "new_value");
// The above code works fine, but I can not use it to add a value
// beyond index 4.
// The following line will not add the string to index 5.
replace_index(strs, 5, "second_value");
}
函数replace_index
将用于更改已包含在初始化程序中的字符串的值,但不能用于添加超出初始化程序中最大索引的字符串。有没有办法分配更多内存并添加新索引?
首先,如果您想进行严格的字符串操作,使用几乎任何其他语言或获取一个库来为您做这件事会容易得多。
无论如何,进入答案。
replace_index(strs, 5, "second_value");
在您的代码中不起作用的原因是因为 5 超出范围 - 该函数将写入与 strs
无关的内存。那不是你的问题,但如果你不问的话,知道这一点很重要。相反,您似乎想要附加一个字符串。下面的代码应该可以解决问题。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char **content;
int len;
} string_array;
void free_string_array(string_array *s) {
for (int i = 0; i < s->len; i++) {
free(s->content[i]);
}
free(s->content);
free(s);
}
int append_string(string_array *s, char *value) {
value = strdup(value);
if (!value) {
return -1;
}
s->len++;
char **resized = realloc(s->content, sizeof(char *)*s->len);
if (!resized) {
s->len--;
free(value);
return -1;
}
resized[s->len-1] = value;
s->content = resized;
return 0;
}
string_array* new_string_array(char *init[]) {
string_array *s = calloc(1, sizeof(string_array));
if (!s || !init) {
return s;
}
while (*init) {
if (append_string(s, *init)) {
free_string_array(s);
return NULL;
}
init++;
}
return s;
}
// Note: It's up to the caller to free what was in s->content[index]
int replace_index(string_array *s, int index, char *value) {
value = strdup(value);
if (!value) {
return -1;
}
s->content[index] = value;
return 0;
}
int main() {
string_array *s = new_string_array((char *[]) {"help", "me", "learn", "dynamic", "strings", NULL});
if (!s) {
printf("out of memory\n");
exit(1);
}
free(s->content[2]);
// Note: No error checking for the following two calls
replace_index(s, 2, "new_value");
append_string(s, "second value");
for (int i = 0; i < s->len; i++) {
printf("%s\n", s->content[i]);
}
free_string_array(s);
return 0;
}
此外,您 没有 将 char **
和 int
放在一个结构中,但如果这样做会更好。
如果您不想使用此代码,关键要点是必须动态分配字符串数组(char **
,如果您愿意)。意思是,您需要使用 malloc()
或类似的方法来获取所需的内存,并且您将使用 realloc()
来获取更多(或更少)的内存。不要忘记 free()
当您使用完后会得到什么。
我的示例使用 strdup()
来制作 char *
的副本,以便您随时可以根据需要更改它们。如果您无意这样做,删除 strdup()
ing 部分及其 free()
ing 部分可能更容易。
静态数组
char *strs[] = {"help", "me", "learn", "dynamic", "strings"};
这将 strs
声明为 array of pointer to char 并用 5 个元素对其进行初始化,因此隐含的 []
是 [5]
。如果不打算修改字符串,则更严格的 const char *strs[]
会更合适。
最大长度
char strs[][32] = {"help", "me", "learn", "dynamic", "strings"};
这将 strs
声明为一个 array of array 32 of char,它用 5 个元素初始化。这 5 个元素在字符串之外 zero-filled。最多可以修改 32 个字符,但不能添加更多。
常量字符串的最大单例容量
static struct str_array { size_t size; const char *data[1024]; } strs;
这将 pre-allocate 启动时的最大容量并使用它来满足请求。其中,capacity为1024,但size
可以是任意数字,不超过capacity。我做这个 static
的原因是通常要放置很多堆栈。没理由不能按要求做动态内存。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
static struct { size_t size; const char *data[1024]; } strs;
static const size_t strs_capacity = sizeof strs.data / sizeof *strs.data;
/** Will reserve `n` pointers to strings. A null return indicates that the size
is overflowed, and sets `errno`, otherwise it returns the first string. */
static const char **str_array_append(const size_t n) {
const char **r;
if(n > strs_capacity - strs.size) { errno = ERANGE; return 0; }
r = strs.data + strs.size;
strs.size += n;
return r;
}
/** Will reserve one pointer to a string, null indicates the string buffer is
overflowed. */
static const char **str_array_new(void) { return str_array_append(1); }
int main(void) {
const char **s;
size_t i;
int success = EXIT_FAILURE;
if(!(s = str_array_append(5))) goto catch;
s[0] = "help";
s[1] = "me";
s[2] = "learn";
s[3] = "dynamic";
s[4] = "strings";
strs.data[2] = "new_value";
if(!(s = str_array_new())) goto catch;
s[0] = "second_value";
for(i = 0; i < strs.size; i++) printf("->%s\n", strs.data[i]);
{ success = EXIT_SUCCESS; goto finally; }
catch:
perror("strings");
finally:
return success;
}
动态数组
struct str_array { const char **data; size_t size, capacity; };
我认为您要求的是 const char *
的动态数组。 Language-level 标准不支持动态数组 C
run-time;一个人必须自己写。这是完全可能的,但涉及更多。因为大小是可变的,所以它可能会更慢,但随着问题的增长,在极限内,以恒定的平均值。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
/** A dynamic array of constant strings. */
struct str_array { const char **data; size_t size, capacity; };
/** Returns success allocating `min` elements of `a`. This is a dynamic array,
with the capacity going up exponentially, suitable for amortized analysis. On
resizing, any pointers in `a` may become stale. */
static int str_array_reserve(struct str_array *const a, const size_t min) {
size_t c0;
const char **data;
const size_t max_size = ~(size_t)0 / sizeof *a->data;
if(a->data) {
if(min <= a->capacity) return 1;
c0 = a->capacity < 5 ? 5 : a->capacity;
} else {
if(!min) return 1;
c0 = 5;
}
if(min > max_size) return errno = ERANGE, 0;
/* `c_n = a1.625^n`, approximation golden ratio `\phi ~ 1.618`. */
while(c0 < min) {
size_t c1 = c0 + (c0 >> 1) + (c0 >> 3);
if(c0 >= c1) { c0 = max_size; break; } /* Unlikely. */
c0 = c1;
}
if(!(data = realloc(a->data, sizeof *a->data * c0)))
{ if(!errno) errno = ERANGE; return 0; }
a->data = data, a->capacity = c0;
return 1;
}
/** Returns a pointer to the `n` buffered strings in `a`, that is,
`a + [a.size, a.size + n)`, or null on error, (`errno` will be set.) */
static const char **str_array_buffer(struct str_array *const a,
const size_t n) {
if(a->size > ~(size_t)0 - n) { errno = ERANGE; return 0; }
return str_array_reserve(a, a->size + n)
&& a->data ? a->data + a->size : 0;
}
/** Makes any buffered strings in `a` and beyond if `n` is greater then the
buffer, (containing uninitialized values) part of the size. A null on error
will only be possible if the buffer is exhausted. */
static const char **str_array_append(struct str_array *const a,
const size_t n) {
const char **b;
if(!(b = str_array_buffer(a, n))) return 0;
return a->size += n, b;
}
/** Returns a pointer to a string that has been buffered and created from `a`,
or null on error. */
static const char **str_array_new(struct str_array *const a) {
return str_array_append(a, 1);
}
/** Returns a string array that has been zeroed, with zero strings and idle,
not taking up any dynamic memory. */
static struct str_array str_array(void) {
struct str_array a;
a.data = 0, a.capacity = a.size = 0;
return a;
}
/** Erases `a`, if not null, and returns it to idle, not taking up dynamic
memory. */
static void str_array_(struct str_array *const a) {
if(a) free(a->data), *a = str_array();
}
int main(void) {
struct str_array strs = str_array();
const char **s;
size_t i;
int success = EXIT_FAILURE;
if(!(s = str_array_append(&strs, 5))) goto catch;
s[0] = "help";
s[1] = "me";
s[2] = "learn";
s[3] = "dynamic";
s[4] = "strings";
strs.data[2] = "new_value";
if(!(s = str_array_new(&strs))) goto catch;
s[0] = "second_value";
for(i = 0; i < strs.size; i++) printf("->%s\n", strs.data[i]);
{ success = EXIT_SUCCESS; goto finally; }
catch:
perror("strings");
finally:
str_array_(&strs);
return success;
}
but will not work to add strings beyond the maximum index in the initializer
为此,您还需要指针数组是动态的。创建动态字符串数组是为数不多的可以使用 pointer-to-pointer 模拟二维数组的地方之一:
size_t n = 5;
char** str_array = malloc(5 * sizeof *str_array);
...
size_t size = strlen(some_string)+1;
str_array[i] = malloc(size);
memcpy(str_array[i], some_string, size);
您必须手动跟踪已用大小 n
,并且当您 运行 超出 str_array
时 realloc
有更多空间。 realloc
保证保留以前的值。
这非常灵活,但代价是分散分配,相对较慢。如果您使用 fixed-size 二维数组,代码会执行得更快,但您无法调整它们的大小。
请注意,我使用的是 memcpy
,而不是 memmove
- 前者是您通常应该使用的,因为它是最快的。 memmove
适用于您怀疑正在复制的两个数组可能重叠的特殊情况。
作为side-note,strlen
+malloc
+memcpy
可以替换为strdup
,目前是non-standard功能(但得到广泛支持)。 strdup
似乎有可能成为即将推出的 C23 版本 C 的标准,因此使用它会成为推荐的做法。