【问题标题】:Simple C syntax for subtracting from char type, lowercase用于从 char 类型中减去的简单 C 语法,小写
【发布时间】:2013-11-23 12:35:10
【问题描述】:

我得到一个段。当我尝试从 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);

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

【问题讨论】:

  • 除了修改字符串文字这一事实外,不要使用“幻数”。当你的意思是'a''z'时,不要写97122
  • 好吧,如果与数字相比,我认为 C 将字符视为它们的 ASCII 值
  • 确实如此。但是你应该使用实际的字符,这样它就更易读了。
  • @user2827214:确实如此,但 Keith 的观点是,如果您使用 'a' 而不是 97,您的代码更具可读性。
  • 我猜不是每个人都会同意,但我更喜欢写s1[0] 而不是*s1 来访问第一个元素。

标签: c syntax char lowercase


【解决方案1】:

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

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

...

free(s1);  // And you also need this to avoid a memory leak!

【讨论】:

    【解决方案2】:

    您的代码存在许多问题。

    char* s1 = "Bob";
    

    字符串文字创建一个只读数组char;这个数组是静态的,意味着它在程序的整个生命周期中都存在。由于历史原因,它不是const,所以如果你尝试修改它,编译器不一定会警告你,但你应该小心避免这样做。

    s1 指向该数组的第一个字符。您不得修改*s1。为了安全起见,您应该将指针声明为const

    const char *s1 = "Bob";
    

    如果你想要一个可修改的字符数组,你可以这样创建:

    char s1[] = "Bob";
    

    现在让我们看看剩下的代码:

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

    97122'a''z' 的数字 ASCII 代码。 32 是小写字母和对应的大写字母之间的区别——同样是 ASCII。

    C 语言不保证字符以 ASCII 或任何与其兼容的字符集表示。例如在 IBM 大型机上,字符用 EBCDIC 表示,其中字母的代码不连续(有间隙),对应的小写字母和大写字母的差是 64,而不是 32。

    EBCDIC 系统如今很少见,但即便如此,可移植代码往往比不可移植代码更清晰,即使除了代码是否适用于所有系统的任何实际问题。

    我相信您知道,最好的方法是使用tolower 函数:

    *s1 = tolower((unsigned char)*s1);
    

    请注意unsigned char 的演员表。由于历史原因,在 &lt;ctype.h&gt; 中声明的 to*()is*() 函数表现异常。它们不适用于char 参数;相反,它们处理int 范围内的unsigned char 参数。 (他们也接受EOF,通常是-1)。如果普通的char 被签名,那么传递一个恰好为负的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',
        /* ... */
    };
    

    剩下的就交给你来写代码来使用吧。

    现在您可以看到为什么存在tolowertoupper 函数——因此您不必处理所有这些东西(除了您需要的奇怪的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

    【讨论】:

    • 很好的解释,我不知道你不能像我一样声明一个 char 数组。
    • 很好的解释,尤其是幻数查表部分。
    • 代码中的问题非常多,您可能希望将此答案分成几部分并标记每个部分(如“##”或“###”标记)。
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      你需要

      1. 分配缓冲区
      2. 将字符串“Bob”复制到缓冲区
      3. 遍历字符串,随时编辑。

      【讨论】:

        【解决方案5】:

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

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

        char s1[] = "Bob";
        

        此外,硬编码 ASCII 是一种非常糟糕的形式,请使用 &lt;ctype.h&gt; 中的 islower()tolower() 函数来使此代码正确。

        【讨论】:

        • 这里的重点是不要使用像 islower() 这样的 c-lib 函数。
        【解决方案6】:
        char *s1 = "Bob";
        

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

        s1[0] = 'b';
        

        自找麻烦。

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

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

        现在可以轻松更改s1

        【讨论】:

        • 为什么是 10?那 14 是多少?
        • @RichieHindle;编辑了我的答案。
        • @Maxime;这取决于OP的选择。他可能会选择44010 还不错。
        • @hacks 您已编辑答案以删除 14。无论如何,您应该解释为什么它不起作用,而不是提出不同的建议。
        猜你喜欢
        • 1970-01-01
        • 2022-06-11
        • 1970-01-01
        • 2020-08-12
        • 1970-01-01
        • 2023-02-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多