我正在处理一些遗留的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]->value
是const char *
,list_of_objs
本身被声明为cpp_extern const struct obj_info list_of_objs[]
并填充了静态数据。我相信这些是字符串文字,此时它们已经在代码中的其他位置使用,所以我无法修改它们的初始值。
我的任务是根据另一个值为values
数组中的每个条目添加动态前缀。 (为了简化下面的代码,我只是显示一个值 - 我可以轻松地if
或?:
来处理多个案例。)在C#中,这对于字符串连接来说是微不足道的,但我知道C与字符串的关系更复杂。
我天真的尝试是向其中声明一个缓冲区sprintf
,然后将其添加到values
数组中,但这给了我一个最终值的列表,重复{{1} }次。 (我理解为什么:重用缓冲区意味着数组中的每个元素都指向同一个缓冲区)
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);
直接进入sprintf
数组,但这给了我一个警告 - 应该是一个错误(the const
qualifier is discarded)。
values
我知道我可以在循环的每次迭代中声明一个缓冲区并使用它,但我担心这会泄漏内存。
我如何安全地将我修改后的字符串放入此数组?
答案 0 :(得分:2)
你可能需要这个:
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
}
答案 1 :(得分:2)
假设代码或多或少与您编写的完全相同,那么您永远不会对字符串进行硬拷贝,只需指向它们并将它们分配到别处(也许它们是只读字符串文字?)。这意味着您无法在此特定代码中修改它,但必须在源代码中对其进行修改。
因此,您必须通过在那里附加前缀(使用realloc,strcpy等)来修改list_of_objs[i]->value
,或者如果这些字符串被视为不可变,则使用前缀构建数据集的第二个副本加入。
具体取决于字符串的存储方式以及允许修改的内容。
修改
例如,如果您可以修改原始代码而不修改数据。我假设你有类似的东西:
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