不了解 C++ stack<char*> 行为

问题描述

我花了两个小时试图了解这个简单的 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;

}