一、指针的定义及使用
1.指针是什么?
指针是程序数据在内存中的地址,而指针变量是用来保存这些地址的变量。在同一CPU构架下,不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因数据的类型不同,所占用的存储空间长度也不同。有了指针以后,不仅可以对数据本身,也可以对存储数据的变量地址进行操作。
2.指针的类型及指向类型
从语法的角度看,你只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型。而再将*去掉,就是这个指针的指向的类型。
#include<stdio.h>
int main()
{
int *ptr;//指针的类型是 int*,指针所指向的类型是 int
char *ptr;//指针的类型是 char*,指针所指向的的类型是 char
int **ptr;//指针的类型是 int**,指针所指向的的类型是 int*
int (*ptr)[3];//指针的类型是 int(*)[3],指针所指向的的类型是 int()[3]
int *(*ptr)[4];//指针的类型是 int*(*)[4],指针所指向的的类型是 int*()[4]
}
3.关于指针的的算术运算
指针可以加上或减去一个整数。指针的这种运算的意义和通常的数值的加减运算的意义是不一样的,以指针所指向的类型的字节数为单位。
#include <stdio.h>
int main()
{
int i = 10;
char* a1 = (char*)&i;
int* a2 = &i;
printf("%p\n", &i);
printf("%p\n", a1);
printf("%p\n", a1 + 1);
printf("%p\n", a2);
printf("%p\n", a2 + 1);
return 0;
}
看来答案显而易见,指针变量与整数的运算与指针类型有关,char*类型的指针指向的是char,故跳过一个字节,int*类型的指针指向的是int,故跳过四个字节。
那么我们还有有一个疑问,指针能否可以和指针进行加减?
请看下面的代码。
#include<stdio.h>
int main()
{
int i = 0;
int* p = &i;
int* o = p + 1;
printf("%d", p - o);
return 0;
}
指针与指针的相减,是在指针类型相同的前提下成立的。它的结果为什么是-1?这是因为p+1之后,内存地址上向高地址的方向移动了四个字节,而结合指针类型是int*,然后移动的字节数要除以4(int类型占四个字节),所以结果就为-1。
两个指针不能进行加法运算,这是非法操作,因为进行加法后,得到的结果指向一个不知所向的地方,这会有安全隐患。
4.取地址操作符&和解引用操作符*
这里&是取地址操作符,*是解引用操作符。
变量会存放在内存空间,这个时候为了得到变量的地址,就需要&取地址操作符。&a 的运算结果是一个指针,指针的类型是 a 的类型加个 *,指针所指向的类型是 a 的类型,指针所指向的地址,就是 a 的地址。
&a取出的是a所占4个字节中地址较⼩的字节的地址。(int类型占四个字节)
在现实⽣活中,我们使⽤地址要找到⼀个房间,在房间⾥可以拿去或者存放物品。
C语⾔中其实也是⼀样的,我们只要拿到了地址(指针),就可以通过地址(指针)找到地址(指针)指向的对象。 *p 的结果是p 所指向的东西,这个东西有这些特点:它的类型是 p 指向的类型,它所占用的地址是 p 所指向的地址。指针的类型决定了,对指针解引用的时候有多大的权限(⼀次能操作几个字节)。
#include<stdio.h>
int main()
{
int a = 100;
int* p = &a;
*p=0;
printf("%d",a);
return 0;
}
二、指针与其他类型的关系
1.数组与指针的关系
数组名的意义:
1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
2. &数组名,这里的数组名表示整个数组,取出的是整个数组的指针。
3. 除此之外所有的数组名都表示首元素的指针。
数组指针存放的是数组的地址,能够指向数组的指针变量。
int (*p)[5];
p先和*结合,说明p是⼀个指针变量,然后指向的是⼀个大小为5个整型的数组。所以p是⼀个指针,指向⼀个数组,叫数组指针。
2.指针与函数的关系
函数指针变量应该是⽤来存放函数地址的,未来通过地址能够调⽤函数的。
int fun(char *,int);
int (*p)(char *,int);
p=fun;
int a=(*p)("abcdef",6); //通过函数指针调用函数。
除了上文的数组名,函数名也是一个指针。不过它们也有些差异,数组名前加&,是取整个数组的指针,而函数名前加&,还是取的是函数指针。
3.指针与结构体类型的关系
struct MyStruct
{
int a;
int b;
int c;
};
struct student s={10,20,30};
//结构对象s,并把s的成员初始化为10,20 和30。
struct student *ptr=&s;
//一个指向结构对象s的指针。它的类型是student*,它指向的类型是student。
int *pstr=(int*)&s;
//一个指向结构对象s的指针。但是pstr和它被指向的类型ptr 是不同的。
那么如何访问结构体的成员,结构体成员的直接访问是通过点操作符(.)访问的。而间-接访问是用操作符(->) 访问的。(结构体变量.成员名、结构体指针->成员名)
s.a//直接访问
ptr->a//间接访问
*pstr; //访问了s的成员a。
*(pstr+1); //访问了s的成员b。
*(pstr+2) //访问了s的成员c。
上面还有一个指针pstr,用这样的方式去访问是错误的。为什么是错误的?因为存放结构对象的各个成员的时候,需要遵循对齐规则,它不同于数组在内存中的连续存放,可能会存在空隙,导致非法访问。
三、指针的正确使用
1.野指针
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)指针变量在定义时如果未初始化,其值是随机的,指针变量的值是别的变量的地址,意味着指针指向了一个地址是不确定的变量,此时去解引用就是去访问了一个不确定的地址,所以结果是不可知的。
产生野指针的原因有哪些?
指针变量未初始化:任何指针变量刚被创建时不会自动成为NULL指针,它的值是随机的。
指针越界访问:当指针指向的范围超出数组的范围时,它很可能会指向随机值。
指针操作超越变量作用域:不要返回指向栈内存的指针或引用,因为栈内存在函数结束时会被释放。
2.如何规避野指针
如果明确知道指针指向哪⾥就直接赋值地址,如果不知道指针应该指向哪⾥,可以给指针赋值NULL。NULL 是C语⾔中定义的⼀个标识符常量,值是0,0也是地址,这个地址是⽆法使⽤的,读写该地址会报错。
用assert()断言来验证变量 p 是否等于 NULL 。如果确实不等于 NULL ,程序继续运⾏,否则就会终⽌运⾏,并且会给出报错。
绝不返回局部变量和局部数组的地址,任何使用与内存操作相关的函数必须指定长度信息。
四、多级指针
指针变量也是变量,它同样有地址。所以存放指针变量的指针就是二级指针。
定义指针时,一级指针有一个*,二级指针加两个*,三级指针加三个*,以此类推。
int a =100;
int *p1 = &a;//一级指针
int **p2 = &p1;//二级指针
想要获取指针指向的数据时,一级指针加一个*,二级指针加两个*,三级指针加三个*,以此类推
标签:变量,指向,int,C语言,地址,详解,类型,指针 From: https://blog.csdn.net/z6665454/article/details/136854470