【C语言】深入理解C语言指针:从入门到精通,掌握指针的关键技巧和应用

目录

一、内存

二、指针变量的定义

二、指针变量和普通变量建立关系 

三、指针变量的初始化

四、指针变量的类型

1、指针变量自身的类型

2、指针变量指向的类型

3、&和*对类型的影响

 4、指针变量指向类型决定取值宽度

 5、指针变量的指向类型 决定了+1的跨度

五、万能指针

 六、数组元素的指针变量

 七、字符串与指针

 八、指针数组

1、数值的指针数组

2、字符的指针数组 

九、数组指针

 1、一维数组指针

(1)数组首元素地址 和 数组首地址区别

(2)数组指针 本质是指针变量 保存的是数组的首地址。 

 (3)通过数组指针访问数组中的元素

2、二维数组数组名含义

3、二维数组和一维数组指针关系

 4、多维数组的物理(在内存)存储

十、指针变量作为函数的形参

十一、数组作为函数的形参 

1、一维数组作为函数的形参

2、二维数组作为函数的形参

十二、指针类型作为函数的返回值类型。

 十三、函数指针

1、函数指针的定义

2、注意项

 3、函数指针变量 使用typedef定义(了解)

十四、函数指针作为函数的参数(了解)


一、内存

在32位平台/cpu,每一个进程 拥有4G的空间。

系统为内存的每一个字节 分配一个32位(4B)的地址编号。

地址编号==指针

指针变量:本质是变量 但该变量 保存的是 内存的地址编号。

二、指针变量的定义

        *修饰指针变量p(*p),要保存,先定义,后用*p替换。

例:

1、定义一个指针变量p 其保存int类型的数的地址; int *p;

2、定义一个指针变量p 其保存的是数组首地址; int (*p)[N](N为具体数值)

3、定义一个指针变量p 其保存的是函数的入口地址; int (*p)(int,int);

4、定义一个指针变量p 其保存的是结构体变量的地址struct stu lucy;  struct stu *p;

5、定义一个指针变量p 其保存的是指针变量int *q的地址; int **p(指针的指针)

        在32位平台任何类型的指针变量 都是4字节

        在64位平台任何类型的指针变量 都是8字节

例如在x86(32位平台) 

二、指针变量和普通变量建立关系 

         *仅仅修饰p为指针变量,还需让普通变量与指针变量建立关系。

初始化时

例:int *p;

       int num;

       p=&num//p的值就是num的地址/p保存了num的地址/p指向了num

使用时,*p代表取p所保存的地址编号所对应的内容。

        例如:p等价与&num        *p则等价于num         因*p=*&num=num;

若定义一个指针变量q,其保存的是指针变量int *p的地址        **q=&p;

则*q==p==#

    **q==*p==num;

三、指针变量的初始化

指针变量 在操作之前 必须指向合法的地址空间。

1、指针变量 如果不初始化 立即操作 会出现段错误。

 int *p;

 *p;//error 段错误 

2、指针变量 如果没有指向合法的空间 建议初始化为NULL(编号0的地址),仍会出现段错误。

int *p = NULL;

*p;//error 段错误

        int *p = NULL;//等价于NULL是赋值给p ---- int *p;p = NULL;

3、将指针变量 初始化为 合法的地址

int num = 10;

int *p = &num//等价于int *p; p = &num

或: 

int num=10, *p=#

四、指针变量的类型

1、指针变量自身的类型

将指针变量名去掉,剩下的类型就是指针变量自身的类型

int *p; //p自身的类型为int *

2、指针变量指向的类型

将指针变量名和离它最近的一个* 一起去掉,剩下的类型就是指针变量指向的类型

int *p; //p指向的类型为int

3、&和*对类型的影响

int num = 10;

int *p = #

在使用中:

&和*相遇 从右往左 依次抵消‐‐‐‐*&p == p

对变量名 取地址 整体类型加一个*‐‐‐‐&num 为int *

