Строка против строкового литерала: прямое присвоение или strcpy / strncpy?

#c #struct #strcpy

#c #структура #strcpy

Вопрос:

Я думаю, что у меня есть некоторые проблемы с пониманием строки и строкового литерала.

Это то, что я узнал из своего класса, при передаче в функцию const char * указывает, что это строковый литерал, в то время как char * указывает на строку.

Предположим, у меня есть объявление структуры:

 struct node{
    char *name;
    struct node *next;
};
 

и функция, это функция, которую я хочу реализовать:

 void load_buffer(struct node *input_node, char *input_name);
 

Предполагается, что эта функция присваивает input_name имени элемента структуры.

Отсюда и моя путаница. Должен ли я написать в теле load_buffer:

 input_node->name = input_name;
 

Или я должен использовать strcpy / strncpy для этого?

 strcpy(input_node->name, input_name);// Suppose it's safe in this case.
 

Подводя итог, я не уверен, следует ли мне использовать прямое присвоение или функции семейства strcpy для присвоения строкового / строкового литерала члену моей структуры.

Спасибо за помощь. 🙂

Комментарии:

1. input_node->name = input_name; должно быть хорошо, учитывая, что в input_name дальнейшем это не изменится.

2. Это зависит от семантики, которую вы хотите: должна ли struct быть строка, на которую ссылается ссылка, и как она должна быть выделена?

3. Если у вас есть указатель, вам нужно, чтобы он указывал куда-то правильно перед использованием strcpy . Что касается того, что использовать, это действительно зависит. Вы всегда будете использовать строковые литералы? Вам нужно изменить строки? Добавлять к ним?

4. Нет, мне не нужно изменять строку, «строка», которую я буду использовать, считывается из файла, так означает ли это, что я должен использовать прямое присвоение вместо функций семейства strcpy?

5. «строка» будет одинаковой для всех узлов?

Ответ №1:

В случае назначения указателя указатели в каждом узле будут указывать на одно и то же местоположение. Таким образом, узлы всегда будут указывать на обновленное значение. Если вы хотите, чтобы каждый узел содержал разные input данные, то этот подход не соответствует вашим требованиям.

 input_node->name = input_name;
 

В случае strcpy указатели в каждом узле будут указывать на другое местоположение. Перед этим вам нужно создать память для каждого указателя.

 input_node->name = malloc(strlen(input_name) 1); //Allocate memory first.
strcpy(input_node->name, input_name);// Suppose it's safe in this case.
 

Для визуализации:
введите описание изображения здесь

Комментарии:

1. ты делаешь malloc() , ты должен free() .

Ответ №2:

… при передаче в функцию const char * указывает, что это строковый литерал, в то время char * как указывает строку.

Не совсем. const char * объявляет, что функция не будет пытаться изменить строку. Таким образом, он идеально подходит для строковых букв, потому что они не могут быть изменены.

На ваш вопрос ответ таков: это зависит от ваших реальных требований. Но просто сохранять переданный указатель, если опасно, если структура может сохраняться после функции и если строка может быть изменена в вызывающем. Давайте посмотрим на следующий код:

 void load_buffer(struct node *input_node, const char *input_name) {
    input_node->name = name;
}

struct node nodes[2];
char buf[4];
const char *data[] = { "foo", "bar"};
for (int i=0; i<2; i  ) {
    strcpy(buf, data[i]);    // suppose it is read from somewhere (file, stdin, socket)
    load_buffer(node   i, buf);
}
 

Оба node объекта будут иметь свой name элемент, указывающий на строку buf от вызывающего абонента, и оба будут указывать на "bar" (содержимое buf в конце цикла)!

Если вы хотите сохранить значение строки во время вызова, вы должны скопировать его в выделенную память:

 void load_buffer(struct node *input_node, const char *input_name) {
    input_node->name = strdup(name);  // allocation errors should be tested...
}
 

Но затем вы должны освободить name элемент, когда узел больше не используется…

Комментарии:

1. Да, const означает больше, что изменять значение недопустимо, чем то, что это магическая константа. Они передаются в функции довольно часто, функция не может их изменить.

Ответ №3:

Сначала рассмотрим некоторую терминологию:

  • это строковый литерал: "I am a string literal"
  • это тип: char* (он же указатель на символ)
  • это также тип: const char* (он же указатель на постоянный символ)
  • это объявление переменной типа char* : char* str
  • это объявление переменной типа const char* : const char* cstr

Указатель не является строковым литералом. Указатель может указывать на строковый литерал, массив, только на один элемент или может быть нулевым.

В C строке находится массив символов с нулевым завершением.

В C вы можете присвоить char* переменную строковому литералу, но изменение строкового литерала является незаконным, поэтому настоятельно рекомендуется никогда этого не делать. Причина, по которой это разрешено, историческая.

 char* a = "asd"; // allowed, but frowned upon
// a points to a string literal, so we can say a is a string
// a is not a string literal

char b = 'x';
char* c = amp;b;
// c points to a single char
// we cannot say c is a string

char d[10] = "asd";
// d is a char array. Its content is a string, so we can say d is a string.
// d is not a string literal
// the string literal is copied into the array d

char* e = d; // or equivalent char* e = amp;d[0];
// e points to a string

char f[4] = {'a', 's', 'd', ''};
// f is an array. Its content is a string, so we can say f is a string

char* g = f;
// g points to a string. We can say g is a string

char h[3] = {'a', 's', 'd'};
// h is an array. Its content is NOT a string, because the char array is not null terminated

char* i = h;
// i is not a string
 

А теперь пройдитесь по всему вышесказанному еще раз, но не заменяйте char на const char , и все комментарии останутся в силе (за исключением того, что `const char * a = «asd» теперь в порядке).


А теперь перейдем к текущей проблеме.

Существует два сценария:

  1. Каждый узел имеет свою собственную строку и «владеет» этой строкой. Каждый узел отвечает за выделение памяти для строки и освобождение этой памяти. Строка живет столько, сколько живет узел. В этом случае используйте malloc и strcpy для создания строки для каждого узла. Не забудьте free указать строку, когда узел будет уничтожен. Это наиболее распространенный сценарий и, вероятно, то, что вы хотите.
  2. Узел не владеет собственной строкой, а скорее указывает на внешнюю строку. Не разрешается ни выделять, ни освобождать память для этой строки. Существует другой объект, ответственный за управление временем жизни этой строки и обеспечение того, чтобы строка была живой, по крайней мере, пока узел живой. Строка может пережить узел без утечки памяти.

Например, рассмотрим этот сценарий:

  • существует список R строковых ресурсов. Этот список владеет этими ресурсами. В этом списке будет использоваться сценарий 1
  • нам нужно сохранить два разных порядка сортировки R. Итак, у нас есть два списка A и B, которые будут использовать сценарий 2: каждый узел в A и B просто указывает на строку R.