Как правильно присвоить новое строковое значение?

Я пытаюсь понять, как решить эту тривиальную проблему в 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: В 'основной' функции: основной c:18: ошибка: несовместимые типы при присвоении для ввода ‘символа [20]’ от типа ‘символ *’

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

#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;
}

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

45
задан Lundin 6 July 2016 в 12:35
поделиться

3 ответа

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

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);
40
ответ дан 26 November 2019 в 21:27
поделиться

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

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

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

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

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

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

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

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

5
ответ дан 26 November 2019 в 21:27
поделиться

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

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

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

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

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

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

9
ответ дан 26 November 2019 в 21:27
поделиться
Другие вопросы по тегам:

Похожие вопросы: