我试图用C ++编写以下代码以反转字符串。由于某些原因,当字符串为奇数长度时,会给出错误的输出。
#include <iostream>
using namespace std;
void swapWithOutThirdVar(char &a, char &b) {
a = a + b;
b = a - b;
a = a - b;
}
void reverse(char string[]) {
int length = 0;
while (string[length] != '\0') {
length++;
}
int left = 0, right = length - 1;
while (left <= right) {
swapWithOutThirdVar(string[left], string[right]);
left++;
right--;
}
}
int main() {
char string[25];
cin>>string;
reverse(string);
cout<<string<<endl;
}
例如,如果我在控制台中输入
lappy
,则后续输出为yp
。我是编程新手,所以无论潜在错误有多愚蠢,请对我友善。
最佳答案
有很多方法可以解决代码中的错误,但是代码中的主干错误在于您自己定义的交换函数。
void swapWithOutThirdVar(char &a, char &b) {
a = a + b;
b = a - b;
a = a - b;
}
我知道这是一个非常著名的函数,用于不使用第三个变量就交换两个变量。但是它有两个问题:
对于
a
和b
的某些值,操作a + b
可能导致溢出。(这里就是这种情况)如果遇到将完全相同的变量传递给函数的情况,交换将最终变得不稳定。原因如下:
假设您要将变量
char c
传递给函数的两个参数。由于在函数中参数是通过引用传递的,因此虚拟变量a
和b
实际上是相同的变量,也就是说,它们是相同的c
的别名。简而言之,a
和b
表示相同的变量c
。因此,现在当您执行
a = a + b
时,该操作实际上会导致c = c + c
,这意味着c
的(ASCII)值在该语句执行结束时已加倍。当第二条语句生效时,乐趣就来了。
b = a - b
生成c = c - c
,将0
分配给c
。那是你做错的地方,孩子。第三条语句对该过程没有任何好处。
a = a - b
导致c = c - c
,这仍然使c
保持0
。因此,您的变量被分配了值
0
,而不是被交换(本身?)。现在,您可能想知道到底要在哪里交换相同的变量,对吗?
当您使用奇数长度的字符串时,请注意在第二个
while
循环的最后一次迭代中,left
和right
的值相同。在这种情况下,left
和right
对于string
具有相同的索引,因此string[left]
和string[right]
是相同的变量。相同的变量将在该迭代中传递给交换函数。现在,正如我之前所说:将相同的变量传递给swap函数将最终将
0
传递给已传递给它的变量。对于您的示例案例,这是上次迭代结束时string
的样子:['y', 'p' '\0', 'a', 'l']
在C / C ++中,
null
(0
)标记字符串的结尾。因此,奇怪的输出(yp
)是合理的。在偶数长度的字符串中,在第二个
left
循环的任何迭代中,right
都不会等于while
。这就是为什么永远不会将相同的变量传递给交换函数,这就是为什么reverse
函数的工作原理与从未传递给相同的变量一样好。因此,首先,您需要注意相同变量的情况。如果
a
和b
是相同的变量,则只需从函数返回即可,因为从技术上讲,将变量与自身交换是毫无意义的。利用以下事实:如果两个变量基本上是对同一变量的引用,则它们必须具有相同的地址。void swapWithOutThirdVar(char &a, char &b) {
if (&a == &b)
return;
a = a + b;
b = a - b;
a = a - b;
}
但这不能解决溢出问题。因此,您需要做其他事情。
假设这是一个编程分配问题,您需要自己实现所有操作,那么您可以进行XOR交换,该交换使用按位XOR交换两个变量。按照交换函数的名称,我认为您知道老式的三变量交换技术,并且使用第三个变量进行交换也是分配的限制。
对两个数字进行异或运算不会导致溢出,因此该问题已得到解决。尽管XOR方法不能独立解析相同变量的情况,并最终在第一条语句本身中将变量
0
交给了它,所以您需要保留地址相等性检查部分:void swapWithOutThirdVar(char &a, char &b) {
if (&a == &b)
return;
a ^= b;
b ^= a;
a ^= b;
}
另外,您可以保持交换功能不变,并稍微修改第二个
while
循环的条件以解决错误:对于奇数长度的字符串,反转时中间字符的位置保持不变。想一想:当
left
和right
(都)都指向字符串的中间字符时,出现left
-等于-right
的情况。因此,循环只需要运行,只要left < right
保持true
。对于偶数长度的字符串,left
永远不会等于right
。当while
和left
是字符串的两个相邻中间元素的索引时,right
循环就结束了。因此,left < right
修改不会损害均匀长度的情况。因此,相应的修复方法是:void reverse(char string[]) {
int length = 0;
while (string[length] != '\0') {
length++;
}
int left = 0, right = length - 1;
while (left < right) {
swapWithOutThirdVar(string[left], string[right]);
left++;
right--;
}
}
到此结束了错误的解释和纠正部分。但是,如果这不是针对您必须自己实现所有事情的编程任务,也就是说,您没有限制,则应该考虑以下内容:
从代码中的
using namespace std;
来看,它似乎适用于C ++ 0x或更高版本。因此,您应该考虑以下事项:从C ++ 0x开始,您已经具有预定义的交换功能(
std :: swap
)。您可以改用它。传递给它的溢出和同变量在这里不是问题。请参见here。您在程序中使用了C样式的字符串。不再建议使用C型字符串。此外,您正在使用C ++ 0x或更高版本。因此,您应该改用
std :: string
。请参见here。您可以使用
reverse
标头中的algorithm
函数。请参见here。
关于c++ - 字符串反向功能不适用于奇数长度的字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59128881/