const char* 指針作為函數參數也能被修改?
問題
如下示例程序所示, 首先構建了一個如下所示的Node樹,每個節點存有一個數字。程序需要通過node的name返回其數字,即findNodeNumByName,其中name是const char*,表示方法中不會修改其參數。另外這個參數來自于另外一個方法,即findNodeNameByNum,輸入數字找到對應的node的name。所以正確的程序輸出應該為輸入1,輸出1,輸入2,輸出2,輸出與輸入相同。
#include<iostream>
#include<string>
#include<vector>
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<Node*> GetChildNodes() {return childNodes;}
char* name;
int num;
std::vector<Node*> 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->childNodes.push_back(B);
A->childNodes.push_back(C);
B->childNodes.push_back(D);
B->childNodes.push_back(E);
B->childNodes.push_back(F);
C->childNodes.push_back(G);
E->childNodes.push_back(H);
}
const char* findNodeNameByNum(Node* node, int num)
{
const char* n = node->GetName().c_str();
if (node->GetNum() == num)
return n;
for (auto* n : node->GetChildNodes())
{
const char* result = findNodeNameByNum(n, num);
if (result != nullptr)
return result;
}
return nullptr;
}
int findNodeNumByName(Node* node, const char* name)
{
//std::cout << name << ":" << (void*)name << std::endl;
if (strcmp(node->GetName().c_str(), name) == 0)
return node->num;
for (auto* n : node->GetChildNodes())
{
int result = findNodeNumByName(n, name);
if (result != -1)
return result;
}
return -1;
}
int main()
{
initializeNodes();
std::cout << "Please input number you want to find:" << std::endl;
int n;
while (std::cin>> n)
{
if (n<0||n>7) break;
const char* name = findNodeNameByNum(A, n);
std::cout << "find: " << findNodeNumByName(A, name) << std::endl;
}
return 0;
}
輸出結果

分析
看結果發現,0,1,3,7正確,2,4,5,6錯誤,并都返回了3. 單單看findNodeNumByName方法基本沒有問題,傳入的name為const char*, 理論上不應該在方法中被修改。程序中的bug也不難發現,這個const char*來源于findNodeNameByNum,而這個指針為懸空指針,所以傳入findNodeNumByName中的值應為空,為什么還會有值返回?- 將node類成員函數GetName()返回值改為DebugString,而DebugString繼承std::string,區別只是構造/析構時候返回內部成員c_str的地址。
struct DebugString : public std::string {
DebugString(const char* s) : std::string(s) {
std::cout << "Constructed: " << c_str() << ":" << (void*)c_str() << "\n";
}
~DebugString() {
std::cout << "Destroyed: " << c_str() << "\n";
}
};
class Node
{
.....
DebugString GetName() {return name;}
.....
}
以0為例發現,第一次findNodeNameByNum,找到A節點名字的臨時指針為FA50,然后析構后為空,傳入findNodeNumByName,函數中首先對A節點查找name,并再次在FA50地址中賦值,所以判斷為相等,返回相應的值。

再以6為例發現, D/E/G 構造的地址都相同,都為FAC0;B,C構造的地址相同,都為FB70;所以當傳入的是6,為G的地址,在第二次循環的時候,D/E/G還在原來的地址上構造,導致先返回D的地址為3.

同理,傳入的是B,C的值,先返回的都是B的值,如下圖所示

由于H(7),并沒有在其他重復地址上構建,所以H(7)能返回正確的值。
[注]: 以上程序為在windows平臺,Release/X64下測試通過,由于std::string具體在哪里構造不可控,更換平臺/架構不一定有相似輸出
結論
- 在指針作函數參數被標記為const,但是在程序運行過程中卻被修改的話,那么很可能這個指針是懸空指針
作者:robot2017
出處:http://www.rzrgm.cn/stephen2023/p/19025925
版權:本文版權歸作者和博客園共有
轉載:歡迎轉載,但未經作者同意,必須保留此段聲明;必須在文章中給出原文連接;否則必究法律責任
出處:http://www.rzrgm.cn/stephen2023/p/19025925
版權:本文版權歸作者和博客園共有
轉載:歡迎轉載,但未經作者同意,必須保留此段聲明;必須在文章中給出原文連接;否則必究法律責任
浙公網安備 33010602011771號