您的代码存在许多问题。
char* s1 = "Bob";
字符串文字创建一个只读数组char
; 这个数组是静态的,意味着它在程序的整个生命周期中都存在。由于历史原因,它不是const
,因此如果您尝试修改它,编译器不一定会警告您,但您应该小心避免这样做。
s1
指向该数组的第一个字符。您不得修改*s1
. 为了安全起见,您应该将指针声明为const
:
const char *s1 = "Bob";
如果你想要一个可修改的字符数组,你可以像这样创建它:
char s1[] = "Bob";
现在让我们看看剩下的代码:
if (*s1 >= 97 && *s1 <= 122)
*s1 -= 32;
}
97
和是和122
的数字 ASCII 代码。是小写字母和相应的大写字母之间的区别——同样,在 ASCII 中。'a'
'z'
32
C 语言不保证字符以 ASCII 或任何与其兼容的字符集表示。例如,在 IBM 大型机上,字符以 EBCDIC 表示,其中字母的代码不连续(有间隙),对应的小写字母和大写字母之间的差异是 64,而不是 32。
如今 EBCDIC 系统很少见,但即便如此,可移植代码往往比不可移植代码更清晰,即使不考虑代码是否适用于所有系统的任何实际问题。
我相信你知道,最好的方法是使用这个tolower
函数:
*s1 = tolower((unsigned char)*s1);
注意转换为unsigned char
. 由于历史原因,在中声明的to*()
andis*()
函数的行为很奇怪。<ctype.h>
他们不处理char
争论;相反,它们处理的int
参数在unsigned char
. (他们也接受EOF
,通常是-1
)。如果 plainchar
是有符号的,那么传递一个char
恰好为负的值会导致未定义的行为。是的,这很烦人。
但是你说你不想用tolower
. (这很好;学习自己做这样的事情是一个很好的练习。)
如果您愿意假设大写字母是连续的,而小写字母是连续的,那么您可以执行以下操作:
if (*s1 >= 'a' && *s1 <= 'z') {
*s1 -= 'a' - 'A';
}
这仍然不能移植到非 ASCII 系统,但如果你没有记住 ASCII 表,它会更容易阅读。
这也让你更清楚地知道你的逻辑是倒退的。您说要转换为小写,但您的代码从小写转换为大写。
或者您可以使用将小写字母映射到大写字母的查找表:
char to_lower[CHAR_MAX] = { 0 }; /* sets all elements to 0 */
to_lower['A'] = 'a';
to_lower['B'] = 'b';
/* ... */
to_lower['Z'] = 'z';
或者,如果您的编译器支持复合文字:
const char to_lower[CHAR_MAX] = {
['A'] = 'a',
['B'] = 'b',
/* ... */
};
我会留给你填写其余的编写代码来使用它。
现在您可以看到tolower
andtoupper
函数存在的原因——因此您不必处理所有这些东西(除了unsigned char
您需要的奇怪类型转换)。
更新 :
针对您问题的新部分:
char* temp = malloc(100);
temp = s1;
该分配temp = s1;
不会复制分配的字符串;它只是复制指针。temp
指向 100 字节的已分配空间,但随后您temp
指向(只读)字符串文字,并且您丢失了对已分配空间的任何引用,从而造成内存泄漏。
您不能在 C 中分配字符串或数组。要复制字符串,请使用以下strcpy()
函数:
char *temp = malloc(100);
if (temp == NULL) { /* Don't assume the allocation was successful! */
fprintf(stderr, "malloc failed\n");
exit(EXIT_FAILURE);
}
strcpy(temp, s1);
另外,为什么我需要为已经在内存中的字符串分配内存?
它在内存中,但它是不允许修改的内存。如果要修改它,则需要将其复制到可修改的位置。或者,正如我上面建议的那样,您可以首先将其放入读/写内存中:
char s[] = "Bob";
该初始化将字符串复制到数组s
中。