C++学习笔记----6、内存管理(二)---- 数组指针的双向性

        你可能已经看到指针与数组之间的一些重叠。自由内存空间分配的数组由其第一个元素的指针进行访问。栈上的数组通过使用数组语法([])或者正常变量声明来访问。你还会看到的是,其重叠不仅如此,指针与数组有更复杂的关系。

1、数组退化至指针

        自由内存空间数组不是指向数组所用的指针的唯一位置。也可以使用指针的语法来访问栈上数组。数组的地址实际上就是第一个元素(索引0)的地址。编译器知道当你通过变量名指向数组时,你实际上就是指向了第一个元素的地址。用这种方式,指针像在自由内存空间上的数组一样工作。下面的代码生成了一个以0初始化的栈上数组,使用了一个指针来访问它:

int myIntArray[10] {};
int* myIntPtr { myIntArray };// Access the array through the pointer.
myIntPtr[4] = 5;

       通过指针访问栈上数组的能力在传递数组给函数时非常有用。下面的函数接受整型数组作为指针。注意调用者需要显示地传递数组的大小,因为指针无法知道数组大小。这也是为什么你需要使用现代比如标准库提供的函数的另一个原因。

void doubleInts(int* theArray, size_t size)
{
    for (size_t i { 0 }; i < size; ++i) 
    { 
        theArray[i] *= 2; 
    }
}

        函数调用者可以传递栈上数组或者自由内存空间数组。对于自由内存空间数组,指针早就存在,通过其值传入函数。对于栈上数组,调用者可以传递数组变量,编译器自动把数组变量当作指向数组的指针,或者你可以显示地将第一个元素的地址传进去。这三种形式的代码如下:

size_t arrSize { 4 };
int* freeStoreArray { new int[arrSize]{ 1, 5, 3, 4 } };
doubleInts(freeStoreArray, arrSize);
delete [] freeStoreArray;
freeStoreArray = nullptr;

int stackArray[] { 5, 7, 9, 11 };
arrSize = std::size(stackArray); // Since C++17, requires <array>
//arrSize = sizeof(stackArray) / sizeof(stackArray[0]); // Pre-C++
doubleInts(stackArray, arrSize);

doubleInts(&stackArray[0], arrSize);

        数组参数传递语法与指针非常类似,因为当它传递给函数时,编译器把数组当作指针。把数组作为参数的函数,并且在数组内修改其值,实际上修改的就是原来的数组,而不是其副本。与指针一样,传递数组有效地模仿了引用传递功能,因为你实际上传递给函数的是原有数组的地址,而不是副本。下面对doubleInts()的实现即使参数是数组不是指针,其改变的依然是原有数组的值。

void doubleInts(int theArray[], size_t size)
{
    for (size_t i { 0 }; i < size; ++i) { theArray[i] *= 2; }
}

函数原型中theArray之后在方括弧之间的任何数字都会被忽略。下面的三个版本是一样的:

void doubleInts(int* theArray, size_t size);
void doubleInts(int theArray[], size_t size);
void doubleInts(int theArray[2], size_t size);

        你可能会想怎么会这样呢?当数组语法在函数定义时这样用,编译器为什么不对数组进行拷贝呢?主要还是效能的考虑----拷贝数组的元素是需要时间的,也会占用大量的内存。只要传递一个指针,编译器不需要包含拷贝数组的代码。

        传递已知长度栈上数组还有一种方法就是“通过引用”给到函数,虽然语法不明显。这个对于在自由内存空间的数组不管用。例如,下面的doubleIntsStack()只接受栈上大小为4的数组。

void doubleIntsStack(int (&theArray)[4]);

        我们以后会讨论的函数模板,用于让编译器自动地推算出栈上数组的大小:

template <size_t N>
void doubleIntsStack(int (&theArray)[N])
{
    for (size_t i { 0 }; i < N; ++i) { theArray[i] *= 2; }
}

        不要直接给函数传递C风格的数组,推荐函数用类型std::span,这个以后会讨论,“标准库函数”span把数组指针与其大小进行了组合。

2、不是所有的指针都是数组!

        因为编译器允许你在期待指针的地方传递数组,就像前面的doubeInts()函数,你可能会错误地认为指针与数组是一样的。实际上,它们会有微小的但是很重要的区别。指针与数组共享许多属性,有时候可以互换使用(如前所示),但它们真的不是完全一样的。

        指针本身没有任何意义。它可能指向随机的内存地址,一个单独的对象,或者是一个数组。你问题可以用数组语法来使用指针,但这样做并不是总是合适的,因为指针不总是数组。例如,考虑一下下面的这一行代码:

int* ptr { new int };

        指针ptr是一个有效的指针,但它不是一个数组。你可以使用数组语法ptr[0]来指向其值,但这样做从风格上是值得怀疑的,并且没有什么好处啊。实际上,对于非数组指针使用数组语法是向错误开了方便之门。Ptr[1]可以是任何东东!

        数组可以退化为指针,但不是所有的指针都是数组。

  • 14
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王俊山IT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值