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

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

定义和使用场景

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

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

内存分配和访问方式

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

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

语法和操作

‌数组‌:数组名代表整个数组的起始地址,不能被修改。数组的每个元素可以通过下标直接访问,例如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()

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
换一批
延伸阅读

在C/C++等语言中,数组作为参数传递时会自动退化为指针,导致编译时无法保留数组的维度信息。这一特性虽简化了语法,却增加了边界检查的难度,易引发缓冲区溢出等安全风险。本文将解析指针衰减的底层机制,并探讨保持数组维度信息的...

关键字: 数组 指针

链表作为动态数据结构,其逆序操作是算法教学中的经典案例。基于结构体指针的实现方式,递归与非递归方法在空间复杂度、执行效率和代码可读性上呈现显著差异。本文以C语言单链表为例,对比分析两种实现策略的技术细节与适用场景。

关键字: 结构体 指针 链表

二级指针作为C/C++中处理动态多维数组的核心工具,能够灵活管理内存并实现高效的数据操作。本文通过实战案例解析二级指针在动态数组中的典型应用场景,结合内存管理技巧提升代码质量。

关键字: 指针 二级指针 动态数组

在C语言编程中,数组名与指针的关系常被简化为"数组名是首元素的指针",这种表述虽在特定场景下成立,却掩盖了二者在编译器层面的本质差异。

关键字: C语言编程 指针

在日常编程和算法设计中,我们经常遇到一个看似矛盾的现象:处理有序数组的速度往往显著快于处理无序数组。这一现象在多种编程语言和场景中都有体现,其背后的原因涉及计算机硬件特性、算法优化策略以及数据结构设计等多个层面。

关键字: CPU 数组

在嵌入式系统开发中,指针作为C语言的核心特性,不仅用于基础内存访问,更可实现硬件寄存器映射、数据结构优化、内存高效管理等高阶功能。本文将深入解析指针在嵌入式场景中的进阶应用技巧,助力开发者突破性能瓶颈。

关键字: C语言 指针 嵌入式系统

在C语言编程中,数组越界是一个常见但极其危险的错误。它指的是访问数组时使用了超出其定义范围的索引,可能导致程序行为异常、数据损坏甚至系统崩溃。由于C语言不提供内置的边界检查机制,这类错误往往难以察觉,却在运行时引发严重后...

关键字: C语言编程 数组

在计算机编程领域,指针是C/C++等语言中强大而灵活的工具,而指针算术则是挖掘指针潜力的关键技术之一。其中,利用偏移量进行数组操作是指针算术的典型应用,它能显著提升代码的执行效率,为高性能计算开辟新路径。

关键字: 指针算术 指针 C语言

在C语言编程中,指针是一个强大且灵活的工具,它允许直接访问和操作内存地址。然而,正是这种直接性使得指针成为了一个容易出错和难以调试的特性。本文将深入解析C语言中的指针概念,探讨其工作原理,并揭示常见的指针陷阱及其避免方法...

关键字: C语言 指针
关闭