鱼C论坛

 找回密码
 立即注册
查看: 1306|回复: 9

[已解决]关于数组和指针的区别

[复制链接]
发表于 2019-1-11 09:54:20 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
两者好像可以互换,但又好像在某些地方又不行,求解答
最佳答案
2019-1-12 08:35:21
本帖最后由 行客 于 2019-1-12 13:20 编辑

首先必须纠正一个概念,有些教科书中所说的概念“数组名就是一个指向数组首元素的指针常量”,这是一种错误的说法。

你要记住:数组和指针是两个完全不同的“类型”。这里所说的“类型”,就如同我们常见的基本类型int、char,数组和指针也是两种类型。你必须先看明白我所说的这一段话。



如果上面你看明白了,我们继续来说:

数组既可以表示一种“数据类型”,也可以表示这种类型的“一个对象”(非面向对象之对象,下同)。如果你明白类和对象的关系,这里就能看明白了;如果你不明白类和对象,那我这样给你解释,假设你定义一个int类型的变量n,即int n; ,这里的int就是我们上面说的“类型”,n就是上面说的“对象”。

那么,数组表示为对象时也可以称之为这个数组类型的变量,和其他如int类型的变量n一样,这个数组变量有地址也有值。


数组的地址就是数组所占据内存空间的第一块存储单元的编号,而数组的值是由数组所有元素的值构成。

数组名既不是指针,也不是指针变量,而是数组变量的名字,与数组名相对应的是指针变量的变量名,对应的是 我们上面说的n,都是一个符号。


好,我们来看代码说话:

        int a[10];
        int* p = a;
       
        cout << sizeof(a) <<endl; // 40
        cout << sizeof(p) <<endl; // 4

作为数组,a拥有可以存放10个int型数据的空间,可以将一个int型的值存储到a中任意一个元素中。但作为一个指针的p,只能存储一个地址。

sizeof 操作符可以获取到一种数据类型所占据的内存大小,指针类型在x32位机器上的大小是4,而数组类型的大小是所有元素大小之和,上例中即为10个int型的大小,是40。


那么,误解从何而来?

我们可以将数组名赋值给一个指针,而赋值后的指针是指向数组首元素的,这让数组名看起来确像一个指针。

直接输出数组名会得到数组首元素的地址,这让人们误觉得“数组名的值就是数组首元素地址“,符合指针的定义。

数组名也可以像指针一样运算,对数组的索引和指针的运算看起来也是相同的。

如例:

#include <stdio.h>

int main(){
    int a[] = {1,2,3};
    int * p = a;
    printf("a:\%#x, p:%#x, &a[0]:%#x\n", a, p, &a[0]);
    printf("*a:\%d, *p:%d, a[0]:%d, p[0]:%d\n", *a, *p, a[0], p[0]);
    printf("*(a+1):\%d, *(p+1):%d, a[1]:%d, p[1]:%d\n", *(a+1), *(p+1), a[1], p[1]);
    return 0;
}

输出:

a:0x5fcaf0, p:0x5fcaf0, &a[0]:0x5fcaf0
*a:1, *p:1, a[0]:1, p[0]:1
*(a+1):2, *(p+1):2, a[1]:2, p[1]:2


这些都是让我们觉得数组和指针很相似。

我们下面来解决这个问题:

首先,我们从 &a 与 &a[0] 说起

数组的地址和数组首元素的地址虽然值相同,但意义不同。

值相同是因为,一个变量无论在在内存中占据多大空间,它的地址总是该空间第一个内存单元的地址。而数组的元素依次连续分布在整块数组空间中,数组空间的第一个内存单元被数组首元素占据,必然也同时是数组首元素所占空间的第一块空间单元,所以数组的地址与数组首元素的地址相同。

意义不同是因为,数组地址代表了整块数组所占据的内存空间,而数组首元素的地址只代表了首元素所占据的空间。这句话你一定要好好理解一下。

&a 表示取数组的地址,其结果是一个指向该数组的指针,它可以赋值给另一个同类型的指针。

&a[0]表示取数组首元素的地址,其结果是指向该数组首元素的指针,可以赋值给另一个同类型的指针。

注意:指向数组的指针和指向数组首元素的指针是两种不同类型的指针。

我们通过一个例子来加强理解:

int main(){
    int a[]={1,2,3};
    int (* pa)[3];
    int * pi;
    pa = &a;
    pi = &a[0];
    printf("&a=%#x, &a[0]=%#x\n",&a, &a[0]);
    printf("pa=%#x, sizeof(a)=%d, pa+1=%#x\n", pa, sizeof(a), pa+1);
    printf("pi=%#x, sizeof(a[0])=%d, pi+1=%#x\n", pi, sizeof(a[0]), pi+1);
    return 0;
}

