Экземпляры Typedef сохраняют только последний ввод

Я пытаюсь получить несколько (на данный момент 3) имен в качестве кандидатов на голосование и назначить их экземплярам typedef с помощью определенного мной массива. Но когда я запускаю код, в массиве сохраняется только фамилия, как вы можете видеть во втором цикле, распечатывается только последний ввод. Может кто-нибудь, пожалуйста, дайте мне знать, что я делаю здесь неправильно?

Мой код:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct
{
    char *name;
    int votes;
}
candidate;
#define MAX 9
candidate candidates[MAX];

int main(void)
{
    char candid[10];
    for (int i =0; i<3; i++)
    {
        scanf("%s",candid);
        candidates[i].name = candid;
        candidates[i].votes = 0;
    }

    printf("Names: \n");

    for (int i = 0; i < 3; i++)
    {
        printf("%s\n",candidates[i].name);
    }
    

}

Выход:

Charlie
Ana 
David
Names: 
David
David
David

Есть только одна строка candid. Все элементы вашего массива имеют указатель на этот же массив. Либо измените структуру на использование char name[10];, а затем используйте strcpy(), либо используйте candidates[i].name = strdup(candid);

Barmar 23.05.2024 00:48
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
80
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Как проницательно отмечено в комментариях, вы храните несколько копий указателя на одну и ту же строковую память. Вам нужно, чтобы у каждого candidate была собственная память для хранения имени. Учитывая размер имени, имеет смысл обойтись без динамического выделения.

Вы также, вероятно, захотите избежать магических чисел, и нет необходимости в том, чтобы candidates выходил за пределы функции main.

Использование fgets вместо scanf позволяет нам указать максимум NAME_SZ символов для чтения, предотвращая переполнение буфера и позволяя нам избежать жестко запрограммированных размеров в поле ширины спецификатора scanf. Для дальнейшей настройки вам нужно обрезать любую возможную новую строку. Использование fgets также означает, что имена не обязательно должны быть мононимами, но могут включать пробелы.

#include <stdio.h>

#define NAME_SZ 20
#define MAX_CANDIDATES 3

typedef struct {
    char name[NAME_SZ];
    int votes;
} candidate;

int main(void) {
    candidate candidates[MAX_CANDIDATES];

    for (int i = 0; i < MAX_CANDIDATES; i++) {
        fgets(candidates[i].name, NAME_SZ, stdin);
        candidates[i].votes = 0;
    }

    printf("Names: \n");

    for (int i = 0; i < MAX_CANDIDATES; i++) {
        printf("%s\n", candidates[i].name);
    }
}

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

#include <stdio.h>

#define NAME_SZ 20
#define MAX_CANDIDATES 3

typedef struct {
    char *name;
    int votes;
} candidate;

int main(void) {
    candidate candidates[MAX_CANDIDATES];

    for (int i = 0; i < MAX_CANDIDATES; i++) {
        char name[NAME_SZ];
        fgets(name, NAME_SZ, stdin);

        candidates[i].name = malloc(strlen(name) + 1);
        strcpy(candidates[i].name, name);

        // Or just use strdup:
        // candidates[i].name = strdup(name);

        candidates[i].votes = 0;
    }

    printf("Names: \n");

    for (int i = 0; i < MAX_CANDIDATES; i++) {
        printf("%s\n", candidates[i].name);

        // Assuming we don't need them further,
        // we can clean up here.
        free(candidates[i].name);
    }
}

Также стоило бы добавить ссылку на Как предотвратить переполнение буфера scanf() в C?, хотя, поскольку я упомянул об этом здесь, теперь это менее критично. И я полностью согласен, что 10 было удручающе мало для имени кандидата.

Jonathan Leffler 23.05.2024 03:23

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

Chris 27.05.2024 03:10

Другие вопросы по теме