C++:指针与数组

关于指针与数组一直有许多问题没有讲清楚,下面就来总结一下:

一、指针不是只能指向堆存储区,它也可以指向栈存储区、静态存储区或线程本地存储区,也可以什么也不指向。

这4种存储区的说明:

二、指针不等同于数组。

void doTest() {

	const int arrSize = 10;

	int *arr0 = (int *)malloc(sizeof(int)*arrSize);
	int arr1[arrSize];
	int *arr2 = new int[3];

	cout << sizeof(arr0) << endl;  // 8
	cout << sizeof(arr1) << endl;  // 40
	cout << sizeof(arr2) << endl;  // 8

	arr0[3] = 3;
	cout << arr0[3] << endl;  // 3

	arr1[4] = 4;
	cout << arr1[4] << endl;  // 4

	arr2[5] = 5;
	cout << arr2[5] << endl;  // 5

	free(arr0);
	// nothing to do with arr1 
	delete[] arr2;
}

通过这个例子可以看到,虽然arr0、arr1、arr2都可以通过数组下标来操作数据,但它们的实质是不同的。
1、sizeof(arr0)和sizeof(arr2),都是 int*的大小,而sizeof(arr1)才是数组的大小。
2、arr0和arr2都是在堆中分配的,而arr1是在栈中分配的。
3、arr0和arr2都需要手动删除,而arr1可以自动回收。

三、数组的传参,最好使用引用传参

#include <iostream>

using namespace std;

void fun1(int a[3]) {
	cout << a << endl;
	cout << sizeof(a) << endl;
	cout << a[2] << endl;
}

void fun2(int (&ra)[3]) {
	cout << ra << endl;
	cout << sizeof(ra) << endl;
	cout << ra[2] << endl;
}

void fun3(int* pa) {
	cout << pa << endl;
	cout << sizeof(pa) << endl;
	cout << pa[2] << endl;
}

int main() {
	int a[3] = {1,2,3};

	fun1(a);
	fun2(a);
	fun3(a);

	return 1;
}

运行结果:

000000764E74FAD8
8
3
000000764E74FAD8
12
3
000000764E74FAD8
8
3

从运行结果中可以看到,使用引用传参时,数组的信息不会丢失,所以应尽量使用数组的引用传参。

四、class和struct中的成员变量是在什么区域分配,由class和struct在什么区域分配决定

比如:如果class和struct的本身是在栈中分配的,则class和struct其中包含的成员变量也一定是在栈分配的。

如果class和struct其中包含的成员变量是指针变量,则堆存储区、栈存储区、静态存储区、线程本地存储区这4种存储区,这个指针变量都有可能指向。

五、std::array<T,size>和std::vector<T>都可以用来代替数组,但内存分配方式不同

1、T实例的存储方式不同:
std::array<T,size>中的T实例是以T[]的形式存放的,所以T实例的存储区与std::array<T,size>相同;
std::vector<T>中的T实例是通过Allocator分配的,是在堆存储区中,与std::vector<T>的存储区不一定相同。
2、std::array<T,size>和std::vector<T>被销毁时,其中包含的T实例也会被回收,但回收方式不同:
std::array<T,size>中的T实例是自动被销毁的;
std::vector<T>中的T实例是由Allocator销毁的。

举一个vector的例子:

#include <iostream>
#include <tuple>
#include <exception>
#include <string>
#include <memory>
#include <vector>

using namespace std;

class Person {

public:
	string name;
	int age;

	Person()  {
		cout << "Person()" << endl;
	}

	Person(string _name, int _age): name(_name), age(_age) {
		cout << "Person(string _name, int _age)" << endl;
	}

	Person(const Person&) {
		cout << "Person(const Person&)" << endl;
	}

	Person(Person&&) {
		cout << "Person(Person&&)" << endl;
	}

	Person& operator=(const Person&) {
		cout << "operator=(const Person&)" << endl;

		return *this;
	}
};

int main() {
	vector<Person> v;

	Person p1 = Person();
	cout << "p1:" << &p1 << endl;

	v.push_back(p1);
	cout << v.size() << endl;

	Person p2 = Person();
	cout << "p2:" << &p2 << endl;

	Person& p_1 = v.at(0);
	cout << "p_1:" << &p_1 << endl;

	Person p3 = Person();
	cout << "p3:" << &p3 << endl;

	v.emplace_back();

	Person& p_2 = v.at(1);
	cout << "p_2:" << &p_2 << endl;

	cout << v.size() << endl;

	return 1;
}

运行结果:

Person()
p1:000000D1AE8FF978
Person(const Person&)
1
Person()
p2:000000D1AE8FF9C8
p_1:000002432652D9F0
Person()
p3:000000D1AE8FFA38
Person()
Person(const Person&)
p_2:00000243265155F0
2

从结果中可以看到,p1、p2、p3是在一的地址段个地址段内,而p_1、p_2是在另一个地址段内,由于p1、p2、p3都是在栈中分配的,如果p_1、p_2也是在栈中创建,则p_1、p_2应与p1、p2、p3相同,但实际输出结果不是这样,这就说明了p_1、p_2一定不是在栈中创建的,这里只有可能是在堆中创建的。

参考文档

Storage class specifiers
Where are pointers in C++ stored, on the stack or in the heap?
Creating array of objects on the stack and heap

猜你喜欢

转载自blog.csdn.net/netyeaxi/article/details/83186301