不了解堆栈<字符*>行为C++



我卡住了两个小时,试图理解这个简单的c++测试程序中发生了什么,但仍然没有得到它。它应该只接收三个字符串作为输入,将它们插入堆栈中,最后打印同一堆栈中的所有元素。

#include <iostream>
#include <stack>
#include <cstring>
using namespace std;
int main(){

stack<char*> stk;

int stringLength;

for (int i=0; i<3; i++){
char new_element[200];
scanf("%s", new_element);
stringLength = strlen(new_element);
stk.push(new_element);
}

cout << "Stack content: ";
while(!stk.empty()){
cout << stk.top() << " ";
stk.pop();
}
cout << endl;
}

奇怪的是,最后的输出是相同的元素(添加的最后一个)打印3次,这对我来说没有意义。

例如,如果输入是:

John
Mary
Rick

则当前输出为

Rick
Rick
Rick

谁能帮我理解和修复这个?

char new_element[200];
// [...]
stk.push(new_element);

你在堆栈对象中压入同一个指针。

更糟糕的是,你正在推送系统堆栈指针,所以如果你要从函数外部使用堆栈,你会得到访问冲突(Linux中的段错误)和崩溃。在您的例子中,您没有从外部使用它们,因为堆栈对象也在堆栈上。

无论如何,有两个快速修复方法:

  1. 编写正确的代码:使用string,让编译器找出复制它们并在需要时清理它们。

  2. 不要写正确的代码:使用strdup来获得唯一的字符串指针。在某些时候,你可能想也可能不想释放它们,对于选择这条路线的人来说,这似乎总是可选的。

因为您已经将stk声明为std::stack<char*>,它的元素将是char*,或者指向char数据的指针(即地址)

)。所以,当执行stk.push(new_element);行时,你要做的是放置地址将您的(本地)字符数组放到堆栈中。从技术上讲,当您稍后pop该地址并打印它指向的字符串时,您正在执行未定义行为,因为指向的内存已经超出范围(它的'生命周期'只是第一个for循环的一次迭代)。

然而,您的系统只是在每次循环迭代中重用相同的地址/缓冲区,因此您的scanf("%s", new_element);行每次都替换该缓冲区的内容。然后,当您打印三个堆栈元素的内容时,每个元素都将是相同的地址,您只是显示该缓冲区的最后修改版本。

要解决这个问题,要么为你的'本地'变量使用std::string(然后push调用复制);或者,如果您坚持使用char*,则推送使用strdup()函数生成的副本的地址:

for (int i=0; i<3; i++){
char new_element[200];
scanf("%s", new_element);
stringLength = strlen(new_element);
stk.push(strdup(new_element)); // Allocate and copy the buffer before pushing
}

然后,在第二个循环中,不要忘记在完成后释放内存:

while(!stk.empty()){
cout << stk.top() << " ";
free(stk.top()); // Release the allocated memory before we lose the pointer
stk.pop();
}

strdup()free()函数在<stdlib.h>头文件中声明。

可以用string代替char*

#include <iostream>
#include <stack>
#include <cstring>
int main(){

stack<string> stk;

int stringLength;

for (int i=0; i<3; i++){
string new_element;
cin>>new_element;
cout<<new_element<<"n";
stk.push(new_element);
}

cout << "Stack content: ";
while(!stk.empty()){
cout << stk.top() << " ";
stk.pop();
}
cout << endl;
}

最新更新