#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» теперь в порядке).
А теперь перейдем к текущей проблеме.
Существует два сценария:
- Каждый узел имеет свою собственную строку и «владеет» этой строкой. Каждый узел отвечает за выделение памяти для строки и освобождение этой памяти. Строка живет столько, сколько живет узел. В этом случае используйте
malloc
иstrcpy
для создания строки для каждого узла. Не забудьтеfree
указать строку, когда узел будет уничтожен. Это наиболее распространенный сценарий и, вероятно, то, что вы хотите. - Узел не владеет собственной строкой, а скорее указывает на внешнюю строку. Не разрешается ни выделять, ни освобождать память для этой строки. Существует другой объект, ответственный за управление временем жизни этой строки и обеспечение того, чтобы строка была живой, по крайней мере, пока узел живой. Строка может пережить узел без утечки памяти.
Например, рассмотрим этот сценарий:
- существует список R строковых ресурсов. Этот список владеет этими ресурсами. В этом списке будет использоваться сценарий 1
- нам нужно сохранить два разных порядка сортировки R. Итак, у нас есть два списка A и B, которые будут использовать сценарий 2: каждый узел в A и B просто указывает на строку R.