对指针变量 取* 整体类型减一个*‐‐‐‐*p 为int

应用:一般用于 赋值语句的判断

举例:

int num=10, *p=&num, **q=&p;以下结果正确的是:ABC

A:*p = 100         B: *q = &num         C:p=&num         D:q=&num

解析:

 4、指针变量指向类型决定取值宽度

综合案例分析:

int num=0x01020304;//16进制

int *p1=#

short *p2=#//若报错,则强制转换类型:short *p2=(short *)#

char *p3=#

则:

 

 5、指针变量的指向类型 决定了+1的跨度

以综合案例继续分析,进行一下操作

1、取出0x0102的值

 2、取出0x02的值

 3、取出0x0203的值

若定义char *p1,则定位到*(short *)(p1+1) //(short *)表示强制类型转换

若定义short *p1,则定位到*(short *)((char *)p2+1)

五、万能指针

1、void不能定义普通变量

void num;//error,不知道num类型大小,不能给num开辟空间。

2、void *可以定义指针变量

void *p;//p为万能一级指针变量,p类型为void *,在32位平台void *为4B

在32位平台任意类型的指针为4B,系统知道为p开辟4B空间,所以定义成功。

3、应用:用于 函数的形参 达到算法操作多种数据类型

4、注意项

(1)对p取*之前 对p先进行指针类型强转。

(2)指针变量未初始化不能取*

(3)指针变量 初始化NULL 也不能取*

(4)指针变量不能越界访问

例1:

char ch = 'a';//1B

int *p = &ch;//4B

*p;//error 越界访问非法内存

例2: 

 int num = 10;

int *p = #

p++;

*p;//越界访问

 六、数组元素的指针变量

1、定义

 int arr[5]={10, 20,30,40,50};  //需求:定义一个指针变量 保存 数组元素的地址

int *p;

p = &arr[0]; 或者p = arr;//arr作为地址 第0个元素的地址 arr==&arr[0]

也可初始化为数组其他元素:p = &arr[3];

简写:int *p=arr;

2、数组元素的指针变量 和 数组名(作为地址) 等价       

例:

int arr[5] = {10, 20, 30, 40, 50};

int n = sizeof(arr) / sizeof(arr[0]);  

int *p = arr; 

int i=0;

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

{

        printf("%d ", *(p + i));

}

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

{

printf("%d ", *(arr + i));

}

3、*()的缩写为【】

        []是*()的缩写 []左边的值 放在+的左边 []里面的值 放在+右边 整体取*

printf("arr[1] = %d\n", arr[1]);

printf("*(arr+1) = %d\n", *(arr + 1));

printf("*(arr+1) = %d\n", *(1 + arr));

printf("1[arr] = %d\n", 1 [arr]);//不建议该写法

例题:

int arr[5] = {10, 20, 30, 40, 50};

int *p = arr+3;

则p[1]的值为:50

4、arr ==&arr[0]的原因:&arr[0] == &*(arr+0) == arr+0 == arr

5、指向同一数组元素的两个指针变量之间的 关系

两个指针变量相减  等于他们之间元素的个数

两指针变量互相赋值  让他们指向同一处

两指针变量判断相等==   判断他们是否指向同一处

两个指针变量判断大小> < >= <= !=  判断它们的位置关系

两个指针变量相加 无意义

 七、字符串与指针

1、字符数组:char str1[128]="hello world";

sizeof(str1) == 128(字节)

str1是数组 开辟128字节 存放字符串"hello world",存放在栈区/全局区

2、字符串指针变量:char *str2="hello world";//双引号描述其为字符串并取字符串第0个元素地址

sizeof(str2) == 4字节 或 8字节  str2是指针变量

在栈区/全局区 保存的是字符串常量"hello world"的首元素地址, 而”hello world“在文字常量区

 八、指针数组

1、数值的指针数组

本质为数组,数组每个元素为指针。

举例说明:

2、字符的指针数组 

