文章目录
1.前言:类型解析
这里是引用:
int p; //这是一个普通的整型变量
int p; //首先从P 处开始,先与结合,所以说明P 是一个指针,然后再与int 结合,说明指针所指向的内容的类型为int 型.所以P是一个返回整型数据的指针
int p[3]; //首先从P 处开始,先与[]结合,说明P 是一个数组,然后与int 结合,说明数组里的元素是整型的,所以P 是一个由整型数据组成的数组
int p[3]; //首先从P 处开始,先与[]结合,因为其优先级比高,所以P 是一个数组,然后再与结合,说明数组里的元素是指针类型,然后再与int 结合,说明指针所指向的内容的类型是整型的,所以P 是一个由返回整型数据的指针所组成的数组
int (p)[3]; //首先从P 处开始,先与结合,说明P 是一个指针然后再与[]结合(与"()"这步可以忽略,只是为了改变优先级),说明指针所指向的内容是一个数组,然后再与int 结合,说明数组里的元素是整型的.所以P 是一个指向由整型数据组成的数组的指针
int **p; //首先从P 开始,先与结合,说是P 是一个指针,然后再与*结合,说明指针所指向的元素是指针,然后再与int 结合,说明该指针所指向的元素是整型数据.由于二级指针以及更高级的指针极少用在复杂的类型中,所以后面更复杂的类型我们就不考虑多级指针了,最多只考虑一级指针.
int p(int); //从P 处起,先与()结合,说明P 是一个函数,然后进入()里分析,说明该函数有一个整型变量的参数,然后再与外面的int 结合,说明函数的返回值是一个整型数据
Int (*p)(int); //从P 处开始,先与指针结合,说明P 是一个指针,然后与()结合,说明指针指向的是一个函数,然后再与()里的int 结合,说明函数有一个int 型的参数,再与最外层的int 结合,说明函数的返回类型是整型,所以P 是一个指向有一个整型参数且返回类型为整型的函数的指针
int (p(int))[3]; //可以先跳过,不看这个类型,过于复杂从P 开始,先与()结合,说明P 是一个函数,然后进入()里面,与int 结合,说明函数有一个整型变量参数,然后再与外面的结合,说明函数返回的是一个指针,然后到最外面一层,先与[]结合,说明返回的指针指向的是一个数组,然后再与结合,说明数组里的元素是指针,然后再与int 结合,说明指针指向的内容是整型数据.所以P 是一个参数为一个整数据且返回一个指向由整型指针变量组成的数组的指针变量的函数.
2.指针
含义:指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址。举个例子,来仔细理解一下。
(1)int*ptr;
(2)char*ptr;
(3)int**ptr;
(4)int(*ptr)[5];
(5)int*(*ptr)[2];
(1)指针类型
从语法的角度看,你只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型。这是指针本身所具有的类型。让我们看看例一中各个指针的类型:
(1)intptr;//指针的类型是int
(2)charptr;//指针的类型是char
(3)intptr;//指针的类型是int
(4)int(ptr)[5];//指针的类型是int()[5]
(5)int*(ptr)[2];//指针的类型是int(*)[2]
(2)指针指向的类型
当你通过指针来访问指针所指向的内存区时,指针所指向的类型决定了编译器将把那片内存区里的内容当做什么来看待。
从语法上看,你只须把指针声明语句中的指针名字和名字左边的指针声明符*去掉,剩下的就是指针所指向的类型。例如:
(1)int*ptr; //指针所指向的类型是int
(2)char*ptr; //指针所指向的的类型是char
(3)int**ptr; //指针所指向的的类型是int*
(4)int(*ptr)[5]; //指针所指向的的类型是int()[5]
(5)int*(*ptr)[2]; //指针所指向的的类型是int*()[2]
(3)指针的值----或者叫指针所指向的内存区或地址
以后,每遇到一个指针,都应该问问:这个指针的类型是什么?指针指的类型是什么?该指针指向了哪里?(重点注意)
上述是我仿照其他博主内容写的,这个内容写的真的是重要且通俗易懂。
3. 指针变量和地址
(1)取地址操作符(&)
变量创建的本质是:在内存中开辟一块空间。
(2)指针变量和解引用操作符(*)
a:指针变量也是一种变量,这种变量就是用来存放地址的,存放在指针变量中的值都会理解为地址。
b:C语言中,我们只要拿到了地址(指针),就可以通过地址(指针)找到地址(指针)指向对象,这里要用到解引用操作符。
eg:
#include <stdio.h>
int main(){
int a = 100;
int *pa = &a; //*p;//解引用操作(间接访问)*p等于a
*p = 0; //*pa的意思就是通过pa来存放的地址,找到指向的空间,*pa其实 就是a变量了,所以*a就是a变量;所以*pa = 0,这个操作就是把a改成0了。
4.指针变量类型的意义
(1)指针的解引用
结论:指针类型决定了指针进行解引用操作时候访问多大空间
//int *的指针类型解引用访问4个字节。
//char *的指针类型解引用访问1个字节。
(2) 指针+ -整数
char类型的指针变量+1跳过一个字节,int类型的指针变量+1跳过四个字节。
== 结论:1.指针类型决定了指针的步长,就是向/向后走一步多大的距离。(2)p+i 是跳过了i个type类型数据相当于跳过了i*sizeof(type)个字节 。 ==
5.void*指针
在指针类型中有种特殊void类型的,可以理解为无其体类型数据(或者叫泛型指针),这种指针可以接收任意类型的地址。但也有局限性,**void类型的指针不能直接进行指针的±整数和解引用运算。**
一般void*类型的指针是使用在函数参数的部分,用来接收不同类型的地址。
6.const修饰指针
(1)const修饰变量
const是常属性——不能被修改了。
(2)const修饰指针变量
一般来讲const修饰指针变量,可以放在 * 左边,也可以放在 * 右边,意义不一样。
//1.const 放在左边:限制的是*p,意思是不能通过p来改变p指向的对象内容,但p本身是可以改变的,p指向其他对象。
//1.const 放在有边:限制的是p,意思是不能修改p本身的值,但p指向的对象的内容可以被修改。
7.指针运算
指针的基本运算有三种:指针+ -整数; 指针-指针;指针的关系运算。
(1)指针 - 指针
得到指针与指针的元素个数:绝对值|指针 - 指针 |= 个数;
运算条件:两个指针指向了同一块空间。
eg:
#include <stdio.h>
int my_strlen(char * )
{
char * p = s;
while(*p !='\0')
p++;
return p - s;
}
int main()
{
printf("%d \n",my_strlen("abc"));
return 0;
}
(2)指针的关系运算
eg:
//指针的关系运算
#include <stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int *p = &arr[0];
int i = 0;
int sz = sizeof(arr)/sizeof(arr[0]);
while(p<arr+sz) //指针的⼤⼩⽐较
{
printf("%d ", *p);
p++;
}
return 0;
}
8.野指针
概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
(1)指针未初始化
#include <stdio.h>
int main()
{
int *p;//局部变量指针未初始化,默认为随机值
*p = 20;
return 0;
}
(2)指针越界访问
#include <stdio.h>
int main()
{
int arr[10] = {0};
int *p = &arr[0];
int i = 0;
for(i=0; i<=11; i++)
{
//当指针指向的范围超出数组arr的范围时,p就是野指针
*(p++) = i;
}
return 0;
}
(3)指针指向的空间释放
#include <stdio.h>
int* test()
{
int n = 100;
return &n;
}
int main()
{
int*p = test();
printf("%d\n", *p);
return 0;
(4) 如何规避野指针
a.指针初始化
#include <stdio.h>
int main()
{
int num = 10;
int*p1 = #
int*p2 = NULL;
return 0;
}
b:⼩⼼指针越界.
c:指针变量不再使⽤时,及时置NULL,指针使⽤之前检查有效性.
9. 传值调用和传址调用
传址调⽤,可以让函数和主调函数之间建⽴真正的联系,在函数内部可以修改主调函数中的变量;所以未来函数中只是需要主调函数中的变量值来实现计算,就可以采⽤传值调⽤。如果函数内部要修改主调函数中的变量的值,就需要传址调⽤。
传值调⽤:变量本⾝直接传递给了函数。
总结:
你看完我这篇文章,基本对指针的了解应该会加深。喜欢我的文章可以点赞,收藏哈!!!!!