C++

C++指针错误

Posted by Roy Zhang on 2019-08-01

一直在工作中对指针和内存分配搞不太清楚。模模糊糊。现在看下今天犯下的一个错误:
方程为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void RouteSerializer::setupChildNode(xml_node<> *childnode, std::string childname, xml_node<> *fathernode, std::vector<std::pair<std::string, std::string>> attri, std::string content)
{
char *contentc;
if (content.empty())
{
contentc = NULL;
}
else
{
contentc = m_doc.allocate_string(content.c_str());
}

char *childnamec = m_doc.allocate_string(childname.c_str());
childnode = m_doc.allocate_node(node_element, childnamec, contentc);
// for (std::vector<std::pair<std::string, std::string>> it = attri.begin(); it != attri.end(); it++)
for (int i = 0; i < attri.size(); i++)
{
char *keyc = m_doc.allocate_string(attri[i].first.c_str());
char *valuec = m_doc.allocate_string(attri[i].second.c_str());
childnode->append_attribute(m_doc.allocate_attribute(keyc, valuec));
}
fathernode->append_node(childnode);
}

其中在方程中为这个指针分配了内存空间,出现的问题是调用一次该方程没问题,但是连续调用两次该方程,程序崩溃。

1
childnode = m_doc.allocate_node(node_element, childnamec, contentc);

原因在于,我们查看原代码可以发现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
xml_node<Ch> *allocate_node(node_type type, 
const Ch *name = 0, const Ch *value = 0,
std::size_t name_size = 0, std::size_t value_size = 0)
{
void *memory = allocate_aligned(sizeof(xml_node<Ch>));
xml_node<Ch> *node = new(memory) xml_node<Ch>(type);
if (name)
{
if (name_size > 0)
node->name(name, name_size);
else
node->name(name);
}
if (value)
{
if (value_size > 0)
node->value(value, value_size);
else
node->value(value);
}
return node;
}

源代码在这里申请了内存。我后面还没试我的想法对不对,但是当我读到了一句话如果函数的参数是一个指针,不要指望用该指针去申请动态内存.如下

1
2
3
4
5
6
7
8
void GetMemory(char *p, int num){
p = (char *)malloc(sizeof(char) * num);
}
void Test(void){
char *str = NULL;
GetMemory(str, 100); // str 仍然为 NULL
strcpy(str, "hello"); // 运行错误
}

毛病出在函数GetMemory中。编译器总是要为函数的每个参数制作临时副本,指针参数p的副本是 _p,编译器使 _p=p。如果函数体内的程序修改了_p的内容,就导致参数p的内容作相应的修改。这就是指针可以用作输出参数的原因。在本例中,_p申请了新的内存,只是把 _p所指的内存地址改变了,但是p丝毫未变。所以函数GetMemory并不能输出任何东西。事实上,每执行一次GetMemory就会泄露一块内存,因为没有用free释放内存。

还有就是对于指针来说无疑就是几个方式,作为参数放到方程里面,作为返回值。作为参数说了,不能在方程中给他赋值。作为返回值,要看指针指向的地方,加入你指向栈,出了函数域你就自动释放了。所以返回的指针必须要new一个内存块。

刚才试了一下新建, 地址为0, 说明建的时候是不会有地址的,有值了才会有地址。

1
2
3
4
5
6
7
8
9
10
11
// Example program
#include <iostream>
#include <string>
#include <memory>
int main()
{
int *p;
std::cout<<"dizhi "<<p;
//p = new int(1);
//std::cout<<"dizhi "<<p;
}

如果是传递指针,那么会先复制该指针,在函数内部使用的是复制后的指针,这个指针与原来的指针指向相同的地址,如果在函数内部将复制后的指针指向了另外的新的对象,那么不会影响原有的指针;
但 是对于传递指针应用,如果将传递进来的指针指向了新的对象,那么原始的指针也就指向了新的对象,这样就会造成内存泄漏,因为原来指针指向的地方已经不能再 引用了,即使没有将传递进来的指针指向新的对象,而是在函数结束的时候释放了指针,那么在函数外部就不能再使用原有的指针了,因为原来的内存已经被释放了.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <cstdlib>
#include <cstring>
#include <memory>
#include <iostream>

using namespace std;

void func2(char *str)
{
str = (char *)malloc(10);
strcpy(str, "hello");
}
void func3(char *&str)
{
str = (char *)malloc(10);
strcpy(str, "hello");
}
int main()
{
char *str = "1234567890";

func2(str);
cout << str << endl;
func3(str);
cout << str << endl;

简单一点可以这么想,如果不用引用的话,被传递的参数本身是不能被修改的,
即使你传递的是指针,也不过能修改指针指向的内容,不能修改指针本身。
如果要修改当前被传递的参数的话,要么再加一级指针,要么用引用。

指针的引用类似于二级指针

传递指针的话,在函数体内你只能修改指针的内容,不能改变指针本身(非const type *类型,指针本身也能修改的,只是这个修改,不会影响到实参指针)
传递指针的引用,在函数体内,你既可以修改指针的内容,也可以修改指针本身(实参)

有区别,
无论你传值还是传指针,函数都会生成一个临时变量, 但传引用时,不会生成临时变量, 当你传值时,只可以引用值而不可以改变值,但传值引用时,可以改变值, 当你传指针时,只可以改变指针所指的内容,不可以改变指针本身,但传指针引用时,即可以改变指针所指的内容,又可以改变指针本身,但传引用主要是它不生成临时变量,不进行返回值copy等,速度快。