在存储const char数组之前修改字符串

时间:2018-05-14 12:59:37

标签: c arrays string

我正在处理一些遗留的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数组中,但这给了我一个最终值的列表,重复{{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

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

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

2 个答案:

答案 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