C++ 语法之数组指针

一维数组:

如果我们定义了一个一维数组,那么这个数组名,就是指向第一个数组元素的地址,也即,是整个数组分配的内存空间的首地址。

比如 int a3; 定义了一个包含三个元素的数组。因为一个int占4个字节,那么系统就会为这个数组分配12个字节的内存空间,用来存储这个数组。

可以运行下面代码来观察每个元素的地址:

cpp 复制代码
int main() {
    int a[3];
    a[0] = 1;
    a[1] = 20;
    a[2] = 33;

    cout << "a的地址:" << a << "\n";
    for (int i = 0; i < 3; i++)
        cout << &a[i] << "\n";

}

运行结果:

可以看到内存的首地址是FAA8开始,注意这里的a和a0的地址是相等,因为它们都是代表数组的第一个地址。

然后依次加4个字节,FAAC,FAB0。

那么因此得出结论,数组名元素值每加1,它就会访问到下个四字节的地址(注意这里是根据类型的)。

那么a3就应该是FAB4,注意这里数组定义三个,下标是从0开始的,所以到2就结束了。

但我们依然可以通过a3访问到FAB4,只不过不是合法的,你不能写入,因为这不是属于你的内存。

如下例子:

cpp 复制代码
#include <iostream>

using namespace std;

int main() {
    int a[3];
    a[0] = 1;
    a[1] = 20;
    a[2] = 33;

    cout << "a的地址:" << a << "\n";
    for (int i = 0; i < 3; i++)
        cout << &a[i] << "\n";

    cout << "a[3]的地址:" << &a[3] << "-------a[3]的值:" << a[3]; //访问a[3]的值,int类型4个字节


}

结果: (注意访问a3是不合法的,只为示例使用)

可以看到a3的地址,是在a2的基础上加了个四个字节,这就是数组的规则。其实可以看作是指针的一种运用。

所以一维数组,我们可以像定义int 变量指针来定义数组指针,如下:

cpp 复制代码
#include <iostream>

using namespace std;

int main() {
    int a[3];
    a[0] = 1;
    a[1] = 20;
    a[2] = 33;

    int* p = a;
    cout << *p << "\n" << p[1] << "\n" << *(p + 2);

}

输出值:

这里要特别说是*(p+2)这个访问,因为指针变量不像正常变量,比如int a;

a+1的话,它就会在a的值加1, 指针变量+1的意思,是在原来的地址上,增加该类型的字节数。这个是根据指针类型来决定的。比如int类型指针加1就是,4字节的+4,+2就是8。

可以通过输出地址值来查看,比如:

cpp 复制代码
    int* p = a;
    int* p1 = p + 1;
    cout << p << "\n" << p1 << endl;

二维数组:

接下来说一下二维数组

比如定义了一个int b23,可以看成是什么,可以看成是我们定义了2个一维数组a3。我们分开来看。

那么b0就是一个内存地址,指向了第一个a3一维数组的地址。

而b1就是指向了第二个a3一维数组的首地址。

可以通过地址观察验证:

cpp 复制代码
#include <iostream>

using namespace std;

int main() {
    int b[2][3];
    b[0][0] = 1;
    b[0][1] = 20;
    b[0][2] = 33;

   //输出二堆数组的每个地址:
    for (int i = 0; i < 2; i++)
        for (int j = 0; j < 3; j++)
            cout << "b[" << i << "][" << j << "]地址:" << &b[i][j]<< endl;

    //输出b[0]和b[1]地址:
    cout << "\n\nb[0]地址:" << b[0] << "\nb[1]地址:" << b[1] << endl;
}

结果:

可以看到定义了一个二维数组,根据地址来看,在内存中是跟一维数组同样的分配方法,依次增加,每次四个字节,F5E8,F5EC,F5F0.....

只不过是以二维的层次来解读的。

那么b0即b00的的首地址,这个跟前面一维数组同样的原理,a跟a0地址一样。

而b1的地址,就是要加上12个字节,即F5F4,即b10首地址。

这个12个字节,就是包含3个整数的一维数组的跨度,这不难理解,跟前面都对应上了。

所以现在问题来了:

即现在二维数组b应该是什么样的指针指向它?它加1是多少?

(推理过程,如果不适请跳过直接看结果)

现在是二维数组b包含了两个一维数组a3;

可以把a3数组看成是一种元素。

那么就是b包含了两个元素。也就是看成是一维数组b0=元素1 b1等于元素2。

所以指针就像是这样 元素 *p=b 也就是 int *p3=b; (此元素有三个整数所以3,前面加上int 类型)

又因为\[\]的运算级高于*号,所以int *p3,你这里是定义了一个指针数组,相当定义了三个指针变量.

这里提一下指针数组和数组的指针 的区别:

那么相当于int a,b,c; p0=&a; p1=&b; p2=&c;

指针数组是这样用的。

所以我们得给*p加上括号,让它成为一个二维数组的指针,即 int (*p)3;

那么定义二维数组指针指向b就是 int (*p)3=b;

而由此可以推断,b+1也就是p+1; 它的跨度是一个a3的跨度 12字节。

那么现在我们来实际应用验证一下:

cpp 复制代码
#include <iostream>

using namespace std;

int main() {
    int b[2][3];
    b[0][0] = 1;
    b[0][1] = 20;
    b[0][2] = 33;

    b[1][1] = 66;
    b[1][2] = 88;

   //输出二堆数组的每个地址:
    for (int i = 0; i < 2; i++)
        for (int j = 0; j < 3; j++)
            cout << "b[" << i << "][" << j << "]地址:" << &b[i][j]<< endl;

    //输出b[0]和b[1]地址:
    cout << "\n\nb[0]地址:" << b[0] << "\nb[1]地址:" << b[1] << endl;
    //以上为观察对比数据

    int(*p)[3] = b;
    cout << "以下为二维数组指针p的用法:" << endl;
    //取地址的方法:
    cout << "*p是b[0]的地址,p是b的地址,所以相等(包括&b[0][0]):" << *p << "-------" << p << endl;
    //取值的方法
    cout << **p << endl;
    cout << (*p)[0] << endl;
    cout << (*p + 1)[1] << endl;

    cout << p[1][2] << endl;



}

结果:

所以二维数组b,是不能用int *p这样来指向的,类型不匹配。

但int *p可以指向b0,b1,本质上 b0是个一维数组名。那么类推三维数组也是一样的,可以依次拆分,按逻辑分解。这里就不介绍了,感兴趣的可以自行实验,乃至更多维的。

下面补充一些字符串数组的说明(未完待续)

相关推荐
clint4562 天前
C++进阶(1)——前景提要
c++
夜悊2 天前
C++代码示例:进制数简单生成工具
c++
郝学胜_神的一滴2 天前
CMake 021: IF 条件判据详诠
c++·cmake
_wyt0012 天前
洛谷 B3930 [GESP202312 五级] 烹饪问题 题解
c++·gesp
LDR0063 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术3 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript
码云数智-园园3 天前
C++20 Modules 模块详解
java·开发语言·spring
swordbob3 天前
NIO的channel中什么是 fd(File Descriptor,文件描述符)
java·开发语言·nio
源分享3 天前
Java线程同步的多种实现方法(非常详细)
java·开发语言·jvm
Luminous.3 天前
C语言--day30
c语言·开发语言