C语言 在存储常量字符数组之前修改字符串



我正在处理一些遗留的 C 代码(通常我使用 C#(,并且我很难处理一个特定的字符串行为。

我们拥有的代码如下所示:

int no = 0;
const char *values[1000];
for(int i = 0; i < num_of_values; i++) {
if (/*is invalid*/) continue;
values[no++] = list_of_objs[i]->value;
}
send_to_display(values, no);

其中list_of_objs[i]->valueconst char *list_of_objs本身声明为cpp_extern const struct obj_info list_of_objs[]并填充静态数据。我相信这些是字符串文字,在这一点上,它们按原样在代码中的其他地方使用,所以我无法修改它们的初始值。

我的任务是根据另一个值为values数组中的每个条目添加一个动态前缀。(为了下面的代码简单起见,我只显示一个值 - 我可以轻松地if?:来处理多种情况。 在 C# 中,这对于字符串连接来说是微不足道的,但我知道 C 与字符串的关系要多得多......复杂。

我天真的尝试是声明一个缓冲区,sprintf到其中,然后将其添加到values数组中,但这给了我一个最终值的列表,重复了i次。 (我明白为什么:重用缓冲区意味着数组中的每个元素都指向同一个缓冲区(

int no = 0;
const char *values[1000];
char buf[MAX_STRING_LENGTH];
for(int i = 0; i < num_of_values; i++) {
if (/*is invalid*/) continue;
sprintf(buf, "Value: %s", list_of_objs[i]->value);
values[no++] = buf;
}
send_to_display(values, no);

我还尝试直接sprintfvalues数组,但这给了我一个警告,即应该是一个错误(const限定符被丢弃(。

sprintf(values[no++], "Value: %s", list_of_objs[i]->value);

我知道我可以在循环的每次迭代中声明一个缓冲区并使用它,但我担心这会泄漏内存。

如何安全地将修改后的字符串放入此数组中?

您可能需要这个:

int no = 0;
const char *values[1000];
for(int i = 0; i < num_of_values; i++) {
if (/*is invalid*/) continue;
char tempbuffer[100];          // provide enough space for the worst case
sprintf(tempbuffer, "Value: %s", list_of_objs[i]->value);
values[no++] = strdup(tempbuffer);
}

但是,一旦完成指针,您需要释放values中的指针:

for (int i = 0; i < number_of_values_added; i++)
free(values[no++]);

如果您的平台上没有strdup

char *strdup(const char *string)
{
char newstring = malloc(strlen(string) + 1);
if (newstring)
strcpy(newstring, string);
return newstring;
}

免责声明:为简洁起见,此处不进行错误检查。

免责声明2:可能还有其他方法可以解决问题,但就目前而言,问题还不够清楚。

你幼稚的尝试以及为什么它是错误的:

char buf[MAX_STRING_LENGTH];  // buf is just a buffer, it's not at all a string in terms oc C#
for(int i = 0; i < num_of_values; i++) {
if (/*is invalid*/) continue;
sprintf(buf, "Value: %s", list_of_objs[i]->value);
values[no++] = buf;  // << you store the same buffer address in all elements of value
}

假设代码或多或少与您编写的完全一致,那么您永远不会硬拷贝字符串,您只需指向它们并将它们分配给其他地方(也许它们是只读字符串文字?这意味着您不能在此特定代码中修改它,但必须在源代码中修改它。

因此,要么你必须通过在那里附加前缀来修改list_of_objs[i]->value(使用 realloc、strcpy 等(,要么如果这些字符串被视为不可变的,则使用添加的前缀构建数据集的第二个副本。

如何执行此操作具体取决于字符串的存储方式以及允许您修改的内容。


编辑

例如,如果可以修改原始代码,但不能修改数据。我假设你有这样的东西:

typedef struct
{
const char* value;
} obj_t;

int main(void)
{
const obj_t list_of_objs[2] =
{
{.value = "hello" },
{.value = "world" },
};
return 0;
}

您可以使用所谓的"X 宏"来维护代码 - 这些通常是要避免的,但在维护遗留代码时它们可能很有意义。

它们使代码阅读起来有点严峻,但更容易维护。最重要的是,字符串分配/串联是在编译时完成的:

#include <stdio.h>
typedef struct
{
const char* value;
} obj_t;

#define OBJ_STRINGS_LIST   
/*  val      prefix:   */  
X("hello", "value1: ")   
X("world", "value2: ")   

int main(void)
{
const obj_t list_of_objs[2] =
{
// original initialization as before, ignore prefix
#define X(val, prefix) {.value = val },
OBJ_STRINGS_LIST
#undef X
};
const char* prefix_list[2] = 
{
// create a look-up table with prefix values based on string literal concatenation
#define X(val, prefix) prefix val,
OBJ_STRINGS_LIST
#undef X
};
puts(list_of_objs[0].value);
puts(prefix_list[0]);
return 0;
}

输出:

hello
value1: hello

最新更新