











我们来看C++中函数中传入的参数是指针类型的情况, 我们来看一个例子
#include <iostream>
int main()
{
int* p = new int(10);
testFunc(p);
std::cout << *p << endl; //这里输出的是15}
void testFunc(int* p) { *p = 15; }
首先我们来看 上面中的 testFunc(int* p) => 它的参数是指针类型,C++中传入的参数是指针类型,那么就意味着传入的是地址,我们来看下面这个例子
void swap(int* x, int* y) { int temp = *x; *x = *y; *y = temp; } int main() { int a = 9, b; swap(&a, &b); //这里传入的是整形数a,b的地址 std::cout << *a << " " << *b <<end1; }
从上面这个例子中可以看出,swap函数的形参是指针类型,在调用它的时候,传入的就是参数的地址
现在,我们再回来看最上面的例子:
int* p = new int(10); => new int(10) 这句话,是用C++的关键字来在内存中开辟了一块内存,这块内存中存的是整形数据10,它返回的是一个int型的指针,这个指针指向这块内存的地址.
当我调用 testFunc函数时,传入的是参数的地址,所以是 testFunc(p) => 这里要好好理解,它这里是按值传递的,也就是说,这里,在函数testFunc内部,它会把指针p复制一份,产生一个新的指针(我们可以叫 new p),只不过这个新的指针new p和原来的指针p都同时指向同一块内存

我们再来看上面第一个例子
int* p = new int(10); //指针p 指向关键字new分配的内存(我们叫它 内存A),这块内存中存的是整数值 10
testFunc(p); // 在testFunc函数内产生一个新的指针Copy p, 它指向的也是内存A,在它内部,把内存A存的整数值改成了15
所以,例子中输出的是15现在,我们再来看一道经典的面试题
void foo(int* p) { p = 0; // 这里是把p设置为空指针,也就是说不指向任何有效的内存. 它等价于 p = NULL 或者 p = nullptr, 但从C++11开始,推荐用nullptr } int main() { int* p = new int(20); foo(p); std::cout << p[0] << std::endl; }
上面代码输出什么 => 输出20, 因为foo(p) 函数里面只是复制了一份新的指针p, p=0 只是把复制的这份新指针变为空指针,但是main函数中原来的指针p还是一样存在,还是指向那块存了20的内存,所以输出还是20
但是上面的foo函数内部是有问题的,在p=0把指针p设置为空指针(不指向任何内存的指针)之前,应该把它指向的内存删除释放出来,所以,之前需要 delete p;
这里,理解一下p[0], 为什么可以这样写? => 是可以这样写的,这里涉及到一个概念
在内置类型(数组 / 指针)上,[] 本质就是“指针偏移 + 解引用”的语法糖 => 这句话怎么理解呢,也就是说对于内置类型,a[b] = *(a+b)
int arr[3] = {10,20,30}; int x = arr[1]; 这句话在这里等价于 int x = *(arr + 1); // 但千万注意,不是 x = *arr1
也就是说,
arr[0] 等价于 *(arr + 0),但不是 *arr0
arr[1] 等价于 *(arr + 1),但不是 *arr1arr[2] 等价于 *(arr + 2),但不是 *arr2
因为 *arr0,*arr1,*arr2三个指针压根就不存在的
所以,上面的 int * p = new(20); p[0] 就等价于 *(p + 0), 也就是 *p, 所以输出的是20 => 但是我们是完全不推荐这么写的, 虽然在这个例子中p[0]没有越界(因为就算第0个元素),但是这会暗示它是个数组,给人造成误解,因为这里显然它不是数组. 而且,如果这里代码编写人员误写p[1], 那就造成了越界访问,产生未定义的行为错误.
但是注意到没有,我们上面特意强调了,在内置类型上. 如果不是内置类型,是自定义类型,比如重载运算符operator[], 它就不是“指针偏移+解引用”了, 也不能这么用了
struct MyArray { int arrData[3] = {1,2,3};
int& operator[](int i)
{
std::cout << "output data in position " << i +1 <<endl;
if(0 <= i < arrData.size()-1)
return arrData[i+1];
else
return arrData[0];
} }
上面这个例子中,我们用operator关键字重写了[], 当数组中坐标是i时,我们输出的不是arrData[i]的值,而是arrData[i+1];
在这里
MyArray a; a[1]; //调用的是operator[]
这里的a[1], 它就不等于 *(a+1)
好了,我们来看C++中一个非常经典的面试题目
int arr[3] = {10, 20, 30}; int* p = arr; std::cout << sizeof(arr) << std::endl; std::cout << sizeof(p) << std::endl;
我们假设系统中int是占4个字节,指针占8个字节(64位系统),那么上面输出的是什么
sizeof(arr)输出的是12 => 输出的数组占的内存大小 4 * 3 = 12
sizeof(p)输出的是8 => 输出的是p这个指针占的内存大小
这里涉及到一个概念 - 数组退化
数组退化 => 大多数情况下,arr都会退化成指针int*(指向数组首元素),但是sizeof是少数不会退化的场景,所以sizeof(arr)它获取的是数组的整块内存
我们再来看一个经典的坑
#include <iostream>
void foo(int arr[]) {
std::cout << sizeof(arr) << std::endl;
}
int main()
{
int arr[3] = {10, 20, 30};
foo(arr); // 这里输出的是什么?
}
在64位系统中,这里输出的是指针大小8(32位系统是4) => 因为数组名作为函数参数时(arr作为函数foo的参数),数组名会退化为指针,所以,这里在函数foo内部,sizeof(arr)返回的是指针大小,而不是数组大小
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。