在c++中使用变量作为系统()输入而不使用c_str()

Use variable as system() input WITHOUT c_str() in C++

本文关键字:输入 str 系统 c++ 变量      更新时间:2023-10-16

这不是类似"为什么简单的system(variable)不起作用"的重复

解决方案是将字符串存储到可由c_str()转换的变量中,然后调用:system(variable.c_str())

然而,我正在寻找一种不用c_str()直接调用的方法。

所以我试过类似的东西

class systemRunner{
    private:
        stringstream prepareStream;
    public:
        void setProgram( string s ){
            prepareStream.str(""); // empty stream
            prepareStream.clear(); // reset stream - !IMPORTANT!
            prepareStream << """ << s << """;
        }
        void appendParam( string s ){ this->prepareStream << " " << s; }
        void appendParam( int i ){    this->prepareStream- << " " << i; }
        const char* getSystemRunCString(){
            //const std::string s = ;
            return this->prepareStream.str().c_str();
        }
};

然后人们会认为这将是enaugh:

system ( systemRunner->getSystemRunCString() )

但它失败了。编译很好,但当系统()像这样被调用时,系统会说它找不到指定的路径。

然而,当我恢复它并在直接系统调用中使用c_str()时,例如:

string tmp = (string)systemRunner->getSystemRunCString();
system( tmp.c_str() );

这很好用

人们会期望,如果我创建一个返回与CCD_ 5相同的东西的方法,
const char*,我会得到相同的结果,但我没有得到它…

我甚至试着不把两个输入都放在system()中,而是放在文件中——相同的结果,这样它就存储了相同的信息。。。

我是不是错过了什么?这可能吗?


PS:我说的是在Windows7控制台应用程序中使用system()

EDIT:好吧,@ravi说得对是什么导致了这个特定的例子失败,但标题中主要问题的答案是什么?是否可以在不直接调用其中的c_str()的情况下调用系统(变量)?:)

return this->prepareStream.str().c_str();

将返回const char*。该指针将是指向字符串类使用的内部字符指针的指针。通过此操作,您可以访问字符串的内部数据,但由于它是常量指针,您将无法修改其内容。

这里发生的情况是,在执行该函数后,字符串(您仍然持有其内部数据)超出了范围。所以,您只是在处理其字符串超出范围的句柄。

你永远不应该依赖这个。

@ravi已经完美地解释了代码失败的原因,因为str()返回的值超出了范围,但由于它的注释提到了它仍然不清楚如何解决实际问题,这里有一个可能的答案:

class systemRunner{
    private:
        string systemCommand;
    public:
        void setProgram( string s ){
            systemCommand.clear();
            systemCommand.append(""").append(s).append(""");
        }
        void appendParam( string s ){ systemCommand.append(" ").append(s); }
        void appendParam( int i ){
            // VS 2013:
            // systemCommand.append(" ").append(to_string(i)));
            // VS 2010:
            // systemCommand.append(" ").append(to_string(static_cast<long long>(i))); 
            // General stringstream conversion
            stringstream argumentStream;
            argumentStream << i;
            systemCommand.append(" ").append(argumentStream.str());
        }
        const char* getSystemRunCString(){
            return systemCommand.c_str();
        }
};

使用string而不是stringstream,这样只要systemRunner的实例没有被破坏,c_str()返回的指针就会有效。此外,对于示例中的这种简单格式,您不需要stringstream,只需使用append()to_string()方法即可。

只要systemRunner本身不是临时对象,就可以执行以下操作:

class systemRunner {
    private:
        stringstream prepareStream;
        string commandLine;
    public:
        //...
        const char* getSystemRunCString() {
            commandLine = prepareStream.str();
            return commandLine.c_str();
        }
};

通过这种方式,您返回的指针不是指向临时字符串,而是指向与systemRunner对象具有相同生存期的字符串。

不过,请注意,getSystemRunCString()应该仅用作"临时"字符串的提供者,千万不要像那样使用它

const char* cmd1 = runner.getSystemRunCString();
runner.setProgram(...);
const char* cmd2 = runner.getSystemRunCString();
// cmd1 is now invalid.
system(cmd1); // UB

这也适用于鲁道夫·邦杜利斯的回答

我将分解操作prepareStream.str().c_str();,以精确地显示发生了什么。

让我们改为写:

const char* getSystemRunCString(){
    const string s = prepareStream.str();
    const char * c = s.c_str();
    printf("%s - %pn", c, c);
    return c;
}

它运行良好,并显示准备命令行

但如果我使用,在返回方法上

const char *c = systemRunner->getSystemRunCString();
printf("%s - %pn", c, c);

指针地址相同,但字符串本身已经不见了。

解释:

在后台,prepareStream.str().c_str();创建一个本地字符串,并在其内容上获得一个const char *。但是,由于字符串没有保存在任何位置,它超出了范围,其内容也随之消失。

因此,您的问题不在system级别,而是与以下事实有关:对于stringstream ssss.str().c_str()是不正确的,因为您在指令末尾获得了一个指向超出范围的字符串的指针。

如果您编写string s = prepareStream.str().c_str();,它会起作用,因为内部字符串只在指令结束时超出范围,并且在指令期间,值会复制到新字符串。