编译后运行,输如下:

&a=0x5fcaf0, &a[0]=0x5fcaf0
pa=0x5fcaf0, sizeof(a)=12, pa+1=0x5fcafc
pi=0x5fcaf0, sizeof(a[0])=4, pi+1=0x5fcaf4

我们发现,取数组地址(&a)得到的指针pa和取数组首元素(&a[0])得到的指针pi是两种不同类型的指针,pa是一个指向有三个int型元素的数组的指针,pi是一个指向int型对象的指针。虽然pi和pa的值相同,但所指的内存空间不同,pi所指的空间处于pa所指空间的内部,而且是内部最靠前的部分。pi和pa所指内存块的大小显然是不同的,因此我们看到pa+1并不等于pi+1。

由指针运算规则可知,pa+1的值就是pa所指空间的下一个空间的地址,所以pa+1的值就是pa的地址向后偏移一段后的地址,这个偏移量就是pa所指的数组a的大小,即12个内存单元。同样,pi+1的值是pi向后偏移4个单位(int型的大小)后的地址。

这样验证了,我们开始所说的“数组可以表示一种“数据类型””。这里我们可以理解为(&a)就是数组类型。

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2019-1-11 10:11:12 | 显示全部楼层
数组是指针的一种表现形式,更加友好罢了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-1-11 10:57:36 | 显示全部楼层
我说下我个人的理解吧,数组就像是一串糖葫芦,一串糖葫芦上面的山楂是一个紧紧挨着另一个,就像数组里面

每个元素都是紧紧挨着他旁边的另一个相同类型的元素。而且一般来说一串糖葫芦上面都是同一个口味,这就是

说一个数组里面存放的数据类型都是一样的。指针的话就只是一个签子而已,你想穿什么吃就穿什么在上面,这

也就是说指针可以指向各种不同的内容,但是指针是要和指向的内容类型一致。吃烧烤总不能用木签子,你

说对吧
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-1-11 14:59:44 | 显示全部楼层
数组名是个地址常量!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-1-11 15:19:25 | 显示全部楼层
数组按照大小占相应内存,但是实际上,你去获取数组的地址,他返回的是首位的地址,指针如果指向数组第一个地址,相当于指向了整个数组
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-1-12 08:26:53 | 显示全部楼层
啊涂涂 发表于 2019-1-11 10:57
我说下我个人的理解吧,数组就像是一串糖葫芦,一串糖葫芦上面的山楂是一个紧紧挨着另一个,就像数组里面
...

可以声明 void 的指针吗??那我不就可以想指向哪个类型的都可以了??
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-1-12 08:35:21 | 显示全部楼层    本楼为最佳答案   
本帖最后由 行客 于 2019-1-12 13:20 编辑

首先必须纠正一个概念,有些教科书中所说的概念“数组名就是一个指向数组首元素的指针常量”,这是一种错误的说法。

你要记住:数组和指针是两个完全不同的“类型”。这里所说的“类型”,就如同我们常见的基本类型int、char,数组和指针也是两种类型。你必须先看明白我所说的这一段话。



如果上面你看明白了,我们继续来说:

数组既可以表示一种“数据类型”,也可以表示这种类型的“一个对象”(非面向对象之对象,下同)。如果你明白类和对象的关系,这里就能看明白了;如果你不明白类和对象,那我这样给你解释,假设你定义一个int类型的变量n,即int n; ,这里的int就是我们上面说的“类型”,n就是上面说的“对象”。

那么,数组表示为对象时也可以称之为这个数组类型的变量,和其他如int类型的变量n一样,这个数组变量有地址也有值。


数组的地址就是数组所占据内存空间的第一块存储单元的编号,而数组的值是由数组所有元素的值构成。

数组名既不是指针,也不是指针变量,而是数组变量的名字,与数组名相对应的是指针变量的变量名,对应的是 我们上面说的n,都是一个符号。


好,我们来看代码说话:

        int a[10];
        int* p = a;
       
        cout << sizeof(a) <<endl; // 40
        cout << sizeof(p) <<endl; // 4

作为数组,a拥有可以存放10个int型数据的空间,可以将一个int型的值存储到a中任意一个元素中。但作为一个指针的p,只能存储一个地址。

sizeof 操作符可以获取到一种数据类型所占据的内存大小,指针类型在x32位机器上的大小是4,而数组类型的大小是所有元素大小之和,上例中即为10个int型的大小,是40。


