目录

  • 1.指针
    • 1.1指针相关标识符
      • 1.1.1 取地址符号 &
      • 1.1.2 解引用符号 *
      • 1.1.3 const 符号
      • 1.1.4 算数运算(偏移概念)
    • 1.2野指针
    • 1.3多级指针
    • 1.4复杂指针
  • 2.数组
    • 2.1数组与指针(重点)
    • 2.2复杂数组
      • 2.2.1变长数组
      • 2.2.2柔性数组(零长数组)

1.指针

在C语言中有很多不同的数据类型,int、char等等,指针也是一种数据类型,也是变量,int存储整型,char存储字符类型,而指针专门存储一个数据在内存中的地址,需要完全理解指针,必须了解内存地址的概念,C语言编程最重要的是对内存的理解。

以下假设的是虚拟内存,进程的内存布局不做详细介绍了
//不同系统,指针的大小不同(在内存中占的字节数),32位系统下占4字节,大小与指针本身类型,所指向的数据,没有任何关系的!!!

//假设存在以下代码  (可以参照图看1.1)
void main(void)
{unsigned char a = 55;  //向系统申请了一块uchar类型大小的内存,内存名为a,内存中存放值55short int b = 23;   //申请了2字节大小的内存,内存名为b,小端序存放(参考博主另一篇文章),低地址字节存放值23,高地址字节存放0short int *p = &b; //申请了一块4字节大小的内存,内存中存放short int 类型数据的地址,并且是其基地址while(1);
}

假设其内存图如下:

  1. 在内存中,每一字节的内存都有其专属地址,如上图所示,运行时这些变量会有自己专属的位置;
  2. 指针指向的是一大块地址,其值只是这部分地址的基地址,即最低的地址数;
  3. 指针指向的是某个数据的基地址。

1.1指针相关标识符

1.1.1 取地址符号 &

初始化时:
int a = 100;
int *p = &a; //或者int *p = NULL; p = &a;

&为取地址的符号,上述两种初始化的理解:
int *p = &a;  //申请了一块内存,存放int类型数据的地址,这块内存(变量名)名为p,内存中的值为&aint *p = NULL; //定义了一个int 类型的指针,先指向NULL位置,或者说先存放0x0000 0000这个地址
p = &a;        //将a的地址赋给p

1.1.2 解引用符号 *

只有在定义指针时,* 符号不代表解引用,只代表指针的标识,让编译器明白此变量是个指针
例如:int a = 100;
int *p; //此时 * 只是使p代表指针
p = &a;
*p = 20; //取得该地址所指向内存的使用权,并将该内存的数值置为20

即间接访问内存,通过地址访问内存,操作里面的数据,此过程为解引用。

1.1.3 const 符号

需要了解变量和常量 — 可读可写和只读
const与指针:分清楚 常量指针(指向常量的指针) 和 指针常量(此指针是个常量不可改变他存储的地址)
int i = -1;
1.const int ic = i; 合法,定义了一个常量
2.const int *pic = ⁣ 合法,定义了一个指针指向一个常量 int const *p = const int *p
3.int *const cpi = ⁣ 非法,定义了一个 指针常量 指向一个变量,而ic是常量
4.const int *const cpic = ⁣合法,定义了一个常量指针,指向一个常量

1.1.4 算数运算(偏移概念)

指针算术运算(指针偏移量)
指针支持算术加减: 加-向后偏移,减-向前偏移
偏移量,根据指针本身的类型而定—> int *的指针+1之后,移动了一个int(4字节)
不支持直接的算术乘除(除非强转了)。
如: 都是访问数组的第二个元素
float a[3] = {1.1,1.2,5.5};
1. float mya = *(a+1); 2. float *p = a; float mya2 = *(p+1); 3. a[1]

//在下面配合数组讲更清晰

1.2野指针

野指针:一个指针所指向的地址不确定,此时对它进行赋值会产生不可估计的错误。
一般造成的错误为段错误,即操作了非法数据,非法数据是没有向系统申请的或者是丧失使用权的数据,合法例如int a; 此数据是申请了合法内存的,可以随便使用。
非法数据,例如:

  1. int *p; //只定义不初始化:是个野指针,所存的地址是乱码(随机的),必须初始化,如果直接解引用赋值,操作了个随机地址的数据。
  2. p=(int *)malloc(100); free§; //此时只是释放了内存,使这块内存不能使用,再使用就是非法的,会造成程序错误
    解决:#define NULL ((void *)0x00 ) free§; p = NULL;

1.3多级指针

既然指针是个数据类型,存储在内存中,那么指针也会有一个专属地址。
二级指针:
存储指针地址的指针

int a = 10;
int *p = &a;
int *(*pp) = &p; //申请了一块4字节内存,存储int * 类型的数据的地址(即指向int类型的指针),内存名字为pp,或者说变量名为pp。//=======================
当然还存在3、4、5、6级指针,也基本用不上

