盘点数组和指针的主要区别

2025-04-20

‌数组和指针在定义、使用场景、内存分配和访问方式等方面存在显著区别。‌

定义和使用场景

‌数组‌:数组是一种数据结构,用于存储多个相同类型的数据元素。数组在编译时就已经确定了内存空间的大小和位置,且一旦创建,其大小和位置不能改变。‌

‌指针‌:指针是一个变量,存储的是另一个变量的内存地址。指针可以在运行时动态地指向不同的内存位置,具有更高的灵活性。

内存分配和访问方式

‌数组‌:数组在内存中是连续存放的,访问数组元素通过下标直接访问。数组的内存空间在编译时或运行时确定,且一旦分配,其大小和位置不能改变。‌

指针‌:指针本身是一个变量,可以指向任何类型的内存地址。通过解引用操作符*来访问指针所指向的内存中的值。指针可以动态地指向不同的内存位置,灵活性更高。

语法和操作

‌数组‌:数组名代表整个数组的起始地址,不能被修改。数组的每个元素可以通过下标直接访问,例如array[i]。数组的大小在编译时确定,不能动态调整。‌

‌指针‌:指针需要显式地赋值才能指向具体的内存位置。通过解引用操作符*来访问指针指向的值,例如*p。指针可以动态地指向不同的内存位置,灵活性更高。

指针数组:首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身的大小决定,每一个元素都是一个指针,它是“储存指针的数组”的简称。

数组指针:首先它是一个指针,它指向一个数组,至于它指向的数组占多少字节,具体要看数组大小。它是“指向数组的指针”的简称。

分辨方法:最简单的辨别方式就是看函数名前面的指针*号有没有被括号()包含,如果被包含就是数组指针,反之则是指针数组。

首先,数组是一种固定大小的数据结构,它可以存储一系列同类型的数据。数组的大小在声明时就已经确定,不能在运行时改变。数组的元素可以通过下标来访问,下标从0开始,最大下标为数组大小减1。数组的元素在内存中是连续存储的,因此数组的访问速度较快。数组的声明方式为:类型
数组名[大小]。

其次,指针是一种变量,它存储的是一个内存地址。指针可以指向任何数据类型,包括数组。指针的大小与系统位数有关,一般是4字节或8字节。指针的值可以被修改,指向不同的内存地址。通过指针可以访问其所指向的变量或数组元素。指针可以通过加减运算来访问它所指向的数组元素,但需要注意指针的类型,以及要访问的数组元素的类型。指针的声明方式为:类型
*指针变量名。

数组和指针在使用上还有一些区别。首先,数组名代表整个数组,可以用来初始化其他数组,但数组名不能被赋值或自增。其次,数组在函数调用时,传递的是数组的地址,而不是整个数组。指针可以被赋值或自增。指针还可以用来动态分配内存空间,这是数组无法做到的。指针也可以用来实现复杂的数据结构,如链表、树等。

一、定义及声明方式

数组

数组是一种数据结构,用于存储固定大小的同类型元素集合。

数组的声明方式为 type arrayName[size]; 例如:int myArray[5]; 表示一个包含5个整数的数组。

指针

指针是一个变量,其值为另一个变量的内存地址。

指针的声明方式为 type *pointerName; 例如:int *myPointer; 表示一个指向整数的指针。

二、内存分配

数组

数组的内存是连续分配的,即数组中的每个元素都按顺序存储在内存中。

数组的大小在编译时确定,不能在运行时改变。

指针

指针本身只占用一定的内存空间(通常是4或8字节,取决于系统架构),用于存储其他变量的地址。

指针可以动态地指向不同的内存位置,这使得它更灵活,但也增加了出错的风险。

三、访问方式

数组

通过索引访问数组元素,例如:myArray[0] 表示访问数组的第一个元素。

数组名在大多数情况下表示数组首元素的地址,但它是一个常量表达式,不能作为左值进行赋值操作。

指针

通过解引用操作符 * 来访问指针所指向的值,例如:*myPointer 表示访问指针 myPointer 所指向的整数。

可以通过指针算术运算来访问相邻的内存位置,例如:*(myPointer + 1) 表示访问指针 myPointer 后面的下一个整数。

