在C和C++中将C字符串转换为大写

21

在我编写C ++中的大写转换函数时,我注意到在C中没有得到预期的输出。

C ++函数

#include <iostream>
#include <cctype>
#include <cstdio>

void strupp(char* beg)
{
    while (*beg++ = std::toupper(*beg));
}

int main(int charc, char* argv[])
{
    char a[] = "foobar";
    strupp(a);
    printf("%s\n", a);
    return 0;
}

输出结果如预期:

FOOBAR


C函数

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

void strupp(char* beg)
{
    while (*beg++ = toupper(*beg));
}

int main(int charc, char* argv[])
{
    char a[] = "foobar";
    strupp(a);
    printf("%s\n", a);
    return 0;
}

输出结果是期望的结果,只缺少第一个字符。

OOBAR

请问有人知道在C语言编译时为什么结果会被截断吗?


5
如果你真的想用C++完成这件事:std::transform(a, a + strlen(a), a, std::toupper);。该代码会将字符串a中的所有字符转换为大写字母。 - PaulMcKenzie
你能解释一下为什么你期望这个将字符串转换为大写吗?具体来说,为什么你期望=的右边在左边之前被评估? - David Schwartz
我感谢所有提供反馈和有价值信息的人们。 - Alex Koukoulas
1
@Schullz 我坚信从错误中学习是避免再犯同样错误的最有效方式。因此,由于这个问题仅源于实验而不是团队项目环境,我认为写这段代码片段并没有错。 - Alex Koukoulas
1
@MillieSmith 在这种情况下,将哪个项目大写是否重要? 无论如何,大多数(如果不是全部)经典的C ++字符串变异方法都使用std :: transform - PaulMcKenzie
显示剩余5条评论
4个回答

30

问题在于没有序列点

while (*beg++ = toupper(*beg));

因此,我们有未定义的行为。在这种情况下,编译器所做的是在C中在评估beg ++之前,在C++中则是在评估toupper(* beg)之前。


这是否意味着经典的C字符串复制一行代码 while(*s++ = *t++) ; 存在未定义的行为? - Mark H
3
@markh 不可以,因为那是两个不同的变量。这与 while (*s++ = *s++) 是同义的。 - NathanOliver
@SteveJessop 实际上,即使s == twhile(*s++ = *t++);也有定义行为。 strcpy在重叠区域中是未定义的原因是strcpy不一定使用while(*s++ = *t++);实现。 - user253751
@immibis:说得好,我同意它是有定义的。然而,即使strcpy使用那个循环实现,对于一个重叠的“方向”,它仍然具有未定义的行为。如果目标大于源并且它们重叠,则终止的nul在读取之前被覆盖,最终缓冲区会溢出!禁止两个“方向”中的重叠是为了可能的其他实现。 - Steve Jessop

15
while (*beg++ = std::toupper(*beg));

会导致未定义的行为。

*beg++ 是在 std::toupper(*beg) 之前还是之后被顺序执行是未指定的。

简单的解决方法是使用:

while (*beg = std::toupper(*beg))
   ++beg;

10

这条线

while (*beg++ = toupper(*beg));

在实体被两次使用时,这会对其产生副作用。您无法知道beg++是在*toupper(内部)之前还是之后执行的。你很幸运,因为这两种实现都显示了这两种行为,我相信C++也是如此。(但是,对于c++11有一些规则变化,我不确定-仍然,这是不好的风格。)

只需将beg++移出条件即可:

while (*beg = toupper(*beg)) beg++;

3
关于上面的答案,'f'从未在函数中传递,您可以尝试使用以下代码:

与此相关的问题,'f'从未在函数内部传递,您应该尝试使用以下代码:

     while ((*beg = (char) toupper(*beg))) beg++;

转换为 char 很重要,因为 int 类型的值可能不适合接收器类型 char。 - vishal
1
那个转换没有任何作用,因为它不会改变本来就会发生的事情。 - Flexo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接