为什么在C++中有些字符无法编辑?

7
我正在尝试在C++中编写自己的strcat函数,但是它有一些问题。
我的输入是两个字符c和a,我的函数将返回一个指向连接了c和a的字符的字符指针。

例如,
输入:'abc' 'xyz'
期望输出:'xyzabc'
我的函数的输出:'xyza@▲∩'

我的函数返回了一些与我的输入不同的特殊字符。

我调试了我的函数并发现:

i=0时,destination[3] = source[0] = 'a'。 但是当i=1时,destination[8] = source[1] = 'b'。 而当i=2时,destination[9] = source[2] = 'c'。 最后,destination[10] = '\0'
#include<iostream>
#include<string.h>
using namespace std;

char* mystrcat ( char * destination, const char *source){
    for (int i=0; i<strlen(source); i++) {
        destination[strlen(destination)+i] = source[i];
    }
    destination[strlen(destination)+strlen(source)]='\0';
    return destination;
}

int main() {
    char c[100];
    cin.getline(c, 99);
    char a[100];
    cin.getline(a,99);

    mystrcat(a,c);
    cout<<a;
    return 0;
}

2
在将另一个字符串附加到目标字符串后,strlen(destination)的值是多少? - undefined
1
如果你正在编写自己的strcat函数,它应该接受一个名为destinationSize的参数,该参数表示目标缓冲区中可以存储的最大字符数,以防止缓冲区溢出。一目了然:如果a是99个字符,c是99个字符,那么哪个字符数组有足够的空间来存储198个字符的结果? - undefined
2
@Wyck 这将是重新实现 strncat 而不是 strcat,后者会快乐地溢出缓冲区。 - undefined
@Chris 好吧,关于strcat的幸福感,我实际上想让提问者考虑一下他们将如何将两辆车停在他们的单车库中,这只是一个比喻。如果声明为char a[200],我就不会提到它,因为根据代码的其他细节,strcat将保证有足够的空间。需要发生三件事之一:要么使用strncat,要么将目标缓冲区变大,要么将getline字符的总和限制在目标缓冲区的大小之内(加上一个NUL字符)。 - undefined
我没有看到任何动态内存分配,strcat将会有。如果你想以稍微现代一点的方式实现它,让你的字符串连接函数至少返回一个std::unique_ptr(在当前的C++中,不再推荐使用裸指针)。 - undefined
显示剩余2条评论
2个回答

5

strlen 返回从指针到第一个遇到的 \0 的长度。在这里,在循环期间,您会覆盖 destination 指针中的此字符,因此后续对 strlen 的调用将返回到内存中某个随机点上保存此字符的长度。

一个简单的修复方法是在开始修改字符串之前提取 strlen 的结果:

char* mystrcat (char *destination, const char *source) {
    int destLen = strlen(destination);
    int srcLen = strlen(source);
    for (int i = 0; i < srcLen; i++) {
        destination[destLen + i] = source[i];
    }
    destination[destLen + srcLen] = '\0';
    return destination;
}

由于在第一次迭代之后,destination 不再是一个有效的以 null 结尾的字符串,所以在后续迭代中使用 strlen(destination) 将导致未定义行为 - undefined
@Chris 但是你在循环中哪里看到了 strlen(destination) - undefined
一个更简单的实现根本不需要使用strlen。只需将destination向前移动,直到达到其'\0',然后从source复制,直到达到其'\0' - undefined
1
@CGi03 Chris显然是在指的是原帖的代码,而不是这个回答的代码。这条评论应该发在问题上,而不是这个回答上。 - undefined
1
抱歉造成困惑。我的意思是建议在你的回答中加入“未定义行为”这个词。你的回答已经足够好,我放弃了自己的回答。恭喜你比我先完成了! - undefined
2
@Chris,你说得很有道理。"未定义行为"的重要性在于它比仅仅返回一个随机长度更具灵活性。如果你幸运的话,它可能会导致程序崩溃。如果你不幸的话,你可能会发现鼻子里飞出恶魔 - undefined

-1
以下是代码的正确实现。
#include<bits/stdc++.h>
using namespace std;
void mystrcat ( char* destination, const char *source){
    int p;
    for(p=0; destination[p] != '\0'; p++);//pointing to the index of the last 
    character of x

    for(int q=0; source[q] != '\0'; q++,p++)
    {
    destination[p]=source[q];
    }
    destination[p]='\0';
    }
int main() {
   char c[100];
   cin.getline(c, 99);
   char a[100];
   cin.getline(a,99);
   mystrcat(a,c);
   cout<<a;
   return 0;
 }

由于源代码的长度会在您的代码中更新,因此它会输出特殊符号。

1
“特殊符号”是未定义行为的直接结果。它们绝对不是被保证的。 - undefined
3
https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice , https://stackoverflow.com/questions/31816095/why-should-i-not-include-bits-stdc-h ,并且您应该解释您对代码所做的更改以及为什么 - undefined

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