我正在尝试学习C编程,并花了一些时间练习指针今天早上,通过编写一个小函数来将字符串中的小写字符替换为大写字母。这就是我得到的:
#include <stdio.h>
#include <string.h>
char *to_upper(char *src);
int main(void) {
char *a = "hello world";
printf("String at %p is \"%s\"\n", a, a);
printf("Uppercase becomes \"%s\"\n", to_upper(a));
printf("Uppercase becomes \"%s\"\n", to_upper(a));
return 0;
}
char *to_upper(char *src) {
char *dest;
int i;
for (i=0;i<strlen(src);i++) {
if ( 71 < *(src + i) && 123 > *(src + i)){
*(dest+i) = *(src + i) ^ 32;
} else {
*(dest+i) = *(src + i);
}
}
return dest;
}
这样可以正常运行并准确打印它应该包含的内容(包括重复“HELLO WORLD”行),但之后以Segmentation故障结束。我无法理解的是,该功能正在清楚地编译,执行和返回成功,并且main中的流程继续。分段错误也发生在return 0
?
答案 0 :(得分:18)
dest
在您的to_upper()
函数中未初始化。所以,当你这样做时,你会覆盖一些随机的内存部分,显然这会导致程序在你尝试从main()
返回时崩溃。
如果您想要修改该值,请初始化dest
:
char *dest = src;
如果您想复制该值,请尝试:
char *dest = strdup(src);
如果你这样做,你需要确保有人在free()
返回的指针上调用to_upper()
(除非你不关心内存泄漏)。
答案 1 :(得分:2)
正如其他人所指出的那样,问题是dest
尚未初始化并且指向包含重要内容的随机位置。您有多种选择如何处理:
dest
缓冲区并返回指针值,调用者负责释放该值; dest
指向src
并修改适当的值(在这种情况下,您必须从{{1}更改main()中a
的声明} char *a = "hello world";
,否则你将试图修改字符串文字的内容,这是未定义的;)选项1 - 动态分配目标缓冲区:
char a[] = "hello world";
选项2 - 让dest指向src并修改字符串:
char *to_upper(char *src)
{
char *dest = malloc(strlen(src) + 1);
...
}
选项3 - 让main()将目标缓冲区作为参数传递:
int main(void)
{
char a[] = "hello world";
...
}
char *to_upper(char *src)
{
char *dest = src;
...
}
在三者中,我更喜欢第三种选择;你没有修改输入(所以无论a是char数组还是指向字符串文字的指针都无关紧要)你并没有在函数之间分配内存管理职责(即main()完全负责分配和释放目标缓冲区)。
我意识到您正在尝试熟悉指针的工作方式以及其他一些低级细节,但请记住int main(void)
{
char *a = "hello world";
char *b = malloc(strlen(a) + 1); // or char b[12];
...
printf("Uppercase becomes %s\n", to_upper(a,b));
...
free(b); // omit if b is statically allocated
return 0;
}
char *to_upper(char *src, char *dest)
{
...
return dest;
}
比a[i]
更容易阅读和遵循。此外,标准库中有许多函数,例如*(a+i)
和islower()
,它们不依赖于特定的编码(例如ASCII):
toupper()
答案 2 :(得分:2)
正如其他人所说,你的问题不是为dest
分配足够的空间。你的代码还有另一个更微妙的问题。
要转换为大写,您要测试一个给定的char
以查看它是否位于71和123之间,如果是,则将该值与32相乘。这假定为字符的ASCII编码。 ASCII是使用最广泛的编码,但它并不是唯一的编码。
最好编写适用于所有类型编码的代码。如果我们确定'a'
,'b'
,...,'z'
和'A'
,'B'
,...,'Z'
,是连续的,然后我们可以计算从小写字母到大写字母的偏移量并使用它来改变大小写:
/* WARNING: WRONG CODE */
if (c >= 'a' && c <= 'z') c = c + 'A' - 'a';
但不幸的是,C标准没有给出这样的保证。事实上,EBCDIC编码就是一个例子。
因此,要转换为大写,您可以采用简单的方法:
#include <ctype.h>
int d = toupper(c);
或者,滚动你自己:
/* Untested, modifies it in-place */
char *to_upper(char *src)
{
static const char *lower = "abcdefghijklmnopqrstuvwxyz";
static const char *upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static size_t n = strlen(lower);
size_t i;
size_t m = strlen(src);
for (i=0; i < m; ++i) {
char *tmp;
while ((tmp = strchr(lower, src[i])) != NULL) {
src[i] = upper[tmp-lower];
}
}
}
toupper()
的优点是它检查当前语言环境以将字符转换为大写。例如,这可能会使æ到Æ,这通常是正确的事情。 注意:我自己只使用英文和印地文字符,所以我对我的特定例子可能是错的!
答案 3 :(得分:1)
正如其他人所说,你的问题是char * dest未初始化。您可以像Greg Hewgill建议的那样修改src的内存,或者您可以使用malloc来保留一些:
char *dest = (char *)malloc(strlen(src) + 1);
请注意,Greg建议使用strdup执行对malloc的调用。 '+ 1'是为空终止符'0'保留空间,你也应该从src复制到dest。 (你当前的例子只有strlen,它不包括null终止符。)我可以建议你在循环之后添加这样的一行吗?
*(dest + i) = 0;
这将正确终止字符串。请注意,这仅适用于您选择进入malloc路线的情况。修改内存或使用strdup将为您解决此问题。我只是指出它,因为你提到你正在努力学习。
希望这有帮助。