那么,误解从何而来?

我们可以将数组名赋值给一个指针,而赋值后的指针是指向数组首元素的,这让数组名看起来确像一个指针。

直接输出数组名会得到数组首元素的地址,这让人们误觉得“数组名的值就是数组首元素地址“,符合指针的定义。

数组名也可以像指针一样运算,对数组的索引和指针的运算看起来也是相同的。

如例:

#include <stdio.h>

int main(){
    int a[] = {1,2,3};
    int * p = a;
    printf("a:\%#x, p:%#x, &a[0]:%#x\n", a, p, &a[0]);
    printf("*a:\%d, *p:%d, a[0]:%d, p[0]:%d\n", *a, *p, a[0], p[0]);
    printf("*(a+1):\%d, *(p+1):%d, a[1]:%d, p[1]:%d\n", *(a+1), *(p+1), a[1], p[1]);
    return 0;
}

输出:

a:0x5fcaf0, p:0x5fcaf0, &a[0]:0x5fcaf0
*a:1, *p:1, a[0]:1, p[0]:1
*(a+1):2, *(p+1):2, a[1]:2, p[1]:2


这些都是让我们觉得数组和指针很相似。

我们下面来解决这个问题:

首先,我们从 &a 与 &a[0] 说起

数组的地址和数组首元素的地址虽然值相同,但意义不同。

值相同是因为,一个变量无论在在内存中占据多大空间,它的地址总是该空间第一个内存单元的地址。而数组的元素依次连续分布在整块数组空间中,数组空间的第一个内存单元被数组首元素占据,必然也同时是数组首元素所占空间的第一块空间单元,所以数组的地址与数组首元素的地址相同。

意义不同是因为,数组地址代表了整块数组所占据的内存空间,而数组首元素的地址只代表了首元素所占据的空间。这句话你一定要好好理解一下。

&a 表示取数组的地址,其结果是一个指向该数组的指针,它可以赋值给另一个同类型的指针。

&a[0]表示取数组首元素的地址,其结果是指向该数组首元素的指针,可以赋值给另一个同类型的指针。

注意:指向数组的指针和指向数组首元素的指针是两种不同类型的指针。

我们通过一个例子来加强理解:

int main(){
    int a[]={1,2,3};
    int (* pa)[3];
    int * pi;
    pa = &a;
    pi = &a[0];
    printf("&a=%#x, &a[0]=%#x\n",&a, &a[0]);
    printf("pa=%#x, sizeof(a)=%d, pa+1=%#x\n", pa, sizeof(a), pa+1);
    printf("pi=%#x, sizeof(a[0])=%d, pi+1=%#x\n", pi, sizeof(a[0]), pi+1);
    return 0;
}

编译后运行,输如下:

&a=0x5fcaf0, &a[0]=0x5fcaf0
pa=0x5fcaf0, sizeof(a)=12, pa+1=0x5fcafc
pi=0x5fcaf0, sizeof(a[0])=4, pi+1=0x5fcaf4

我们发现,取数组地址(&a)得到的指针pa和取数组首元素(&a[0])得到的指针pi是两种不同类型的指针,pa是一个指向有三个int型元素的数组的指针,pi是一个指向int型对象的指针。虽然pi和pa的值相同,但所指的内存空间不同,pi所指的空间处于pa所指空间的内部,而且是内部最靠前的部分。pi和pa所指内存块的大小显然是不同的,因此我们看到pa+1并不等于pi+1。

由指针运算规则可知,pa+1的值就是pa所指空间的下一个空间的地址,所以pa+1的值就是pa的地址向后偏移一段后的地址,这个偏移量就是pa所指的数组a的大小,即12个内存单元。同样,pi+1的值是pi向后偏移4个单位(int型的大小)后的地址。

这样验证了,我们开始所说的“数组可以表示一种“数据类型””。这里我们可以理解为(&a)就是数组类型。

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-1-12 15:05:29 | 显示全部楼层
1809228982 发表于 2019-1-12 08:26
可以声明 void 的指针吗??那我不就可以想指向哪个类型的都可以了??

可以声明的,小甲鱼后面有讲到我记得
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-1-14 09:45:36 | 显示全部楼层
行客 发表于 2019-1-12 08:35
首先必须纠正一个概念,有些教科书中所说的概念“数组名就是一个指向数组首元素的指针常量”,这是一种错误 ...

谢谢,大概懂了。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2019-1-14 09:46:22 | 显示全部楼层
啊涂涂 发表于 2019-1-12 15:05
可以声明的,小甲鱼后面有讲到我记得

好的,谢谢
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-10-3 04:30

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表