当我调用不相关的方法时,c-strings 的值会发生变化

Posted

技术标签:

【中文标题】当我调用不相关的方法时,c-strings 的值会发生变化【英文标题】:The value of c-strings change when I call an unrelated method 【发布时间】:2016-03-11 19:15:11 【问题描述】:

我正在处理一个头文件,该文件定义了一个命名空间,其中定义了一些 c 字符串。

namespace env 
    const char* C_NAME;
    const char* SYS_DRIVE;
    const char* PROCESSOR;
    const char* PROCESSOR_ARCHITECTURE;
    const char* OSNAME;

我的主要功能是这样的:

int main(int argc, char* argv[], char* env[]) 
    initialize_environment_vars(env);

    cout << "C_NAME\t\t\t" << env::C_NAME << endl;
    /*...*/

    return 0;

我的问题是我在initialize_environment_vars() 中初始化的字符串没有我想要的值。

void initialize_environment_vars(char* env[]) 
    int id = PRIVATE::findEntry(env, "COMPUTERNAME");
    env::C_NAME = (str::getAfter(env[id], "=")).c_str(); // getAfter() returns a string
    //std::cout << env::C_NAME << std::endl; //Right value!!!

    id = PRIVATE::findEntry(env, "SystemDrive");
    std::cout << env::C_NAME; //Value at env[id]

    /*Here the other constants are initialized in the same way.*/

我发现在函数 initialize_environment_vars() 中变量的值是正确的,直到我调用函数 findEntry() 来查找另一个条目。

int PRIVATE::findEntry(const char* const arr[], std::string toFind) 
    bool found = false;
    int i = 0;
    std::string actual;
    while(arr[i] && !found) 
        actual = arr[i];
        if(str::contains(actual, toFind)) 
            found = true;
            break;
        
        i++;
    

    if(found)
        return i;
    else  /*Error message and exit program*/ 

看了string::c_str query这个帖子,我也觉得我在initialize_environment_vars()中使用.c_str()是错误的,因为调用.c_str()后返回的字符串会被销毁,但是这好像不是确实如此,因为env::C_NAMEmain() 中有效。

因此我有两个问题:

    为什么我的PRIVATE::findEntry(const char* const [], std::string) 函数会像我上面使用的那样更改env::C_NAME 的值,即使它只返回一个int 并且没有修改数组或条目? 为什么env::C_NAME 在'main()' 中仍然有效? str::getAfter(const std::string&amp;, std::string)返回的字符串的析构函数被调用后不应该失效吗? (已回答)

【问题讨论】:

"我还认为我在initialize_environment_vars()中使用.c_str()是错误的,因为调用.c_str()后返回的字符串会被销毁 “ 答对了! “但这似乎不是这样,因为 env::C_NAME 在 main() 中是有效的。” 嗯? @David Schwartz 我刚刚重新阅读了另一篇文章的答案并理解了我第二个问题的答案。嗯?:我的意思是,在main() 我得到了我期望的行为,所以我认为它总是可以正常工作。但现在我知道这是错误的。 没有理由你应该期待任何特定的行为,因为你知道你正在访问无效的东西。你怎么知道会发生什么? (没有深入的编译器/平台知识。) 您无法通过查看或使用值来确定值是否有效。一旦一个物体被摧毁,它的残骸就会留在原来存放的地方。 (同样,你不能通过检查某人的尸体是否还在那里来确定他是否还活着。) @molbdnilo 我现在正在使用这个类比...太棒了! 【参考方案1】:

为什么我的 PRIVATE::findEntry(const char* const [], std::string) 函数会以我上面使用它的方式更改 env::C_NAME 的值,即使它只返回一个 int 并且不修改数组还是条目?

如果字符串被销毁或修改,c_str() 的返回值不再保证有效。

为什么 env::C_NAME 在 'main()' 中仍然有效? str::getAfter(const std::string&, std::string) 返回的字符串的析构函数被调用后不应该失效吗?

不是。它只是碰巧包含了你想要它包含的东西,而不是碰巧包含了你想要它包含的东西之外的东西。如果你在硬币正面朝上的情况下掷硬币,你正在做一些坏事,但它可能会碰巧奏效。如果你再次这样做,它可能会发生不工作。这就是为什么我们不做这样的事情。

不要混淆代码的行为方式,而不是您应该期望代码的行为方式。在我们期望事情无效的情况下,我们不知道代码的实际行为方式。它可能会发生好事,也可能会发生灾难性的事情。它可能会随着编译器选项、编译器版本、平台或其他参数而改变。

您有两个明显的选择。您可以将这些变量的类型从const char * 更改为std::string,或者您可以使用mallocstrdup 来分配将保持有效的内存。

【讨论】:

在这种情况下,由于函数findEntry() 没有修改我直接从中获取值的字符串,并且字符串C_NAME 的值在我调用findEntry() 时指向更改,我是否正确假设我在“findEntry()”中使用的变量使用了我原始字符串使用的部分内存? @N.Pich 很有可能。【参考方案2】:

env 环境中的“c 字符串”只是指针……仅此而已。例如,env::C_NAME 指向曾经保存您使用(str::getAfter(env[id], "=")) 获得的字符串的地址。谁知道现在有什么?您可以将env 中的“c-strings”更改为固定大小的char 缓冲区并使用strcpy() 将内容复制到其中(但要注意超出缓冲区的末尾),或者您可以保留它们作为字符串副本的指针和malloc() 空间,然后将strcpy() 原始字符串放入malloc()ed 缓冲区,或者最好的选择,使用std::string,不要担心细节。

【讨论】:

以上是关于当我调用不相关的方法时,c-strings 的值会发生变化的主要内容,如果未能解决你的问题,请参考以下文章

c-string用户输入为空时如何发送错误消息?

将浮点数分配给 long double 时,它​​的值会发生啥变化?

列表视图滚动时复选框自动调用onCheckedChange?

从 UITableView 中删除值会导致崩溃

4.6 异常处理

为啥 android webView 不支持 webSocket?