Ошибка присвоения значений ячейке памяти с помощью цикла for

Я только что вошел в мир программирования на C и сейчас учусь использовать malloc и free.

Я написал короткий код упражнения для printf строки, введенной с помощью scanf, и у меня возникли проблемы с его компиляцией.

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

int main(void)
{
    char *keyword;
    printf("keyword: ");
    scanf("%s", keyword);
    if (keyword == NULL)
        return 1;

    char *t = malloc(strlen(keyword) + 1);
    if (t == NULL)
        return 1;

    for (int i = 0, n = strlen(keyword) + 1; i < n; i++)
        t[i] = keyword[i];

    printf("t: %s\n", t);

    free(t);
    return 0;
}

Для отладки я написал тот же код, но без цикла for (код ниже), и тогда код работал хорошо. Это привело меня к выводу, что проблема может быть в цикле for, который используется для назначения символов соответствующим ячейкам памяти, но я до сих пор не могу найти решение.

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

int main(void)
{
    char *keyword;
    printf("keyword: ");
    scanf("%s", keyword);
    if (keyword == NULL)
        return 1;

    char *t = malloc(strlen(keyword) + 1);
    if (t == NULL)
        return 1;

    t[0] = keyword[0];
    t[1] = keyword[1];
    t[2] = keyword[2];
    t[3] = keyword[3];
    t[4] = keyword[4];
    t[5] = keyword[5];
    printf("t: ");
    printf("%c", t[0]);
    printf("%c", t[1]);
    printf("%c", t[2]);
    printf("%c", t[3]);
    printf("%c", t[4]);
    printf("%c", t[5]);
    printf("\n");

    free(t);
    return 0;
}

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

Когда вы это делаете scanf("%s", keyword), на что указывает keyword? Что ваши материалы для начинающих (курсы, учебные пособия, учителя, книги и т. д.) говорят о scanf, его спецификаторах формата и соответствующих аргументах?

Some programmer dude 10.07.2024 08:28

Что касается проблем со сборкой кода, то если вы новичок и не выполняете сборку с включенным дополнительным предупреждением, он действительно будет собран, но у вас, скорее всего, возникнут проблемы при его запуске. Вам нужно всегда включать дополнительные предупреждения (и действительно относиться к ним как к ошибкам), потому что компилятор сообщит вам о вашей проблеме.

Some programmer dude 10.07.2024 08:32

@Someprogrammerdude Судя по тому, что я изучил и понял, *keyword просто объявляет указатель, но не выделяет никакой памяти. И я подумал, что scanf автоматически вычислит размер строки и соответствующим образом выделит память (честно говоря, я усвоил всего несколько процентов всех этих концепций, таких как указатель, выделение памяти или scanf, и признаю, что мне нужно больше учиться, чтобы понять их).

Woodrow 10.07.2024 10:22

@Someprogrammerdude Также я ценю твой совет. Мой отладчик не обнаружил никаких проблем при запуске второго кода, и я решил, что его можно использовать char *keyword;. Включение дополнительных предупреждений мне очень поможет в дальнейшем!

Woodrow 10.07.2024 10:23

«Я думал, что scanf автоматически вычислит размер строки и соответствующим образом выделит память». К сожалению, это невозможно. Ни одна функция в C, созданная пользователем или одна из стандартных функций, не может изменить свой аргумент для вызывающей стороны. Это, конечно, означает, что указатель, переданный scanf, не может быть изменен функцией. Он изменяет память, на которую указывает указатель. Кроме того, если вы не инициализируете локальную переменную, ее значение будет неопределенным (смотрите на это как на мусор), что в вашем случае означает, что keyword может указывать куда угодно.

Some programmer dude 10.07.2024 10:51

@Woodrow scanf не выделяет память. Он ожидает передачи действительных адресов буферов/переменных.

wohlstad 10.07.2024 11:01
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
6
88
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

Должен ли я привести результат malloc (в C)? ТЛ;ДР: Нет.
Some programmer dude 10.07.2024 09:03

and then the code worked well. ...
ЭТО НЕ РАБОТА well !!
Это просто потому что... ты счастливчик....

Прежде всего, когда вы компилируете свой код, вы можете получить предупреждающее сообщение об этом коде:

char *keyword;
scanf("%s", keyword);

Потому что,

  1. Вам НЕ присваивается (начальное) значение (указатель) keyword.
  2. Если указатель keyword неопределенен, вы используете его в качестве входного указателя для scanf().

Итак, где хранить данные с входа «scanf()»?? Это определенно место памяти!

Итак, вам повезло, что ваша программа не вышла из строя...

НЕ игнорируйте предупреждающее сообщение в любой ситуации!

В вашем случае лучше изменить его на:

char keyword[1048575];
scanf("%s", &keyword);

-или-

char *keyword = malloc(1048575);
scanf("%s", keyword);

Тогда вы можете плавно столкнуться с провалом.
~наслаждайся~

Хотя не следует жадничать с размерами массива, 1048575 — это слишком много. Более пары сотен элементов — это просто пустая трата места.

Some programmer dude 10.07.2024 09:02

@Someprogrammerdude Да, но он должен выяснить эту проблему и исправить ее самостоятельно。

James 11.07.2024 08:59
Ответ принят как подходящий

Следующие строки:

char *keyword;
printf("keyword: ");
scanf("%s", keyword);
if (keyword == NULL)
     return 1;

Есть несколько проблем:

  1. keyword не инициализируется. Его необходимо инициализировать, чтобы он указывал на некоторый буфер/память, чтобы scanf войти в него в scanf("%s", keyword);.
    Неинициализированный доступ к нему вызывает неопределенное поведение (UB).
    Чтобы его инициализировать, вам следует определить максимальную длину, которую вы для него допускаете, а затем инициализировать его следующим образом:
    (1) Использование malloc
    (2) Использование распределения стека (если максимальный размер относительно невелик)
    (3) Установка его так, чтобы он указывал на какой-то существующий буфер
    Не забудьте добавить 1 к максимальной длине при выделении для нулевого завершения.

  2. После определения максимальной длины keyword вы должны передать это значение scanf, чтобы убедиться, что буфер не переполнен, например:

    ... = scanf("%33s", keyword);  // here the maximum length is 33
    
  3. Вы всегда должны проверять возвращаемое значение scanf. Если это удалось, он возвращает:

    Количество успешно назначенных аргументов приема

    (здесь должно быть 1).

  4. (keyword == NULL) вряд ли будет правдой (разве что случайно), независимо от того, удалось scanf или нет. И если вы правильно его инициализируете, как описано выше, этого никогда не будет NULL.

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