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,但是在程序运行过程中却被修改的话,那么很可能这个指针是悬空指针

来源链接:https://www.cnblogs.com/stephen2023/p/19025925

© 版权声明
THE END
支持一下吧
点赞11 分享
评论 抢沙发
头像
请文明发言!
提交
头像

昵称

取消
昵称表情代码快捷回复

    暂无评论内容