1.4复杂指针

//指针指向函数,数组等
数组指针:指向数组的指针   运算符优先级,数组指针和指针数组
int (*p)[3]     //指针:指向 int [3] 的数组类型(如int arr[3])指针数组:存储一系列指针的数组
int *arr[3]     //数组:存储了3个 int *类型的指针
(如字符串数组:char *str[3] = {"hello", "world", "hahaha"};)函数指针:指向函数的指针
int func_max(int a, int b);
int (*p1)(int, int) = func_max;
printf("%d\n", p1(a, b));指针函数:返回值为指针的函数
int *func_test(int a, int b)    //函数:形参为 int,int。返回值为 int *

2.数组

数组: 一整块连续的内存,存储了具体长度的相同类型数据。(变量的集合)
任何数组名都被视为一个指向其首元素的指针 — 指针常量特性

//数组的定义
int arr[3];
1.int 元素类型(里面所有元素的数据类型)2.arr 数组名(数组名就是首元素地址)属于指针常量
3.[3] 数组长度(数组中有3个相同的int类型数据)
(数组类型: int [3])
//如:int a[2] [3]  定义了一个数组,有两个元素,每个元素存放的都是int [3]类型的数据
定义的解释:
通俗解释:定义了一个3个int的数组
内存角度:向系统申请一块连续内存,规定了内存中每个类型为int整型,长度为3。
//使用数组中的元素:arr[0]    使用数组中第1个元素(下标范围:0 ~ 2)

2.1数组与指针(重点)

任何数组名都被视为一个指向其首元素的指针,首元素为即为数组中第一个元素:

int a[3] = {0,1,2};         //首元素为 a[0] = a[0] = *a = 0;
int a[3][2] ={6,7,2,8,3,7,} //首元素为a[0],此a[0]代表的是第一个数组,如下图中的小红圈,*a[0] = a[0][0] = 6;

在此处首元素可以从内存图中看出。
首先了解二维数组:
在C语言中,其实是不存在二维数组这种数据类型的!是为了便于理解交流的一种说法,它在本质上也是一维数组,在内存中也是以一维数组形式存在。
例如: int arr[3][2]
内存的角度:向系统申请一个连续的内存,长度为3个元素,每个元素为 int [2]类型。数组名会和最近的一个[ ]结合。
如上图所示:
int arr[3][2] = {6,7,2,8,3,7};
int (*p)[2] = arr; //定义一个指针指向int [2]类型的数据。
int ***p = &arr; //代表整个数组的地址.

  1. arr :代表首元素的地址,其首元素是上图中的小红圈,所以对其+1,指向的是数字2那个位置。
  2. arr[0]:代表第一个int [2]类型的首元素的地址,即数字6的地址,*a[0] = a[0][0], 对其+1,指向的是数字7. ---- arr[1],指向下一个int[2]数组.
  3. arr[0][0]:代表首元素,就是数字6.
  4. &arr:代表的是整个二维数组的地址,不能对其地址偏移载操作,可能出现段错误
1.数组名也可以当成指针进行运算。
2.指针也可以使用数组下标的形式,来对数组元素进行访问。
3.如指针的下标为负数,则表示指针向左偏移。
4.数组名不能进行自加操作。
5.如果只想对地址进行数值上的算术加减
必须对地址进行强制类型转换,转换为数值形式,才能进行算术加减。
printf("%p \n",arr);   printf("%lx \n",(unsigned int)arr );

2.2复杂数组

2.2.1变长数组

int i = 3;
int a[i]; //这一项在传统C语言中不可初始化,在C99标准和C++中是合法使用的,之前的要求是数组大小必须为常量。

2.2.2柔性数组(零长数组)

int a[0];
柔性数组既数组大小待定的数组,C语言中结构体的最后一个元素可以是大小未知的数组,也就是所谓的0长度.

它的主要用途是为了满足需要变长度的结构体,为了解决使用数组时内存的冗余和数组的越界问题用法
在一个结构体的最后,申明一个长度为空的数组,就可以使得这个结构体是可变长的。对于编译器来说,此时长度为0的数组并不占用空间,因为数组名本身不占空间,它只是一个偏移量,数组名这个符号本身代表了一个不可修改的地址常量,但对于这个数组的大小,我们可以进行动态分配malloc
//============  例子    //同样在C99标准和C++中合法使用struct ZeroArrTest{int a;int b;int test[0];  //不占内存大小};//  此时的 8 这个位置,可以方便的自加struct ZeroArrTest *p = (struct ZeroArrTest *)malloc(sizeof(struct ZeroArrTest) + 8);p->a = 1;p->b = 2;p->test[0] = 3;p->test[1] = 4;          //在malloc时,后面的8 为两个int类型数据,一般用sizeof(int)*2int nub = *(&p->b + 1);     //nub = 3;sizeof(struct ZeroArrTest); //8