九、数组指针

 1、一维数组指针

(1)数组首元素地址 和 数组首地址区别

数组首元素地址:&arr[0] == arr         +1跳过一个元素 

数组的首地址:&arr         +1跳过整个数组 

数组首元素地址编号 和数组的首地址编号 一致。但是他们的指针变量类型不同 

(2)数组指针 本质是指针变量 保存的是数组的首地址。 

举例说明:

int arr[5]={10, 20, 30,40,50};

int (*p)[5] = NULL;(初始化为NULL)或  int (*p)[5]=&arr;(合法初始化)//数组指针

 (3)通过数组指针访问数组中的元素

(1)对数组首地址取*==数组首元素地址:*p=*&arr=arr

以2中初始化数组arr为例访问arr[2]:*(arr+2)=*(*p+2)=*(*(p+0)+2)=*(p[0]+2)==p[0][2]

(2)通过强制类型转换访问

以2中初始化数组arr为例访问arr[3]:*((int *)(p+1)‐2)=arr[3]=*((int *)p+2)

总结:

int *arr[5];//指针数组 本质是数组,保存的是数组的首元素地址,每个元素为int *

int (*arr)[5];//数组指针 本质是指针变量,保存的是数组的首地址,每个元素为int

2、二维数组数组名含义

二维数组名代表的是第0行的行地址        +1跳过一个行

对行地址取*代表当前行第0列的列地址

arr[1] => *(arr+1) 第一行第0列的列地址

&arr[1] => &*(arr+1)=>arr+1 第1行的行地址

*arr+1 => 第0行第1列的列地址

arr[1]+2 =>*(arr+1)+2 =>第1行第2列的列地址

**arr ==*(*(arr+0)+0) == arr[0][0]

3、二维数组和一维数组指针关系

一维数组指针和二维数组名是完全等价  *p[4]=arr;

初始化举例:

int arr[3][4]={1,2,3,4,5,6,7,8,9,10,11,12}

int *p[4]=arr;

int arr[n]; int *p;

int arr[n][m]; int (*p)[m];

int arr[n][m][k]; int (*p)[m][k]

n维数组 和n‐1维的数组指针 等价

 4、多维数组的物理(在内存)存储

不管几维数组在物理上 都是一维存储

应用:降低算法时间复杂度 

十、指针变量作为函数的形参

单向传递 之 值传递 函数内部 不能修改外部变量的值

单向传递之地址传递 函数内部 可以修改外部变量的值

应用:需要在函数内部 修改外部变量的值时,将外部变量的地址 传递给函数

十一、数组作为函数的形参 

1、一维数组作为函数的形参

        操作(读或写)外部数组元素时,数组名传递给函数。

数组名会被编译器 优化成 指针变量,因数组名实际为首元素地址。

直接写成指针变量:void array(int *p, int n)

2、二维数组作为函数的形参

函数内部操作函数外部的二维数组,将二维数组名 传递给函数。

数组名会被编译器 优化成 一维数组指针。

 3、n维数组 作为函数形参 会被优化成 n-1维的数组指针

十二、指针类型作为函数的返回值类型。

将函数内部的合法地址 通过返回值 返回给函数外部使用。

 注:不要返回值 普通局部变量的地址

 初始化提高:

 十三、函数指针

1、函数指针的定义

函数名 代表函数的入口地址

函数指针:本质是一个指针变量 只是该变量 保存的是函数的入口地址

以两数相加为例说明:

2、注意项

函数指针变量加减无意义

对函数指针变量取* 无意义 ,*p会被编译器优化成p

函数指针变量 判断大小无意义

函数指针变量 可以赋值 

函数指针变量 可以判断相等 

 3、函数指针变量 使用typedef定义(了解)

以两数相加为例说明:

十四、函数指针作为函数的参数(了解)

以两数相乘除为例说明:

猜你喜欢

转载自blog.csdn.net/m0_75045191/article/details/131464813
今日推荐