指针详解(1)

时间:2022-11-02 14:02:15

1.含义:变量的地址叫做指针,指针就是地址。

2.指针变量

1)。不管是个什么东西,首先它是1个变量.

指针变量就是专门用来存储地址的变量,专门用来存储另外1个变量的地址的。

那么我们就说这个指针变量指向了另外1个变量,

2)。这么做的好处

访问1个变量的方式主要分为两种,

a.直接访问。

int num = 10;

num =20;// 直接访问这个num变量

b.间接访问

可以通过指针变量找到这个指针变量指向的变量,通过指针变量就可以间接的访问这个指向的变量。

3).如何声明1个专门用来存储地址的指针变量

a,我们之前学习的普通变量是如何声明的?

数据类型变量名;int num;

b,声明指针变量的语法

数据类型*指针变量的名称; int* p1;

代表声明了1个指针变量。这个指针变量的名字叫做p1.这个指针变量的类型是int* 读作int指针。

这个*代表 这个变量不是1个普通变量,而是1个专门用来存储地址的指针变量。

c.声明的时候注意。

*的位置 可以与数据类型挨在一起,也可以和指针变量名挨在一起,也可以单独写中间,

int* p1; int *p1; int * p1;

d,指针变量是用来存储另外1个变量的地址。

但是1个指针变量并不是 可以存储 任意类型的变量的地址。而是有限定的。只能存储和这个指针类型相同的 普通变量的地址。

int* p1;p1变量中只能存储int变量的地址。

double* p2;p2变量中只能存储double类型的变量的地址。

float* p3;p3变量中只能存储float变量的地址。

char* p4;p4变量中只能存储char变量的地址。

否则会出现问题!!!

3.指针变量的初始化

1),指针变量是用来存储另外1个变量的地址的。

-> 所以,我们不能直接赋值1个非地址类型的常量数据。

所以,我们也不可以直接赋值1个变量给指针。

所以,指针变量是来存储地址的,不是来存储这些东西的。


2)。指针是用来存储另外1个变量的地址。并且指针可以存储的另外1个变量的地址 这个变量的类型是限定的。


3),正确的初始化步骤

a.先取出变量的地址。

好简单,使用&取地址运算符就可以取出变量的地址。要打印地址,使用格式控制符

b,将取出来的变量的地址赋值给指针变量,

int num = 10;

int* p1 = #

这个时候,p1指针变量的值就是num变量的地址。那么我们就说p1指针指向了num变量。

4),指针变量只能存储和指针变量类型相同的普通变量的地址,否则就会出问题,起码我们现在看到在编译的时候回报1个大警告。

指针详解(1)

5),如果直接写变量名.操作的就是这个变量,你可以为这个变量赋值或者取值。

&变量名;其实这个是1个表达式。

&是1个运算符 叫做取地址运算符。这个表达式的结果 是这个变量的地址。

6),指针变量在内存中也有1个地址,因为指针变量也是1个变量嘛。

所以,我们也可以使用&符号取出指针变量的地址。

int* p1;

p1 操作的是p1这个指针变量,可以取p1的值 也可以为p1赋值。&p1 拿到的是p1的地址。

4.指针变量的使用。

1),指针变量中存储的是另外1个普通变量的地址。

这么做的好处:在于可以使用指针间接的操作指针指向的变量。

2). Pointer.建议大家在声明指针变量的时候以p开头。

3).操作变量的方式

a. 直接访问。

int num =10; num = 20;

直接操作这个变量。

b.间接访问.

当1个指针指向了另外1个变量的时候那么就可以通过指针来间接的操作这个变量,操作1个变量就两种形式:赋值、取值。

4),使用指针间接的操作指针指向的变量。

格式:*指针变量名; 代表这个指针指向的变量。

int num = 10; int* p1 = #

*p1代表p1指针指向的变量,也就是num

*p1 完全等价于 num

*p1 = 100;

将100赋值给p1指针指向的变量,也就是num变量。

这个时候可以通过这种方式间接的去 为指针指向的变量赋值或者取值。

*指针变量名; 这个表达式的结果是指针指向的变量,拿到看指针指向的变量,

就可以为指针指向的变量赋值或者取值。

指针详解(1)

5).注意.

*指针变量 就完全代表指针指向的变量。

所以通过这种方式为指针指向的变量赋值的时候 数据类型不同的时候

5.使用指针变量的时候注意的问题。

1),指针变量也是1个变量,所以可以批量声明指针变量。

int* p1,p2,p3;

这样声明的话,只有p1是int指针,p2 和 p3 是int类型的。

如果希望全部都是指针。

int* p1, *p2, *p3;

2).野指针。

