Пара замечаний в дополнение к тому, что @ 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