Преобразование верхнего регистра в нижний регистр с помощью указателей в C - PullRequest
2 голосов
/ 16 марта 2019

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

#include <stdlib.h>
#include <string.h>
char *changeL(char *s);
char *changeL(char *s)
{
    char *upper = s;

    for (int i = 0; upper[i] != '\0'; i++)
    {
       if (upper[i] >= 'A' && upper[i] <= 'Z')
        {
           upper[i] += 32;
        }
     }
   printf("%s\n", upper);
   return upper;
}



int main()
{
    char *first;
    char *second;
    first = "HELLO My Name is LoL";
    printf("%s\n", first);
    second = changeL(first);
    printf("There is no error here\n\n");
    printf("%s\n", second);



    return 0;
 }

Используя gdb, я обнаружил, что ошибка сегмента находится в "upper [i] + = 32;". Я не понимаю, почему произошла ошибка сегмента.

Ответы [ 2 ]

5 голосов
/ 16 марта 2019

«Здравствуйте, меня зовут LoL» - это постоянная память. Вы не можете это изменить. Однако вы передаете указатель на эту память (сначала) в функцию, которая пытается изменить ее. Таким образом, вы получили ошибку сегментации. Вы должны скопировать эту строку в буфер памяти. Нравится

char buffer[] = "HELLO My Name is LoL";

и затем передать буфер для изменения L

3 голосов
/ 16 марта 2019

Пара замечаний в дополнение к тому, что @ Alex правильно указывает в своем ответе. Во-первых

char *changeL(char *s);
char *changeL(char *s)
{
   ....
}

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

Второй, как отмечалось в ответе Алекса, в подавляющем большинстве систем - String Literal , например. "Something Here" в char *s = "Something Here"; является неизменным и находится в постоянной памяти, и любая попытка изменить строковый литерал обычно приводит к SegFault.

Вместо этого вам нужно создать массив символов, который можно изменить, например,

char first[] = "HELLO My Name is LoL";

или с C99 + вы можете использовать составной литерал для инициализации first в качестве указателя на массив char, например,

char *first = (char[]){ "HELLO My Name is LoL" };

В обоих случаях выше символы, на которые указывает first, могут быть изменены.

Добавление за комментарий

"can you also explain to him why is he getting segfault at upper[i] += 32;"

Да. Как уже упоминалось выше, когда вы инициализируете указатель на String Literal практически во всех современных системах (древние системы не имели различий или защиты для постоянной памяти - вся память была доступна для записи). В текущий день создание строкового литерала (например, "foo") создает в памяти строку, которую нельзя изменить. (для исполняемых файлов ELF, которые обычно находятся в разделе .rodata исполняемого файла - разбирая ближе ".ro...data", что означает "read-only data")

Когда делается любая попытка изменить данные, которые нельзя изменить, обычно возникает ошибка сегментации, поскольку вы пытались записать адрес в сегменте, доступном только для чтения. (таким образом, ошибка сегментации - SegFault)

В приведенном выше коде, как первоначально написано с

first = "HELLO My Name is LoL";

Если вы скомпилируете в сборку (например, в Linux, например, gcc -S -masm=intel -o mysaved.asm myfile.c, вы увидите, что строка "HELLO My Name is LoL" фактически создана в разделе .rodata. У вас нет возможности изменить эти данные - вы сейчас знать, что происходит, когда вы пытаетесь :)

Код, написанный в Вопросе, также демонстрирует путаницу в отношении того, на что в действительности указывают указатели first и second. При назначении возврата changeL на second не создается новая память для second. Это ничем не отличается от простого присвоения second = first; в main(). second - это просто отдельный указатель, который указывает на ту же память, на которую ссылается first. Более краткая версия кода будет:

#include <stdio.h>

void changeL (char *s)
{
    for (int i = 0; s[i]; i++)
        if (s[i] >= 'A' && s[i] <= 'Z')
            s[i] += 32;
}

int main (void)
{
    char first[] = "HELLO My Name is LoL";
    char *second = first;

    printf("%s\n", first);
    changeL(first);
    printf("%s\n", second);

    return 0;
}

( примечание: оба заголовочных файла в исходном коде не нужны, <stdio.h> является единственным обязательным заголовком)

Для иллюстрации second просто указывает на first:

Пример использования / Вывод

$./bin/chars
HELLO My Name is LoL
hello my name is lol
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...