Почему переменная внезапно становится равной 0? (язык c)

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

я ввел имя пользователя: Джеймс я ожидал, что результат будет:

j
5
a 
5
m
5
e
5
s
5
hello james , your name has 5 characters
ae
#include <stdio.h>
#include <cs50.h>
#include <string.h>

int main(void)
{
    string name = get_string("whats your name ? ");
    int x = strlen(name);
    char new[4] = "";
    for (int i = 0 ; i < x ; i++)
    {
        printf("%c\n" ,name[i] );
        if ( (i+1) % 2  == 0)
        {
            strcat(new,&name[i]);
        }
            printf("%i\n",x);
    }

    printf("hello %s ,your name has %i characters \n", name,x);
    printf("%s\n",new);
}
whats your name ? james
j
5
a
0
hello james ,your name is 0 characters long
ames

Шаг 1. Прекратите использовать string. Это ненужное запутывание, которое вызывает путаницу, а не проясняет что-либо.

William Pursell 23.07.2023 13:59

Чего вы ожидаете от strcat(new,&name[i]);? Такое ощущение, что вы пытаетесь добавить один символ к new, но на самом деле эта строка делает не это.

William Pursell 23.07.2023 14:06
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать 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
2
75
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Основная проблема заключается в том, что буфер new недостаточно велик для хранения данных, которые вы в него записываете. Я подозреваю, что основная путаница связана с неправильным использованием strcat. Когда вы пишете strcat(new,&name[i]);, он не копирует ни одного символа в конец new. Если name — это «james», а i — это 2, то &name[i] — это адрес «m», а strcat пытается скопировать строку «mes» в конец new. Если вы просто хотите скопировать один символ, strcat — неправильный инструмент. ИМО, эта путаница усугубляется использованием string typedef, единственной целью которого является запутать учащихся. Прекратите использовать string и вместо этого используйте char *. Вы должны понимать эту деталь, если хотите когда-нибудь понять C.

Если вы хотите перебрать строку и скопировать каждый второй символ в массив, возможно, вам нужно что-то вроде ниже. Обратите внимание, что вы должны проверить границы массива, если вы используете неизвестный ввод.

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

int
main(int argc, char **argv)
{
    char *name = argc > 1 ? argv[1] : "james";
    int length = strlen(name);
    char new[128] = "";
    char *d = new;
    /*
    Checking the parity of i inside the loop is a code smell, but
    we leave this as dead code for comparison to the original.  Simply
    skipping the unwanted indices is done below the dead code.
    for( int i = 0; i < length && i < sizeof new - 1; i += 1 ){
        if ( (i+1) % 2 == 0 ){
            *d++ = name[i];
        }
    }
    */
    for( int i = 1; i < length && i < sizeof new - 1; i += 2 ){
        *d++ = name[i];
    }
    *d = '\0';
    printf("hello %s, your name has %i characters \n", name, length);
    printf("%s\n", new);
    return 0;
}

Многие сочли бы более естественным написать цикл примерно так:

char *end = new + min(length, sizeof new);
for( name += 1; d < end; name += 2 ){                                      
        *d++ = *name;                                                      
} 

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

Указание людям, обучающимся через CS50, не использовать string, вероятно, приведет к путанице, поскольку именно этому учит курс и что использует заголовок cs50.h.

Jonathan Leffler 23.07.2023 15:00

... что является одним из худших недостатков CS50. string typedef глубоко укоренился в курсе, но по своей сути он сбивает с толку и даже вводит в заблуждение. Думаю, в CS50 есть что-то, что может понравиться, но в целом мне сложно его рекомендовать.

John Bollinger 23.07.2023 15:58
Ответ принят как подходящий

Вы ввели строку "james", содержащую 6 символов, включая завершающий нулевой символ '\0'.

whats your name ? james

И вы объявили массив символов, который состоит только из 4 элементов.

char new[4] = "";

В этом операторе if

    if ( (i+1) % 2  == 0)
    {
        strcat(new,&name[i]);
    }

когда, например, i равно 1, строка, на которую указывает выражение указателя name + 1, добавляется к массиву символов new.

        strcat(new,&name[i]);

Это следующая строка (представленная в виде массива символов) { 'a', 'm', 'e', 's', '\0' }. Как видно, строка содержит 5 символов, включая завершающий нулевой символ '\0'. В результате этот вызов strcat приводит к перезаписи памяти вне массива new, что приводит к неопределенному поведению.

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

Вы могли бы написать, например

for (int i = 0 ; i < x ; i++)
{
    printf("%c\n" ,name[i] );
    if ( (i+1) % 2  == 0)
    {
        new[i / 2] = name[i];
    }
        printf("%i\n",x);
}

Обратите внимание, что массив new будет содержать строку, так как все его элементы изначально были инициализированы нулями в объявлении массива

char new[4] = "";

С другой стороны, использование магического числа 4 в объявлении массива небезопасно. Вместо этого вы можете объявить, например, массив переменной длины, например

int x = strlen(name);
char new[x / 2 + 1];
memset( new, 0, x / 2 + 1 );

В этом случае ваша программа будет работать, даже если пользователь введет более длинное имя.

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