嵌入式八股文----------数组和指针

目录

前言:

一、准备发车,基础-------吻住吻住

1.1 类型三连问 ?

1.2 指针内存关系:

1.3 指针运算:

1.4 指针自加:

1.5 指针函数:

1.6 指针数组,数组指针:

1.7 一维数组与指针函数:

1.8 指针和数组的区别:

1.9 野指针:

1.10 智能指针:

二、准备发车,稍提升----吻住吻住。

2.1 数组名num /&num的区别:

2.2 二级指针以及指针引用:

2.3 指针与引用:

​编辑

2.4 指针易混淆类型 :

2.5 sizeof数组:

三、准备发车,再提升----吻住吻住。

3.1 二维数组:

3.2 指针相减:

3.3 数组作为参数传递:

四、下车--------结语


前言:

        相较于其他语言来说,为什么C语言才是做嵌入式不二选。就是因为C语言的指针,可以针对内存进行操作区别于其他语言。接下来我们将介绍C语言中指针于数组。大家常说,指针就是数组,数组就是指针,但是两者还是有区别的。指针数组的难点在于一、二维数组的初始化,数组名的加法和&数组名的加法,指针数组和数组指针和一、二维数组的关系。

一、准备发车,基础-------吻住吻住

1.1 类型三连问 ?

什么是指针,什么是指针类型,什么又是指针所指向的类型?

1、指针其实也是个变量,只不过这个变量里面存储的是内存地址
2、指针类型指的是指针变量本身的数据类型。在C语言中,指针类型决定了指针可以指向的数据类型以及指针运算的步长。例如,int* 是一个指向整数的指针类型,char* 是一个指向字符的指针类型,double* 是一个指向双精度浮点数的指针类型等。
3、指针所指向的类型是指指针变量所指向的数据的类型。这通常与指针的类型是一致的。
1.2 指针内存关系:
 指针的值----或者叫指针所指向的内存区或地址

    ps:这个指针的类型是什么?指针指的类型是什么?该指针指向了哪里?

指针本身所占据的内存区:

    占用内存的大小,在32为系统中无论是什么类型的指针都是4个字节
1.3 指针运算:
例子1:
int *ptr;//假设指针指向的地址是0x 00 00 00 00
Ptr++;//运算之后指针指向0x 00 00 00 04

注意P+1也是加4个字节
因为int在32位系统中是32位,运算的时候是以单元为单位及一个int,即4个字节

Char *p;
P++;//地址偏移1

注意:对于一级指针的++操作偏移量要看指针指向的是什么类型
对应二级指针偏移量,对于32系统是4个字节,因为二级指针指向的类型就是指针,
所以永远是一个指针类型的大小


例子2:
#include<stdio.h> 
int main() 
{ 
char a[20]="You_are_a_girl"; 
char *p=a; char **ptr=&p; 
printf("**ptr=%c\n",**ptr); 
ptr++; 
printf("**ptr=%c\n",**ptr); 
}
在这个例子中是无法确定二级指针++之后指向的地址内容,
因为二级指针(ptr)指针指向的一级指针的地址,如果二级指针(ptr)++之后,
那么就会指向一级指针的后4个字节(对于32位操作系统来说指针类型是4字节),
至于这个地址里面是啥无从得知
1.4 指针自加:
*p++   *(p++)     (*p)++       *++p   ++*p

1.后++先不用管,即先把地址里面的值给别人,再去++,
可以是地址或值(看括号是否包住*p,是则是值++),后++有三种如下:
(*p)++   地址前后都不会变化,变化的是地址里面的值,先赋值给别人,*P再++
*(p++)和*p++一样  地址发生变化,先把*P赋值给别人,再++地址
2.前++,先++操作,可以是++地址或者值,(++符号靠近p就是地址++,靠近*P就是值++),再把值给别人
*++p   地址发生改变,先把地址++,再把地址变化后的里面的值给别人
++*p   地址不发生变化,*P的值++之后再赋值给别人


注意:指针++,到底加几个字节要根据指针指向的类型决定,指针在32系统中永远是4个字节
举例子:
Int * a;//假设指针指向的地址0
a++;//此时指针指向后移4个字节,即指向4

Char *b;//假设指针指向的地址0
b++;//此时指针指向后移1个字节,即指向1
1.5 指针函数:
首先是个指针,这个指针指向函数
应用场景:回调
例如:
int  (*fun) (int ,int );
举例:

int max(int x,int y)
{
    int z;
    if (x > y)
    {
 z=x;
    }
    else
    {
        z=y;
    }
    return z;
}
int main()
{
    int (*p) (int ,int );//定义一个函数指针
    int a,b,c;
    p=max;//把函数的地址给指针
    a=30;
    b=50;
    c=(*p)(a,b);//调用函数指针
    printf("%d\n",c);
    return 0;
}
1.6 指针数组,数组指针:
指针数组:
首先是个数组,这个数组里的元素是指针
例如:
int *p [4];
int a[4]={1,2,3,4};
p[0]=&a[0];
printf("%d\n",*p[0]);

数组指针:
首先是个指针,这个指针指向数组例如:
Int (*p) [4];//表示一个指向有4个int 元素的数组的指针,所以p+1,加的是4个int

int num[8] ={1,2,3,4,5,6,7,8};
	int (*p)[4] ;
	p = num;
	printf("%d\n",sizeof(p));//8 因为p是指针,64位系统8个字节
	printf("%p\n",p);//0x7ffe11d8a4e0 
	printf("%p\n",p+1);//0x7ffe11d8a4f0 //加的是指针指向的类型大小,
这里指针指向的是有四个4int元素的数组,所以加的是16个字节

1.7 一维数组与指针函数:
如何求某个元素?
比如求num[5]的元素
表示如下:
*(*(P+1)+1)
其实你就把数组指针看成是二维数组就行,p+1,
加的是一个一维数组的长度,*(p+1)就是二维变一维组数,
(*(P+1)+1)表示后移一个地址,*(*(P+1)+1)取出元素

指针函数:

首先是个函数,这个函数的返回值是个指针类型
例如:
Int * fun(int x,int y){}

1.8 指针和数组的区别:
1.概念
数组:是同种类型的集合
指针:里面保存的地址的值
2.赋值:
同种类型指针之间可以直接赋值,数组只能一个个元素赋值
3.存储方式:
数组是连续的一段空间,指针的存储空间是不确定的
4.修改内容不同
比如:
char p[] =”hello”,我们可以执行p[0] = ‘s’操作原因是p是数组,
可以使用下标的方式进行修改数组内容
Char * p = “hello” 执行p[0] = ‘s’是错误的,原因是p是指针,
指向字符串常量,常量是不允许修改的
5.所占字节不同
指针永远是4个字节在32为系统中,而数组是不固定的,要看数组的类型和元素个数

1.9 野指针:
野指针:是指指针指向的地址是不确定的;
原因:释放内存之后,指针没有及时置空
避免:
1.初始化置NULL
2.申请内存后判空
3.指针释放后置NULL
4.使用智能指针
一般流程如下:
Assert函数:
void assert(int expression);
expression -- 这可以是一个变量或任何 C 表达式。
如果 expression 为 TRUE,assert() 不执行任何动作。
如果 expression 为 FALSE,assert() 会在标准错误 stderr 上显示错误消息,并中止程序执行。
int *p1 = NULL; //初始化置NULL
p1 = (int *)calloc(n, sizeof(int)); //申请n个int内存空间同时初始化为0 
assert(p1 != NULL); //判空,防错设计
free(p1);  
p1 = NULL; //释放后置空
1.10 智能指针:
概念:什么是智能指针:

智能指针是个类,用来存储指针(指向动态分配对象的指针)
C++程序中使用堆内存是非常频繁的,堆内存的申请和释放由程序员手动管理,
这很容易造成堆内存的泄漏,使用智能指针能更好的管理堆内存。

智能指针如何防止内存泄露:

为了解决循环引用导致的内存泄漏,引入了weak_ptr。weak_ptr的构造函数不会修改引用计数的值
从而不会对对象的内存进行管理,类似一个普通指针,但是不会指向引用计数的共享内存,
但是可以检测所管理的对象是否已经释放,从而避免非法访问。

二、准备发车,稍提升----吻住吻住。

2.1 数组名num /&num的区别:
对于一维数组来说
num+1是偏移到下个元素,&num+1是偏移整个数组

对于二维数组来说
num+1是偏移一个一维数组,&num+1是整个数组
2.2 二级指针以及指针引用:

2.3 指针与引用:
 有了指针为什么还需要引用?
    直接的原因是为了支持运算符重载
用指针的使用经常犯得错:
1,操作空指针,
2,操作野指针,
3,不知不觉改变了指针的值,而后还以为该指针正常。
如果我们要正确的使用指针,我们不得不人为地保证这三个条件。
而引用的提出就解决了这个问题。

