0

我得到一个段。当我尝试从 char 类型中减去 32 时出现错误(尝试在 C 中不使用 tolower() 转换为小写。我已经完成了搜索相关 Q/A 线程的先决条件,但没有运气。我还尝试了 'a' - 'A'对于转换值“32”,将其转换为 (char*) 以及我能想到的任何其他内容。例如:

char* s1 = "Bob";

if (*s1 >= 97 && *s1 <= 122)
     *s1 -= 32;
}

有什么建议吗?

编辑:

按照下面的帮助后,我仍然得到错误。(对于此示例,我只是尝试将名称的第一个字母更改为小写。)这是我正在尝试的:

 char* s1 = "Bob";
 printf("\n %s before", s1);

 // below I call my string length function to get actual size instead of 100
 char* temp = malloc(100);   
 temp = s1;

 if (*temp >= 'A' && *temp <= 'Z'){
    *temp -= 32;
 }

 printf("\n%s after", temp);
 free(temp);

另外,为什么我需要为已经在内存中的字符串分配内存?

4

6 回答 6

7

你不能像那样改变文字字符串——它们(通常)在只读存储器中。您需要制作字符串文字的可写副本:

char* name = "Bob";
char* s1 = strdup(name);

...

free(s1);  // And you also need this to avoid a memory leak!
于 2013-11-11T16:15:39.700 回答
5

您的代码存在许多问题。

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',
    /* ... */
};

我会留给你填写其余的编写代码来使用它。

现在您可以看到tolowerandtoupper函数存在的原因——因此您不必处理所有这些东西(除了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中。

于 2013-11-11T16:42:37.860 回答
2

初始化 char 并使用 malloc 分配内存以存储所有字符串,然后使用 for 循环并将整个字符串转换为小写。

于 2013-11-11T16:19:46.067 回答
1

你需要

  1. 分配缓冲区
  2. 将字符串“Bob”复制到缓冲区
  3. 循环遍历字符串,随时编辑它。
于 2013-11-11T16:16:35.437 回答
0

这会失败,因为字符串文字通常存储在只读内存中。

最简单的解决方法是使用字面量来初始化一个数组,该数组将是可修改的(除非明确做出const所以不要这样做):

char s1[] = "Bob";

此外,硬编码 ASCII 是非常糟糕的形式,使用islower()tolower()函数<ctype.h>来使此代码正确。

于 2013-11-11T16:32:50.393 回答
-1
char *s1 = "Bob";

正在创建一个指向字符串常量的指针。这意味着该字符串"Bob"将位于内存的只读部分中,而您只有一个指向它的指针。您可以将字符串用作只读。您无法对其进行更改。
例子:

s1[0] = 'b';

是自找麻烦。

要进行更改,s1您应该为其分配内存

s1 = malloc(10); //or what you want

s1现在可以轻松完成更改。

于 2013-11-11T16:17:07.797 回答