四、函数参数传递

数组

当数组作为函数参数传递时,实际上传递的是数组首元素的地址。因此,函数内部对数组元素的修改会影响到原数组。

在函数参数中,通常不需要指定数组的大小,但在某些情况下为了代码清晰和安全,会加上数组大小的信息(虽然这只是建议性的)。

指针

指针可以直接作为函数参数传递,允许函数直接操作指针所指向的数据。

函数可以通过指针参数返回多个值,或者修改调用者提供的数据。

五、生命周期和作用域

数组

数组的生命周期依赖于其声明的位置和范围。全局数组在整个程序运行期间都存在,而局部数组则在函数执行完毕后销毁。

数组的作用域也由其声明的位置决定,可以是全局作用域或局部作用域。

指针

指针的生命周期和作用域同样依赖于其声明的位置和范围。但是,指针可以指向不同生命周期和作用域的变量,这增加了其灵活性但也带来了复杂性。

需要特别注意野指针(未初始化或已释放但仍被使用的指针)和悬挂指针(指向已释放内存的指针)的问题。

(1)数组指针(行指针)

定义 int (*p)[n];

()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。

如要将二维数组赋给一指针,应这样赋值:

int a[3][4];

int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。

p=a; //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]

p++; //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]

所以数组指针也称指向一维数组的指针,亦称行指针。

(2)指针数组

定义 int *p[n];

[]优先级高,[]先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1时,则p指向下一个数组元素,错误赋值方法:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]...p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样
*p=a; 这里*p表示指针数组第一个元素的值,a的首地址的值。如要将二维数组赋给一指针数组:

int *p[3];

int a[3][4];

p++; //该语句表示p数组指向下一个数组元素。注:此数组每一个元素都是一个指针

for(i=0;i<3;i++)

p[i]=a[i]

这里int *p[3] 表示一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2]所以要分别赋值。

数组指针只是一个指针变量,是C语言中专门用来指向二维数组的,它占有内存中一个指针的存储空间。指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间。当指针数组用来指向二维数组时,其引用和用数组名引用都是一样的。

比如要表示数组中i行j列一个元素:

*(p[i]+j)、*(*(p+i)+j)、(*(p+i))[j]、p[i][j]

2函数指针和指针函数的区别

最简单的辨别方式就是看函数名前面的指针*号有没有被括号()包含,如果被包含就是函数指针,反之则是指针函数。

(1)指针函数

指带指针的函数,即本质是一个函数,函数返回类型是某一类型的指针。首先它是一个函数,只不过这个函数的返回值是一个地址值。函数返回值必须用同类型的指针变量来接受,也就是说,指针函数一定有函数返回值,而且,在主调函数中,函数返回值必须赋给同类型的指针变量。

当一个函数声明其返回值为一个指针时,实际上就是返回一个地址给调用函数,以用于需要指针或地址的表达式中。

格式:

类型说明符 * 函数名(参数)

由于返回的是一个地址,所以类型说明符一般都是int。

例如:

int *GetFlag();

int * lzq(int,int);

float *fun();

float *p;

p = fun(a);

(2)函数指针

指向函数的指针变量,即本质是一个指针变量。

int (*f) (int x); /*声明一个函数指针 */

f=func; /* 将func函数的首地址赋给指针f */

指向函数的指针包含了函数的地址的入口地址,可以通过它来调用函数。声明格式如下:

类型说明符 (*函数名) (参数)

其实这里不能称为函数名,应该叫做指针的变量名。这个特殊的指针指向一个返回整型值的函数。指针的声明必须和它指向函数的声明保持一致。

指针名和指针运算符外面的括号改变了默认的运算符优先级。如果没有圆括号,就变成了一个返回整型指针的函数的原型声明。

例如:

void (*fptr)();

把函数的地址赋值给函数指针,可以采用下面两种形式:

fptr=&Function;

fptr=Function;

取地址运算符&不是必需的,因为单单一个函数标识符就标号表示了它的地址,如果是函数调用,还必须包含一个圆括号括起来的参数表。

可以采用如下两种方式来通过指针调用函数:

x=(*fptr)();

x=fptr()

文章推荐

相关推荐