我一直在尝试使用指针将大写字母更改为小写字母,但是我一直遇到分段错误。这是我的源代码:
#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,我发现seg错误位于“ upper [i] + = 32;”中。我不明白为什么出现段错误。
答案 0 :(得分:5)
“ HELLO My Name is LoL”是常量存储器。您无法更改。但是,您将指向该内存的指针(第一个)传递给试图更改它的函数。因此,您遇到了细分错误。您应该将此字符串复制到内存Butffer。像
char buffer[] = "HELLO My Name is LoL";
然后传递缓冲区以更改L
答案 1 :(得分:3)
除了@Alex在他的答案中正确指出的内容外,还有一些注意事项。首先
char *changeL(char *s);
char *changeL(char *s)
{
....
}
如果函数在下面一行,则在函数之前不需要原型。原型用于通知其下面的代码该原型描述的功能存在并在其他地方定义。如果您在原型的正下方定义函数,则会使原型不相关。
第二个是Alex的回答,在绝大多数系统上,是 String Literal ,例如"Something Here"
中的char *s = "Something Here";
是不可变的,并且驻留在只读存储器中,任何尝试修改字符串文字的尝试通常都会导致SegFault。
相反,您需要创建一个可以修改的字符数组,例如
char first[] = "HELLO My Name is LoL";
或在C99 +中,您可以使用 Compound Literal 来初始化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