我们声明1个指针变量,如果没有为其初始化。

那么这个时候这个指针变量中是有值的。垃圾值。随机数这个时候,这个指针变量就有可能指向了1块随机的空间。

这块空间: 有可能无人使用。有可能别的程序在用。有可能系统在用。

这个时候,去访问指针指向的变量的时候,就会报错。BAD ACCESS错误

像这样的指针我们就叫做野指针。

3),NULL值。

我们声明1个指针,如果不初始化这个指针,这个指针就是1个野指针就指向了1块随机的空间。那么这个时候 如果你通过这个指针访问指针指向的随机的空间的时候,是相当危险的,

如果是取值还罢了,如果赋值就相当危险,就可能会造成别的程序崩溃,

所以,我们建议大家。声明1个指针变量以后,最好为其初始化。如果你没有变量的地址初始化给这个指针变量,那你就初始化1个NULL值,

NULL值代表指针变量不指向内存中的任何地址。谁都不指,这个NULL完全等价于0.

所以,你也可以直接赋值给1个指针变量0

如果1个指针变量的值是NULL值。这个时候通过指针变量去访问指向的变量的时候100%报错。

4)。多个指针指向同一向量


#include<stdio.h>

int main()

{

int num = 10;

//先将num的地址赋给指针p1

int* p1 = &num;

//然后将p1值赋给p2

int* p2 = p1;//这时指针p1中的地址赋给了p2

//所以p1,p2都指向num变量

printf("%p\n", p1);
printf("%p\n", p2);
return 0;
}

指针详解(1)

#include<stdio.h>

int main()

{

int num = 10;

//先将num的地址赋给指针p1

int* p1 = &num;

//然后将p1值赋给p2

int* p2 = p1;//这时指针p1中的地址赋给了p2

//所以p1,p2都指向num变量

//利用指针重新给num赋值

*p1 = 200;//然而这样p2的值也是200

printf("%d\n", *p1);

printf("%d\n", *p2);

return 0;

}

指针详解(1)

6.指针作为函数的参数

1)。先回顾一下函数

当函数的参数的类型是int char double float的时候。

这个时候参数传递是值传递。在函数的内部去修改形参变量的值,对实参变量没有丝毫的影响。

当函数的参数的类型是数组的时候,这个时候参数传递是地址传递。

在函数内部修改参数数组的元素的时候,其实修改的就是实参数组的元素。

2)。.指针是一种新的数据类型。

a指针完全当然可以作为函数的参数,因为指针也是1个数据类型。

直接将指针的声明放在小括弧中,

b当我们调用1个函数的时候,如果这个函数的参数是1个指针。

那么我们就必须要为这个指针传递1个和指针类型相同的普通变量的地址。

c 这个时候,在函数的内部去访问参数指针指向的变量的时候,其实访问的就是实参变量。

3)。指针作为函数的参数,可以实现什么效果?

函数的内部可以修改实参变量的值。


4)。什么时候需要将指针作为函数的参数?

a遇到的问题。

函数只能返回1个数据。

如果函数需要返回多个数据怎么办?

b解决方案。

使用指针作为函数的参数,让调用者将自己的变量的地址传递到函数的内部函数的内部通过指针就可以修改实参变量的值。


c当函数需要返回多个数据的时候就可以使用指针作为函数的参数。

例子:

#include<stdio.h>

#include <cstdint>

//声明函数

void puanDuanZuiDaZhi(int arr[],int len,int* max,int* min);

//利用指针定义需要被返回的值

int main()

{

int max = 0, min = 0;

int arr[] = { 10,20,30,16,56,1651,213,1,3,543,1,34,321,321354,22203,98};

//调用函数将max,min的地址取出来

//所以现在指针指向的是max,min,将指针在void函数中赋值则main函数中的max,min会被重新赋值

puanDuanZuiDaZhi(arr, sizeof(arr) / sizeof(arr[0]), &max, &min);

printf("%d\n%d",max,min);

return 0;

}


//函数体

void puanDuanZuiDaZhi(int arr[], int len, int* Max, int* Min)

{

int max = INT32_MIN;

int min = INT32_MAX;

//在函数中定义最大和最小值

for (int i = 0; i < len; i++)

{

if (arr[i] > max)

{

max = arr[i];

}

if (arr[i] < min)

{

min = arr[i];

}

}

//然后将max和min的值赋给指针

* Max = max;

* Min = min;

}

7.指针要分类型

1).指针分好多类型.

int* double* float* char*

指针变量是1个变量,无论指针是什么类型,在内存中都是占据8个字节,

既然指针都是占据8个字节,为什么指针还要分类型呢?

2).通过指针间接的操作指针指向的变量的方式。