引用区别于指针的特性是 :
1,不存在空引用(保证不操作空指针),
2,必须初始化(保证不是野指针),
3,一个引用永远指向他初始化的那个对象(保证指针值不变)

指针的好处:

1.指针可以动态分配内存
2.在链表中可以方便修改链表的节点
3.解析字符串
4.相同类型的指针可以直接复制

指针和引用的异同,如何转换?

1.都是指针的概念,指针保存的是内存的地址,引用是某块内存的别名,
这个内存一但初始化就不能再去指向别的内存
2.两者都会占用内存区别:
1.指针是实体,而引用是别名
2.引用的本质是指针常量,指向指针的地址不可以改变,指向地址的内容可以改变
3.自增表示的意义不同,指针自增表示地址自增,引用表示值自增
4.引用必须初始化
5.引用不能为空
6.Sizeof(引用)得到的是所指向的变量的大小,sizeof(指针)的到的是指针大小
7.引用不需要解引用

指针与引用的转换:

1.指针转引用:把指针用*可以转换成对象,可以用在引用参数中
2.引用转指针:把引用类型的对象用&就可以获得指针
int a=0;
int *pa=&a;
void fun(int &va){}
调用:fun(*pa);
pa是指针,加*可以转成该指针指向的对象,此时fun的参数是一个引用的值,
pa指针指向的对象会转换成va
2.4 指针易混淆类型 :
指针常量、常量指针、指向常量的常量指针

1.const int* p //常量指针----->指针指向的地址的内容不可以改变
2.Int const *p//常量指针
3.Int * const p 指针常量-------->指针指向的地址可以不改变
4.const int *  const p //指向常量的常量指针
2.5 sizeof数组:
sizeof(数组名)和sizeof(&数组):

int Num[100];
printf("%ld\n",sizeof(Num));//400
printf("%ld\n",sizeof(&Num));//8起始就是打印int *指针大小
printf("%ld\n",sizeof(int *));//8

三、准备发车,再提升----吻住吻住。

3.1 二维数组:
高纬数组说白了就是每个元素都装了下一维数组的。
例子:int a[3][4][][5] //3个int [3][4]的一位数组

int a[3][3];
1.int a[3][3];表示是个三行三列的二维数组
2.数组名表示数组首元素的地址,即第0行第0个地址
3.a+1表示地址偏移一个一维数组的地址,即三列*int大小=3*4 = 12
4.*a 表示去二维变一维,*a就相当于一维数组的数组名,
比如 *a +1 表示第0行下标为1的元素地址,只是偏移一个Int地址
5.若要表示a[2][2]的元素 即 *(*(a+2)+2)  
//解释:
a+2表示偏移2个一维数组, 地址
*(a+2)表示去二维变一维数组    地址
*(*(a+2)+2) 表示在一维的基础上偏移两个元素  数值
6.a[0] + 1// 相当于一维数组移动一个int地址,
比如表示a[2][2]元素 
*(a[2] + 2)

对于二维字符数组的初始化
char str[][2] ={“h”,”h”};
字符二维数组的初始化不能是字符,要是字符串
3.2 指针相减:
地址相减 =  (地址a -地址b)/sizeof(指针指向的类型)
int a[3];
a[0] = 0;
a[1] = 1;
a[2] = 2;
int *p, *q;
p = a;
q = &a[2];
printf("%p\n",p);//0x7ffe80e38b0c   
printf("%p\n",q);//0x7ffe80e38b14   

printf("%d\n",q-p);//2        ( q - p) /sizeof(int )
那么就有
a[q-p]  =  a[2] = 2
3.3 数组作为参数传递:
/数组名作为参数传递时,实际上是变为指针,所以,sizeof(arr)是指针的大小
void print_array(int arr[]) {
    int n = sizeof(arr) / sizeof(arr[0]);
    for (int i = 0; i < n; i++)
        printf("%d ", arr[i]);// 1 2
}

int main() {
    int arr[] = {1, 2, 3, 4, 5};
    print_array(arr);
    return 0;
}
四、下车--------结语

以上就是指针与数组的部分了,感觉都没事要记得,就整合了一下(如有侵权部分,联系up主删除)。下车吃饭。

下一篇:C++中常用的部分:函数。

嵌入式八股文--------函数篇-CSDN博客

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值