【问题标题】:how to change single char in string array?如何更改字符串数组中的单个字符?
【发布时间】:2020-06-06 15:02:42
【问题描述】:

有这个:

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

char *up(char *);

int main() {
    char initstr[20];
    printf("enter string\n");
    fgets(initstr, 20, stdin);

    char *str = up(initstr);
    printf("%s\n", str);
}

char *up(char *in) {
    char *ret;
    for (ret = in;
         *in != '\n'; 
         *(ret++) = toupper(*(in++))
        );
    return ret;
}

运行如下:

$./a.out
enter string
abc

#only new line from `printf("%s\n",str);`

从调试器

Hardware watchpoint 3: in

Old value = 0x7fffffffdc20 "abc\n"
New value = 0x7fffffffdc21 "bc\n"

Hardware watchpoint 2: ret

Old value = 0x7fffffffdc20 "abc\n"
New value = 0x7fffffffdc21 "bc\n"

Hardware watchpoint 3: in

Old value = 0x7fffffffdc21 "bc\n"
New value = 0x7fffffffdc22 "c\n"

Hardware watchpoint 2: ret

Old value = 0x7fffffffdc21 "bc\n"
New value = 0x7fffffffdc22 "c\n"
...

我可以看到两个变量都在减少,但我想逐个字符地更改ret 内联。但在最后(循环之后),ret 减少为空,程序只会输出\n。那么如何在循环头中实现这一点呢?

编辑: 感谢下面的回答,考虑到我必须返回指针的第一个地址,我可以通过这个实现 loop_head-only 函数:

char *up(char *in){
    char *ret;
    size_t size=strlen(in);
    for(ret=in;
         *in!='\n'; 
         *(ret++)=toupper(*(in++))
        );
    return (ret-size+1);
}

【问题讨论】:

  • 您的 up 函数应该就地更改字符串,在这种情况下不需要返回任何内容,或者它应该返回字符串的修改副本,而原始字符串保持不变。你的返回一个指向原始字符串末尾的指针,这是没有意义的。为什么retin 在整个循环中都具有相同的值?

标签: c loops debugging gdb


【解决方案1】:

up 中的错误是您将 ret 一直递增到换行符 (\n) 并返回指向字符串中该字符的 ret。您应该返回一个指向初始字符的指针。

  • 使用索引编写此函数更简单。
  • 将所有逻辑打包到带有空主体的 for 子句中很难阅读并且容易出错。
  • 另请注意,字符串可能不包含换行符,因此在空终止符处停止会更安全,toupper() 不会更改换行符。
  • 最后,您不应将char 值传递给toupper(),因为此函数和&lt;ctype.h&gt; 中的所有函数仅针对unsigned char 类型的值和特殊的负值EOF 定义。在默认签名char 的平台上,字符串可能包含负的char 值,当传递给toupper() 时可能会导致未定义的行为。将它们转换为 (unsigned char) 以避免此问题。

这是修改后的版本:

#include <ctype.h>
#include <stdio.h>

char *up(char *s) {
    for (size_t i = 0; s[i] != '\0'; i++) {
         s[i] = toupper((unsigned char)s[i]);
    }
    return s;
}

int main() {
    char initstr[20];
    printf("enter string\n");
    if (fgets(initstr, sizeof initstr, stdin)) {
        char *str = up(initstr);
        printf("%s\n", str);
    }
    return 0;
}

【讨论】:

  • 哇,太可怕了。我的意思是 toupper 不能保证正确处理所有值。可以忽略 a 到 z 之外的所有内容。 +1 让我们知道。
  • @AugustKarlstrom:确实toupper() 接受int 参数,但并非所有int 值都是合法的,只有0UCHAR_MAX 之间的值是合法的,除了负值@987654342 @(通常是-1)。可怕的是,负的char 值可能会导致未定义的行为,或者可能与EOF 混淆(在许多平台上就像'\xff' == EOF)。这是 C 库中的根本不一致。如果可能的话,安全的方法是将编译器配置为默认使char 无符号。例如:gcc -funsigned-char
  • @chqrlie 我知道这是解决方案,但我仍然希望只通过循环头实现,没有主体(只是为了看看区别)。可以吗?
  • 但必须是ret-size+1?最后加一个?当我插入abc\n 时,然后在循环中ret 的地址为ret+4(abc 并在看到\n 时递增,但完成循环)。所以当我返回地址(ret-size+1)时,就相当于ret + 4 (by the loop) - 4 + 1 == ret+1,而不是ret
  • @autistic456:您的修复仍然很脆弱:字符串可能不包含'\n',输入流可能包含没有换行符的单行。此外,您不会检查 fgets() 是否成功,因此您会在空文件上获得未定义的行为。如果您坚持循环结构,请使用char *ret; for (ret = in; *in != '\0'; *in = toupper((unsigned char)*in), in++) ;
猜你喜欢
  • 2014-01-16
  • 1970-01-01
  • 1970-01-01
  • 2019-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多