int num =10;

int* p1 = &num;

p1指针变量中存储的是num变量的地址 也就是num变量的低字节的地址。

通过这个p1指针其实只能找到这个地址的字节。

这个时候,通过p1指针找到这个字节 操作的时候,是操作多个字节空间呢?

操作多少个字节是根据指针的类型来决定的。

指针变量的类型决定了 通过这个指针找到字节以后连续操作多少个字节空间。

指针是int* 那么就连续操作4字节

指针是double* 那么就连续操作8字节

指针是float* 那么就连续操作4字节

指针是char* 那么就连续操作1字节

所以,指针的类型如果不和指向的变量的类型相同话。那么通过指针就无法正确的操作指向的变量

所以,指针变量一定要指向1个和自己类型相同的普通变量才可以。

举例说明

int num=10;//int型占4个字节
int char* p1=&num;//用char型指针,那接下来连续使用一个字节
* p1=300;//将300转换为二进制输送到那一个字节和剩下的三个字节组成一个新的int型数据

8.多级指针

A  有哪些级数的指针

一级指针

1个指针变量中存储的是1个普通变量的地址,像这样的指针,我们就叫做一级指针,

二级指针

1个指针变量中存储的是1个一级指针变量的地址,像这样的指针,我们就叫做二级指针。

三级指针

1个指针变量中存储的是1个二级指针变量的地址,像这样的指针,我们就叫做三级指针。

B  二级指针.

1)。只能存储一级指针变量的地址。这样的指针叫做二级指针。

2)。声明二级指针的语法:

a.声明一级指针:  数据类型*指针名;

b.声明二级指针:  数据类型** 指针名;

c.声明三级指针:  数据类型***指针名;

3)。一级指针只能存储普通变量的地址。

二级指针只能存储一级指针变量的地址三级指针只能存储二级指针变量的地址。

n级指针只能存储n-1级指针变量的地址。

否则就会出问题。

5).初始化.

使用取地址运算符。拿到对应的指针变量的地址赋值给指针变量就可以了。

int num = 10;

int* p1 =&num;//p1是1个一级指针,存储的是num变量的地址。

int** p2 =&p1;//p2是1个二级指针,存储的是一级指针变量p1的地址。

int*** p3 =&p2;//p3是1个三级指针,存储的是二级指针变量p2的地址。

举例说明

#include<stdio.h>

int main()

{

int num = 10;
int* p1 = &num;
int** p2 = &p1;
//1.打印num变量的地址。
printf("num变量的地址:%p\n", &num);//num变量的地址。
//2.打印p1变量的值。
printf("p1指针变量的值是:%p\n", p1);//和上面的输出一样。
//3.打印p1指针变量的地址。
printf("p1指针变量的地址是:%p\n", & p1);//输出p1指针变量的地址
//4.打印p2变量的值。
printf("p2指针变量的值是:%p\n", p2);//和上面一样。
//5.打印p2的地址。
printf("p2指针变量的地址是:%p\n", &p2);//p2的地址,

}

C二级指针的使用

3. 二级指针的使用。

1),使用指针的时候,有几颗星就拐几个弯,

*指针变量名,代表这个指针指向的变量。

**指针变量名。这个指针至少要是1个二级指针,代表这个指针指向的指针指向的变量。

*p1 代表p1指针指向的变量.

**p1代表p1指针指向的指针指向的变量。

***p1代表p1指针指向的指针指向指针指向的变量。

#include<stdio.h>

int main()

{

int num = 20;

int* p1 = &num;

int** p2 = &p1;

int*** p3 = &p2;

printf("%d\n", *p1);//指向num的值

printf("%d\n", **p2);//先指向p1再指向num

printf("%d\n", ***p3);//先指向p2然后指向p1最后指向num

printf("%p\n", p1); //取p1的地址

printf("%p\n", *p2);//取p2的地址

printf("%p\n", **p3);//取p3的地址

//但最后都指向num的地址

return 0;

}

指针详解(1)

9.指针与整数的加减法

指针与整数进行加减运算。

比如指针 +1;

并不是在指针地址的基础之上加1个字节的地址。而是在这个指针地址的基础之上加1个单位变量占用的字节数。

如果指针的类型是int* 那么这个时候1代表4个字节地址。

如果指针的类型是double* 那么这个时候1代表8个字节地址。

如果指针的类型是float* 那么这个时候1代表4个字节地址。

如果指针的类型是char* 那么这个时候1代表1个字节地址。

指针详解(1)

10.指针和数组

A.数组中的元素的地址也可以用指针表示

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

int *p1 = &arr[0];表示p1指针指向了数组中的第0个元素。