C语言的指针\数组用图解一次搞懂相关推荐

  1. c语言动态指针数组--一种伪二维数组

    通过动态内存分配实现c语言动态指针"数组". 其实这种结构并非是一种数组,只是手动实现的一种类似数组的结构,实现类似数组的功能.应该可以说是一种伪数组结构吧. #include & ...

  2. c语言--函数指针数组

    c语言–函数指针数组 1.指针数组的概念 本质是数组,数组的每一个元素是一个指针变量: 2.指针数组的定义方法 类型说明符 *数组名[元素个数] void test() {int a=10,b=20, ...

  3. C语言基础---指针数组----初始化方式常量指针数组、指针常量数组

    文章目录 1.方式一:变量的地址放入数组中 2.方式二:字符赋值 3.方式三:字符串赋值 4.为什么指针不能修改字符串.可以修改字符数组?? 5.常量指针数组----三点注意 6.指针常量数组---- ...

  4. 【让你从0到1学会C语言】指针/数组传参以及static关键字

    作者:喜欢猫咪的的程序员 专栏:<C语言> 喜欢的话:世间因为少年的挺身而出,而更加瑰丽.                                  --<人民日报> ...

  5. C语言进阶——指针笔试题图解

    作者:敲代码の流川枫 博客主页:流川枫的博客 专栏:C语言从入门到进阶 语录:Stay hungry stay foolish 笔试题1: int main() { int a[5] = { 1, 2 ...

  6. c语言之——指针数组与数组指针

    一.指针数组和数组指针的定义 1. 指针数组:是指一个数组里面装着指针,也即指针数组是一个数组: 定义形式:int *a[10]: 说明:[]的优先级高于*,该定义形式应该理解为:int * (a[1 ...

  7. 「C语言」指针数组 数组指针 指针函数 函数指针

    相信很多人和我一样,有着这样的恐惧,那就是这四个玩意怎么也分不清,这都是啥啥啥啥呢?今天我们来具体分析一下.其实要具体了解这四个概念,只需要了解符号优先级,并关注最后两字,你就成功了一半. 符号优先级 ...

  8. 【c语言】指针数组和数组指针-解释和用法

    数组指针与指针数组: 优先级:()>[]>* 首先我们要说的一个知识点是 int *p[3]   和  int (*p)[3]的区别 int (*p)[3]   因为()优先级高,首先说明 ...

  9. C语言基础——指针数组(指向数组的指针)

    数组(Array)是一系列具有相同类型的数据的集合,每一份数据叫做一个数组元素(Element).数组中的所有元素在内存中是连续排列的,整个数组占用的是一块内存.定义数组时,一定要给出数组名,数组名可 ...

最新文章

  1. ubuntu12.04中sublime输入中文
  2. Asp.net设计模式笔记之一:理解设计模式
  3. python输入文件名读取文件_[Python] python3 文件操作:从键盘输入、打开关闭文件、读取写入文件、重命名与删除文件等...
  4. IBM Watson物联网平台的两个MQTT工具
  5. fedora java 开发环境_在Linux(Fedora)下搭建JAVA开发环境
  6. Python企业微信机器人
  7. UVA10803 Thunder Mountain【Floyd算法】
  8. getaway网关转发去前缀_蚂蚁金服 Service Mesh 大规模落地系列 - 网关篇
  9. java for语句
  10. Zigbee协议栈中文说明
  11. IRS2110S+IGBT半桥驱动调试问题记录
  12. python提取图片中的文字并生成word文档
  13. “不限量”只是幌子!流量卡到底哪家最划算?
  14. 防骗指南-QQ微信仿冒诈骗
  15. 华为服务器电源性能指标,华为服务器可服务性设计介绍-电源篇.PDF
  16. 关于H5中的Canvas API的探索
  17. 电子白板计算机培训心得,电子白板培训心得体会(精选5篇)
  18. 肖战真的没我帅!我自己写的Python颜值检测说的!
  19. Yii实战开发大型商城项目视频教程
  20. vue项目接入腾讯im

热门文章

  1. pandas 取excel 中的某一列_Python数据分析之Pandas读写外部数据文件
  2. QT开发调用FDTI ftd2xx.dll库
  3. python文本文件和二进制文件的区别_以文本格式和二进制格式打开文件,到底有什么区别?...
  4. Emlog采集插件 刀网资源采集 一键显示资源1.1
  5. Geometry理解
  6. 【面试】蜻蜓FM2020秋季校园招聘
  7. Swagger2 总结
  8. 商用在线客服软件测试报告
  9. 像李云迪那样爱 IT界那些“情”
  10. pyqt5登录界面设计——模仿qq登录界面,可登录注册(数据库)