变量的访问方式:
1:直接访问:直接使用变量名进行访问
2:间接访问:通过指针来访问
内存地址与指针:
为了有效访问到内存内的每个单元,就给内存单元进行了编号,这就是内存单元的地址。因为每个内存单元都有地址,所以变量存储的数据也是有地址的。
通过地址能找到所需的变量单元,可以说,地址指向该变量单元,将地址形象的称为指针,即:
变量:命名的内存空间,用于存放各种类型的数据
变量名:变量名是给内存空间取一个容易记忆的名字
变量值:在变量单元中存放的数据值
变量的地址:变量所用的内存空间的地址,即指针(如果要访问该变量,就访问首地址)
如图:

指针变量:一个变量专门用来存放另一变量在内存中数据的地址(即指针),则他被称为“指针变量”,我们可以通过访问指针变量达到访问内存中另一个数据的目的(有时为了方便直接将指针变量称为指针)
指针变量的定义:
一般格式:
数据类型 * 指针变量名 [ = 初始地址值];
数据类型是指,指针变量所指向变量数据的数据类型。可以是int,char,float等基本类型,也可以是数据等构造类型
字符*用于告知系统这里定义的是一个指针变量
举例1:
int *p;(这是一个指针变量,用于存储int型的整数在内存空间中数据的地址)
注意:
指针变量的名字是p,不是*p
指针变量中只能存放地址,不要将任何其他非地址类型的数据赋值给一个指针变量
举例2:同一行声明两个指针变量
int * a, * b;
举例3:一个指针指向的可能还是指针,这时就要用两个*号表示
int ** foo;
指针的应用场景:
场景1:使用指针访问变量或数组的元素
场景2:应用在数据结构中
指针的运算:
指针的运算都是针对内存中的地址来实现的。
取址运算符:&
作用:取出指定变量在内存中的地址。
格式:&变量
举例:
#include <stdio.h>
int main() {
int num = 10;
printf("%d\n",num);//输出变量的值
printf("%p",&num);//输出变量的内存地址
return 0;
}
说明:
在输出取址运算获得的地址时,需要使用%p作为输出格式符
这里的num有4个字节,每个字节都有地址,取出的是第一个字节的地址
指针变量的赋值:
1.指针变量中只能存放地址,不要将其他任何非地址类型的数据赋给指针变量
2.一个指针变量只能指向同一个类型的变量,不能抛开类型随意赋值,比如:
char*类型的指针是为了存放char类型变量的地址
int*类型的指针是为了存放int类型变量的地址等等
3.在没有指针变量赋值时,指针变量的值时不确定的。通常给指针变量赋初始值为0,并把值为0的指针变量称为空指针变量
举例:通过指针变量修改指向的内存中的数据
#include <stdio.h>
int main() {
int num = 10,*ptr;
ptr = #
printf("%d\n",num);
scanf("%d",ptr);//等价于scanf("%d",&num)
printf("%d\n",num);
return 0;
}
取值运算符:*
其作用与&相反,根据一个给定的内存地址取出该地址对应的变量,也称为解引用符号。
格式如下:
*指针表达式
举例1:
#include <stdio.h>
int main() {
int a = 2024;
int *p;//这里的*是告诉系统这是一个指针变量
p = &a;
printf("%p\n",&a);
printf("%p\n",p);
printf("%d",*p);//这里的*p与上面的*p不同,这里的*是运算符号
return 0;
}
举例2:
#include <stdio.h>
int main() {
int num = 10;
printf("%d\n",num);//输出num的值
printf("%p\n",&num);//输出num的地址
printf("%d",*&num);//通过num的地址读取num中的数据,输出10
return 0;
}
*和&互为逆运算,下面的表达式总是成立:
i ==*(&i)
举例3:通过指针变量修改指向内存地址上的值
#include <stdio.h>
int main() {
int num = 10;
int *p = #
printf("%d\n",num);
*p = 20;
printf("%d",num);
return 0;
}
其他类型的变量也适用
举例4:
//定义指针变量p1,p2
//默认各自指向整数a,b。a,b从键盘输入。
//设计程序,使得p1指向其中较大的值,p2指向其中较小的值
#include <stdio.h>
int main() {
int *p1,*p2;
int a,b;
p1 = &a;
p2 = &b;
printf("输入a和b的值:");
scanf("%d%d",&a,&b);
if (*p1 < *p2) {
int *temp = p1;
p1 = p2;
p2 = temp;
}
printf("%d\n",*p1);
printf("%d",*p2);
return 0;
}
指针的常用运算:
指针可以与整数加减,自增自减,同类指针相加减,但规则不是整数运算的规则
指针与整数值的加减运算
格式:指针+(-)整数
含义:指针与整数值的加减运算,表示指针所指向的内存地址的移动(加,向后移动;减,向前移动),指针移动的单位,与指针指向的数据类型有关。数据类型占据多少字节,每单位就移动多少字节。

说明:s +1表示指针向内存地址
的高位移动一个单位,而一个单位的short类型占据两个字符,所以相当于向高位移动两个字节。
注意:只有指向连续的同类型数据区域,指针加,减才有实际意义
举例:

