Как правильно назначить новое строковое значение? - PullRequest
44 голосов
/ 28 июня 2010

Я пытаюсь понять, как решить эту тривиальную проблему в C самым чистым / безопасным способом. Вот мой пример:

#include <stdio.h>

int main(int argc, char *argv[])
{
    typedef struct
    {
        char name[20];
        char surname[20];
        int unsigned age;
    } person;

    //Here i can pass strings as values...how does it works?
    person p = {"John", "Doe",30};

    printf("Name: %s; Age: %d\n",p.name,p.age);
    // This works as expected...
    p.age = 25;
    //...but the same approach doesn't work with a string
    p.name = "Jane";

    printf("Name: %s; Age: %d\n",p.name,p.age);

    return 1;
}

Ошибка компилятора:

main.c: в функции «main»: main.c: 18: ошибка: несовместимые типы при присвоение типу 'char [20]' из типа ‘Char *’

Я понимаю, что C (не C ++) не имеет типа String и вместо этого использует массивы символов, поэтому другой способ сделать это - изменить пример структуры, чтобы она содержала указатели символов:

#include <stdio.h>

int main(int argc, char *argv[])
{
    typedef struct
    {
        char *name;
        char *surname;
        int unsigned age;
    } person;

    person p = {"John", "Doe",30};

    printf("Name: %s; Age: %d\n",p.name,p.age);

    p.age = 25;

    p.name = "Jane";

    printf("Name: %s; Age: %d\n",p.name,p.age);

    return 1;
}

Это работает, как и ожидалось, но мне интересно, есть ли лучший способ сделать это. Спасибо.

Ответы [ 3 ]

39 голосов
/ 28 июня 2010

Первый пример не работает, потому что вы не можете присвоить значения массивам - массивы работают (вроде) как константные указатели в этом отношении.Однако вы можете скопировать новое значение в массив:

strcpy(p.name, "Jane");

Массивы символов можно использовать, если вы заранее знаете максимальный размер строки, например, в первом примере вы на 100%убедитесь, что имя будет соответствовать 19 символам (не 20, потому что для хранения конечного нулевого значения всегда требуется один символ).

И наоборот, указатели лучше, если вы не знаете возможный максимальный размер вашей строкии / или вы хотите оптимизировать использование памяти, например, избегайте резервирования 512 символов для имени «Джон».Однако с указателями вам нужно динамически выделять буфер, на который они указывают, и освобождать его, когда он больше не нужен, чтобы избежать утечек памяти.

Обновление: пример динамически распределенных буферов (с использованиемопределение структуры во втором примере):

char* firstName = "Johnnie";
char* surname = "B. Goode";
person p;

p.name = malloc(strlen(firstName) + 1);
p.surname = malloc(strlen(surname) + 1);

p.age = 25;
strcpy(p.name, firstName);
strcpy(p.surname, surname);

printf("Name: %s; Age: %d\n",p.name,p.age);

free(p.surname);
free(p.name);
9 голосов
/ 28 июня 2010

Думайте о строках как об абстрактных объектах, а массивы символов как о контейнерах. Строка может быть любого размера, но контейнер должен быть как минимум на 1 больше длины строки (для хранения нулевого терминатора).

C имеет очень небольшую синтаксическую поддержку строк. Строковых операторов нет (только операторы char-array и char-pointer). Вы не можете назначать строки.

Но вы можете вызывать функции, чтобы добиться желаемого.

Здесь можно использовать функцию strncpy(). Для максимальной безопасности я предлагаю следующий шаблон:

strncpy(p.name, "Jane", 19);
p.name[19] = '\0'; //add null terminator just in case

Также обратите внимание на функции strncat() и memcpy().

5 голосов
/ 28 июня 2010

Две структуры разные.Когда вы инициализируете первую структуру, выделяется около 40 байт памяти.Когда вы инициализируете вторую структуру, выделяется около 10 байт памяти.(Фактическое количество зависит от архитектуры)

Вы можете использовать строковые литералы (строковые константы) для инициализации символьных массивов.Вот почему

person p = {"John", "Doe", 30};

работает в первом примере.

Вы не можетеприсвойте (в общепринятом смысле) строку в C.

Строковые литералы, которые у вас есть ("Джон"), загружаются в память при выполнении вашего кода.Когда вы инициализируете массив одним из этих литералов, строка копируется в новую ячейку памяти.Во втором примере вы просто копируете указатель на (местоположение) строкового литерала.Выполнение чего-то вроде:

char* string = "Hello";
*string = 'C'

может привести к ошибкам компиляции или выполнения (я не уверен.) Это плохая идея, потому что вы модифицируете буквенную строку «Hello», которая, например, в микроконтроллере, можетнаходиться в постоянной памяти.

...