关于指针与数组一直有许多问题没有讲清楚,下面就来总结一下:
一、指针不是只能指向堆存储区,它也可以指向栈存储区、静态存储区或线程本地存储区,也可以什么也不指向。
这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