数组的第0个元素的地址就是数组的地址。数组名就代表数组的地址。

B.运用指针遍历数组中的元素(利用指针与整数加减)

1)。普通遍历

#include<stdio.h>

int main()

{

int arr[] = { 12,212,151,215,12121,231,313,132,31,1223 };

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

for (int i = 0; i < len; i++)

{

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

}

return 0;

}

2).运用指针遍历

a.

#include<stdio.h>//使用指针遍历数组中的元素
int main()
{ int arr[] = { 12,212,151,215,12121,231,313,132,31,1223 };
int len = sizeof(arr) / sizeof(arr[0]);
int* p1 = arr;//将arr用指针表示
for (int i = 0; i < len; i++)
{
printf("%d\n", *(p1 + i));//利用指针加法将其的地址每次加四个字节
}
return 0;
}

b.

#include<stdio.h>

int main()

{

int arr[] = { 12,212,151,215,12121,231,313,132,31,1223 };

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

for (int i = 0; i < len; i++)

{

printf("%d\n", * (arr+i));//数组本身就是一个指针

}

return 0;

}

c.

#include<stdio.h>

int main()

{

int arr[] = { 12,212,151,215,12121,231,313,132,31,1223 };

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

int* p1 = arr;//将arr用指针表示

for (int i = 0; i < len; i++)

{

printf("%d\n", *(p1++));//利用指针加法将其的地址每次加四个字节

//使用这种方法指针p1的地址会改变不在跟原来一样。

}

return 0;

}
//注意注意的地方,每次循环.p1的值都会变化。
最后1次执行完毕之后,p1指针指向外面去了。 p1就不再执行数组中的任何元素了。

d.注意:数组名代表数组的地址。而数组一旦创建,数组的地址就确定了,不能改变所以。我们不能为数组名赋值。不能修改数组名的值。但是可以使用数组名的值。

结论:无法修改数组名的值。不能为数组名赋值。任何值都不可以。

因为数组名代表数组的地址,而数组一旦创建数组的地址是无法改变的。所以你不能给他赋值,数组名是1个常量.1个地址常量

11.当数组作为函数的参数时

在声明这个参数数组的时候。它并不是去创建1个数组而是去创建1个用来存储地址的指针变量,

如果我们为函数写了1个数组作为参数。其实啊,编译器在编译的时候,已经把这个数组换成了指针。

所以。以后,我们的函数如果带了1个参数 这个参数是1个数组。

建议,不要写数组了,直接写1个指向数组的第0个元素的指针,再传入长度

12.索引的本质。

1),指针变量后面可以使用中括弧的在中括弧中写上下标来访问数据。

2),p1[n];前提是p1是1个指针变量。

完全等价于 *(p1+n);

3),只要是指针都可以使用中括弧下标,就相当于是去访问指针指向的变量。

操作数组我们虽然使用的中括弧下标来操作,实际上内部仍然使用的指针来操作

#include<stdio.h>

int main()

{

int arr[4] = { 1,15,121,51};

//为数组中的元素赋值

arr[0] = 12;//* (arr+0)

arr[1] = 56;//* (arr+1)

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

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

return 0;

}

13.指针数组

如果1个数组是用来存储指针类型的数据的话。那么这个数组就叫做存储指针的数组。

格式:

元素类型数组名[数组长度];

int* arr[3];

这个arr数组的元素的类型是int*,是int指针,所以这个数组可以存储int指针数据。最多存储3个

#include<stdio.h>

int main()

{

int arr[] = { 1,2,3 };

//arr数组中的第零个元素指针parr[0]中储存的是arr[0]的地址。

int* parr[] = { arr,&arr[1],&arr[2] };

*(parr[0]) = 250;//将其赋值,则会改变原来数组中的值。

printf("%d", arr[0]);
return0;
}

14.指针与指针之间的减法

a.指针与指针之间可以做减法运算。

结果是1个long类型的数据。

结果的意义:代表两个指针指向的变量之间相差多少个单位变量。

绝大多数情况下,我们用在判断数组的两个元素之间相差多少个元素,


b.如果参与减法运算的两个指针不指向同1个数组,结果就有问题。

 唯一的意义:就是用在数组中,判断两个元素之间相差多少个元素。

d.指针与指针之间只能做减法运算。

15.指针与指针之间可以做比较运算。

a.

>

>=

<

<=

==

!=

都可以作用于两个指针之间。

b.为变量分配字节空间的时候,

从高地址向低地址分配的嘛。

>= 它可以判断两个指针指向的变量的地址 谁在高字节 谁在低字节。

c,也可以使用==、!= 来判断两个指针指向的地址是不是为同1个地址,