举例2:通过指针访问数组
#include<stdio.h>
int main() {
int arr[5] = {10,20,30,40,50};
int i = 0;
int *p = &arr[0];
for (i = 0; i < 5; i++) {
printf("%d\n",*(p + i));
}
return 0;
}
指针的自增,自减运算:
指针型变量也可以进行自增,自减的运算
格式:p++,p--,++p,--p
对于数组来说:
当对指针进行++时,指针会按照它指向的数据类型字节数大小增加,比如:int*指针,每++一次,就增加4个字节
--,同理
拓展:
*(p++)先取*p值,再使p自增1
*(++p)先使p自增1个单位字节长度,再取*p
拓展:
//当p当前指向a数组中第i个元素a[i],则:
//*(p--),相当于a[i--],先对p进行*运算,再使p自减
//*(++p),相当于a[++i],先使p自加,在进行*运算
同类指针相减运算:
格式:指针 - 指针
同类型的指针允许进行减法运算,返回他们之间的距离,即 相隔多少个
数据单位(注意:非字节数)
高位地址减去低位地址,返回的是正值;低位减去高位返回的是负值
返回的值属于ptrdiff_t类型,这是一个带符号的整数类型别名,具体类型根据系统不同而不同。这个类型的原型定义在头文件stddef.h里面
举例:
#include <stdio.h>
int main() {
int arr[] = {1,2,3,4,5};
int *p1 = &arr[0];
int *p2 = &arr[3];
printf("p1的地址为:%d\n",p1);//当前的地址是1287649088
printf("p2的地址为:%d\n",p2);//当前的地址是1287649100
printf("p2 - p1 = %d",p2 - p1);//3,等同于俩地址互减之后/4(int类型所占用的字节数)
return 0;
}
体会:两个指针相减,通常两个指针都是指向同一数组中的元素才有意义,结果是两个地址之差除以数组元素的长度。不相干的两个变量的地址,通常没有相减的必要
注意:同类指针进行加法是没有意义的,也是非法的
同类型指针间的比较运算:
指针间的比较运算,比如:==,!=,<,<=,>,>=等比较的是各自之间内存的大小,返回值通常是1(true)和0(false)
野指针:
定义:
指针指向的位置是不可知的(随机性,不正确,没有明确限制),实际开发中要避免野指针
野指针的成因:
1.指针使用前未初始化
指针变量在定义时,如果未初始化,其值就是随机的,此时操作指针就是去访问一个不确定的地址,所以结果是不可知的,此时指针就是野指针
举例:
#include <stdio.h>
int main() {
int *p;
printf("%d",*p);
return 0;
}
在没有给指针变量赋初始值时,后续一系列的操作(包括修改指向内存的数据)也是错误的
2.指针越界访问
举例:
int main() {
int arr[10] = {0};
int *p = arr;
for (int i = 0; i <= 10; p++) {
*p = i;
}
return 0;
}
当i = 10时,此时*p访问的空间内存不在有效范围内,此时*p就属于非法形式的访问,为野指针
3.指针指向已经释放的空间
有关函数,还没学,先放着
野指针的避免:
1.指针初始化
定义指针的时候,如果没有确切的地址赋值,为指针变量赋值NULL值是一个良好的习惯。
举例:
#include <stdio.h>
int main() {
int *p = NULL;
return 0;
}
赋值NULL的指针被称为空指针,NULL指针是一个定义在标准库stdio.h中值为0的常量
后面如果用到指针的话,再让指针指向具有实际意义的地址,然后通过指针的取值符号(*)改变其指向的内容。
练习:
#include <stdio.h>
int main() {
int *p =NULL;
int b = 8;
p = &b;//一定要有显示初始化这一步
*p = 100;
printf("%d\n",*p);//输出为100
printf("%d",b);//输出为100
return 0;
}
2.小心指针越界
3.避免返回局部变量的地址
4.指针指向空间释放,及时置NULL(先放着)
5.指针使用前检查有效性
二级指针(多重指针):一个指针p1记录一个变量的地址,由于指针p1也是变量,那么也会有地址,那p1的地址就可以用另一个指针变量p2表示,则p2就是二级指针。简单理解就是二级指针就是指向指针的指针格式:数据类型 **指针名;举例:
int main() {
int a = 10;
int *pa = &a;//pa是一级指针
int **ppa = &pa;//ppa是二级指针,类型为int **
return 0;
}
练习:使用malloc函数创建二维数组
#include <stdio.h>
#include <malloc.h>
int main() {
//使用malloc()函数创建二维数组
//1.从键盘获取row和column
int row, column;
printf("第一维长度:");
scanf("%d", &row);
printf("第二维长度:");
scanf("%d", &column);
//2.初始化外层数组
int **arr = (int **)malloc(row*sizeof(int *));
//通过循环赋值
for (int i = 0; i < row; i++) {
arr[i] = (int *)malloc(column*sizeof(int));
for (int j = 0; j < column; j++) {
arr[i][j] = 1;
printf("%d",arr[i][j]);
}printf("\n");
}
//后续可以使用此数组
//使用完以后回收此数组
free(arr);
return 0;
}

被折叠的 条评论
为什么被折叠?



