指针和数组分析
发布日期:2021-07-01 04:22:06
浏览次数:70
分类:技术文章
本文共 4298 字,大约阅读时间需要 14 分钟。
文章目录
1 数组的访问方式
1 数组的访问方式
我们首先来看一个问题,数组名可以当作常量指针使用,那么指针是否也可以当作数组名来使用呢?
数组可以以下标的形式和以指针的形式两种方式进行访问:
下标形式VS指针形式:- 指针以固定增量在数组中移动时,效率高于下标形式。
- 指针增量为1且硬件具有硬件增量模型时,效率更高。
- 下标形式与指针形式的转换如下: 注意: 现代编译器的生成代码优化率已经大大提高,在固定增量时,下标形式的效率已经和指针形式相当;但从可毒性和代码维护的角度来看,下标形式更优。
示例代码:
#includeint main(){ int a[5] = { 0}; int* p = a; int i = 0; for(i=0; i<5; i++) { p[i] = i + 1; } for(i=0; i<5; i++) { printf("a[%d] = %d\n", i, *(a + i)); } printf("\n"); for(i=0; i<5; i++) { i[a] = i + 10; } for(i=0; i<5; i++) { printf("p[%d] = %d\n", i, p[i]); } return 0;}
2 指针和数组名的不同
2.1 指针和数组名的不同
虽然我们可以通过指针访问数组,但是我们必须知道数组名和指针仅使用方式相同:
- 数组名的本质不是指针。
- 指针的本质不是数组。
看如下代码:
// main.c#includeint main(){ extern int* a; printf("&a = %p\n", &a); printf("a = %p\n", a); printf("*a = %d\n", *a); return 0;}// extern.ca[] = { 1, 2, 3, 4, 5};/* 分析:当编译器编译到extern int* a;这句话话的时候就会把a这个标识符当作一个指针看待。指针a的地址为就是数组a标识符所在的地址,&a就是a所在的地址值,a就是里面保存的值(0x1),*a就是保存的地址值所对应的内存中的值,因此会产生段错误。*/
3 数组作为函数参数
3.1 数组参数退化的意义
首先我们要知道C语言中只会以值拷贝的方式进行参数传递。
当我们向函数传递数组时,其实有如下两种实现方案:- 将整个数组拷贝一份传入函数。
- 将数组名看作常量指针传数组首元素地址。
C语言选择的是第二种方式,是因为C语言以高效作为最初设计目标:
- 参数传递的时候如果拷贝整个数组执行效率将大大下降。
- 参数位于栈上,太大的数组拷贝将导致栈溢出。
3.2 一维数组作为函数参数
当一位数组作为函数参数时,编译器将其编译成对应的指针。
结论: 一般情况下,当定义的函数中有数组参数时,需要定义另一个参数来标示数组的大小。3.3 二维数组作为函数参数
二维数组同样存在退化的问题:
- 二维数组可以看作是一维数组。
- 二维数组中的每个元素是一维数组。
二维数组参数中第一维的参数可以省略:
void f(int a[5]) <--> void f(int a[]) <--> void f(int* a)
void f(int a[3][3]) <--> void f(int a[][3]) <--> void f(int(*a)[3])
二维数组并不是退化为二级指针,而是退化为数组指针!指针数组才是退化为二级指针!
记住如下表格!!!
关于数组作为参数,还需要注意以下几点:- C语言无法向一个函数传递任意的多维数组,对于多维数组的函数参数只有第一维是可变的。
- 必须提供除第一维之外的所有维长度
- 第一维之外的维度信息用于完成指针运算。
- N维数组的本质是一维数组,元素是N-1维的数组。
- 对于多维数组的函数参数只有第一维是可变的。
传递与访问二维数组:
#includevoid access(int a[][3], int row){ int col = sizeof(*a) / sizeof(int); int i = 0; int j = 0; printf("sizeof(a) = %d\n", sizeof(a)); // 4 printf("sizeof(*a) = %d\n", sizeof(*a)); // 12 for(i=0; i
4 数组指针和指针数组
4.1 数组指针
先来看下数组类型:
- C语言中的数组有自己特定的类型。
- 数组的类型由元素类型和数组大小共同决定。比如,int array[5]的类型为int[5]。
定义数组类型:
- C语言中通过typedef为数组类型重命名,
typedef type(name)[size]
。 - 数组类型:
typedef int(AINT5)[5];
typedef float(AFLOAT10)[10];
- 数组定义:
AINT5 iArray;
AFLOAT10 fArray;
数组指针:
- 数组指针用于指向一个数组。
- 数组名是数组首元素的起始地址,但并不是数组的起始地址。
- 通过将取地址符&作用于数组名可以得到数组的起始地址。
- 可通过数组类型定义数组指针:
ArrayType* pointer;
。 - 也可以直接定义:
type(*pointer)[n];
- pointer为数组指针变量名。
- type为指向的数组的元素类型。
- n为指向的数组的大小。
4.2 指针数组
指针数组:
- 指针数组是一个普通的数组。
- 指针数组中每个元素为一个指针。
- 指针数组的定义:
type* pArray[n];
指针数组的应用:
#include#include #define DIM(a) (sizeof(a)/sizeof(*a))int lookup_keyword(const char* key, const char* table[], const int size){ int ret = -1; int i = 0; for(i=0; i
5 指针和数组的纠缠
有时候还是会分不清指针和数组名之间的关系,主要是二维数组,一维数组倒是没什么问题。懒得写太多,就直接拿代码测试下,如下:
// CodeTest.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include#include using namespace std;int main(){ /****************************一维数组************************************/ int array1[5] = { 1, 2, 3, 4, 5 }; // 当数组名放在sizeof中代表的是整个数组 cout << sizeof(array1) << endl; //20 // 当数组名直接做右值时,其代表数组空间中第一个元素的地址 int* p1 = array1; //int* p11 = &array1; //注意:&array1的类型是int (*)[5],数组指针 cout << p1 << endl; //0x003DF998 cout << &array1 << endl; //0x003DF998 cout << p1[1] << endl; //其本质也是简单的指针运算 + 解引用 cout << array1[1] << endl; //其本质也是简单的指针运算 + 解引用 cout << *(p1 + 1) << endl; //简单的指针运算 cout << "------------------" << endl; /****************************二维数组************************************/ int array2[3][3] = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } }; // 当数组名放在sizeof中代表的是整个数组 cout << sizeof(array2) << endl; //36 // 当数组名直接做右值时,其代表数组空间中第一个元素的地址 int (*p2)[3] = array2; //二维数组也可以看成是一维的,不过每一维都是一个一维数组 //所以其数组空间的第一个元素的地址就是指向一维数组的 //int (*p22)[3] = &array2; //&array2的类型是int (*)[3][3] cout << (*p2)[0] << endl; //*p2得到其所指向的一维数组的第一个元素的指针 cout << *(*p2 + 0) << endl; cout << (*array2)[0] << endl; //[]有两个作用:1.指针运算 2.解引用 //*array2 得到二维数组中第一个元素(也就是一个一维数组)中 //的第一个int类型元素的地址,然后通过[]运算符进行地址偏移 //和解引用然后得到第一个int类型元素的值 cout << *array2[0] << endl; //array2[0]得到第一个元素(也就是一个一维数组)中第一个int类型 //的地址值,然后再通过*拿到其中存储的值 //其实array2、&array2、*array2的地址值都是一样的,只是其类型不同而已,需要注意。 system("pause"); return 0;}
参考资料:
转载地址:https://muzimin.blog.csdn.net/article/details/101345916 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
初次前来,多多关照!
[***.217.46.12]2024年04月09日 20时46分43秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
【嵌入式】Libmodbus源码分析(一)-类型和结构体
2019-04-26
【嵌入式】Libmodbus源码分析(二)-常用接口函数分析
2019-04-26
【嵌入式】Libmodbus源码分析(三)-modbus相关函数分析
2019-04-26
【嵌入式】Libmodbus源码分析(四)-RTU相关函数分析
2019-04-26
【嵌入式】Libmodbus源码分析(五)-TCP相关函数分析
2019-04-26
【嵌入式】Libmodbus之RTU模式Master端程序示例
2019-04-26
【嵌入式】Libmodbus之RTU模式Slave端程序示例
2019-04-26
【嵌入式】Libmodbus之TCP模式Master端程序示例
2019-04-26
【嵌入式】Libmodbus之TCP模式Slave端程序示例
2019-04-26
【博客】博客资源汇总
2019-04-26
【数字图像】数字图像处理博客汇总
2019-04-26
【机器视觉】机器视觉博客汇总
2019-04-26
【Qt】Qt6调用Visual Studio2019生成的动态库详解
2019-04-26
【Qt】QModbusClient类
2019-04-26
【Qt】QModbusDataUnit类
2019-04-26
【Qt】QModbusDevice类
2019-04-26
【Qt】QModbusDeviceIdentification类
2019-04-26
【Qt】QModbusExceptionResponse类
2021-06-29
【Qt】QModbusPdu类
2021-06-29
【Qt】QModbusReply类
2021-06-29