狼魄 發表於 2025-8-6 21:45:00

const char* 指针作为函数参数也能被修改?

<h2 id="问题">问题</h2>
<font size="4">
如下示例程序所示, 首先构建了一个如下所示的Node树,每个节点存有一个数字。程序需要通过node的name返回其数字,即findNodeNumByName,其中name是const char*,表示方法中不会修改其参数。另外这个参数来自于另外一个方法,即findNodeNameByNum,输入数字找到对应的node的name。所以正确的程序输出应该为输入1,输出1,输入2,输出2,输出与输入相同。</font>
<p><img src="https://img2024.cnblogs.com/blog/1306820/202508/1306820-20250806214006031-1274587987.png"></p>
<pre><code class="language-c++">#include&lt;iostream&gt;
#include&lt;string&gt;
#include&lt;vector&gt;

class Node
{
public:
        Node(std::string s, int n)
        {
                name = (char*)malloc(10);
                strcpy_s(name, 10, s.c_str());
                num = n;
        }
        std::string GetName() {return name;}

        const char* GetNameCstr() {return name;}

        int GetNum() { return num; }

        std::vector&lt;Node*&gt; GetChildNodes() {return childNodes;}

        char* name;
        int num;
        std::vector&lt;Node*&gt; childNodes;
};

Node* A, * B, * C, * D, * E, * F, * G, * H;
void initializeNodes()
{
        A = new Node("A", 0);
        B = new Node("B", 1);
        C = new Node("C", 2);
        D = new Node("D", 3);
        E = new Node("E", 4);
        F = new Node("F", 5);
        G = new Node("G", 6);
        H = new Node("H", 7);

        A-&gt;childNodes.push_back(B);
        A-&gt;childNodes.push_back(C);
        B-&gt;childNodes.push_back(D);
        B-&gt;childNodes.push_back(E);
        B-&gt;childNodes.push_back(F);
        C-&gt;childNodes.push_back(G);
        E-&gt;childNodes.push_back(H);
}

const char* findNodeNameByNum(Node* node, int num)
{
        const char* n = node-&gt;GetName().c_str();
        if (node-&gt;GetNum() == num)
                return n;
        for (auto* n : node-&gt;GetChildNodes())
        {
                const char* result = findNodeNameByNum(n, num);
                if (result != nullptr)
                        return result;
        }

        return nullptr;
}

int findNodeNumByName(Node* node, const char* name)
{
        //std::cout &lt;&lt; name &lt;&lt; ":" &lt;&lt; (void*)name &lt;&lt; std::endl;
        if (strcmp(node-&gt;GetName().c_str(), name) == 0)
                return node-&gt;num;

        for (auto* n : node-&gt;GetChildNodes())
        {
                int result = findNodeNumByName(n, name);
                if (result != -1)
                        return result;
        }

        return -1;
}

int main()
{
        initializeNodes();
        std::cout &lt;&lt; "Please input number you want to find:" &lt;&lt; std::endl;
        int n;
        while (std::cin&gt;&gt; n)
        {
                if (n&lt;0||n&gt;7) break;
                const char* name = findNodeNameByNum(A, n);
                std::cout &lt;&lt; "find: " &lt;&lt; findNodeNumByName(A, name) &lt;&lt; std::endl;
        }
       
        return 0;
}

</code></pre>
<h2 id="输出结果">输出结果</h2>
<p><img src="https://img2024.cnblogs.com/blog/1306820/202508/1306820-20250806214024298-390262089.png"></p>
<h2 id="分析">分析</h2>
<font size="4">
看结果发现,0,1,3,7正确,2,4,5,6错误,并都返回了3. 单单看findNodeNumByName方法基本没有问题,传入的name为const char*, 理论上不应该在方法中被修改。程序中的bug也不难发现,这个const char*来源于findNodeNameByNum,而这个指针为悬空指针,所以传入findNodeNumByName中的值应为空,为什么还会有值返回?
</font><ul><font size="4">
</font><li><font size="4">将node类成员函数GetName()返回值改为DebugString,而DebugString继承std::string,区别只是构造/析构时候返回内部成员c_str的地址。</font></li>
</ul>
<pre><code class="language-c++">struct DebugString : public std::string {
        DebugString(const char* s) : std::string(s) {
                std::cout &lt;&lt; "Constructed: " &lt;&lt; c_str() &lt;&lt; ":" &lt;&lt; (void*)c_str() &lt;&lt; "\n";
        }
        ~DebugString() {
                std::cout &lt;&lt; "Destroyed: " &lt;&lt; c_str() &lt;&lt; "\n";
        }
};

class Node
{
        .....
        DebugString GetName() {return name;}
        .....
}
</code></pre>
<font size="4">
以0为例发现,第一次findNodeNameByNum,找到A节点名字的临时指针为FA50,然后析构后为空,传入findNodeNumByName,函数中首先对A节点查找name,并再次在FA50地址中赋值,所以判断为相等,返回相应的值。
<p><img src="https://img2024.cnblogs.com/blog/1306820/202508/1306820-20250806213733435-1276571133.png"></p>
<p>再以6为例发现, D/E/G 构造的地址都相同,都为FAC0;B,C构造的地址相同,都为FB70;所以当传入的是6,为G的地址,在第二次循环的时候,D/E/G还在原来的地址上构造,导致先返回D的地址为3.<br>
<img src="https://img2024.cnblogs.com/blog/1306820/202508/1306820-20250806213835145-147879803.png"></p>
<p>同理,传入的是B,C的值,先返回的都是B的值,如下图所示<br>
<img src="https://img2024.cnblogs.com/blog/1306820/202508/1306820-20250806213933302-309777742.png"></p>
</font><p><font size="4">由于H(7),并没有在其他重复地址上构建,所以H(7)能返回正确的值。</font></p>
<p>[注]: 以上程序为在windows平台,Release/X64下测试通过,由于std::string具体在哪里构造不可控,更换平台/架构不一定有相似输出</p>
<h2 id="结论">结论</h2>
<font size="4">
</font><ul><font size="4">
</font><li><font size="4">在指针作函数参数被标记为const,但是在程序运行过程中却被修改的话,那么很可能这个指针是悬空指针<br>
</font></li>
</ul>


</div>
<div id="MySignature" role="contentinfo">
    <div style="border:silver 1px dashed;background-color: rgba(0, 0, 0, 0);width:600px;padding:12px;font-size: 12px;font-family: 微软雅黑; ">
作者:robot2017<br>
出处:https://www.cnblogs.com/stephen2023/p/19025925<br>
版权:本文版权归作者和博客园共有<br>
转载:欢迎转载,但未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任
</div><br><br>
来源:https://www.cnblogs.com/stephen2023/p/19025925
頁: [1]
查看完整版本: const char* 指针作为函数参数也能被修改?