Модификация строки перед сохранением в массиве константных символов - PullRequest
0 голосов
/ 14 мая 2018

Я работаю над некоторым унаследованным кодом 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 # это было бы тривиально с конкатенацией строк, но я знаю,Отношения Си со строками гораздо более ... сложные.

Моя наивная попытка состояла в том, чтобы объявить один буфер, 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);

Я также пытался sprintf напрямую в массив values, но этовыдает мне предупреждение, которое должно быть ошибкой ( квалификатор const отбрасывается ).

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

Я знаю, что мог бы объявить буфер в каждой итерациицикл и использовать его, но я беспокоюсь, что это приведет к утечке памяти.

Как я могу безопасно получить мою измененную строку в этот массив?

Ответы [ 2 ]

0 голосов
/ 14 мая 2018

Предполагая, что код более или менее точно соответствует тому, что вы написали, вы никогда не копируете строки строго, просто указываете на них, и они размещаются где-то еще (возможно, они являются строковыми литералами только для чтения?).Это означает, что вы не можете изменить его в этом конкретном коде, но вы должны изменить его в исходном коде.

Таким образом, вы должны либо изменить 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
0 голосов
/ 14 мая 2018

Возможно, вам нужно это:

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
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...