c语言深入理解指针(1)

本文详细介绍了指针的概念,包括内存单元、地址、操作符、指针变量的创建和使用、不同类型指针(如int*、char*、void*等)、常量指针、指针运算、野指针的成因与规避、以及assert断言的作用。
摘要由CSDN通过智能技术生成

1.什么是指针

指针  == 地址  ==  内存单元的编号

注1.内存被划分为一个个单元,一个单元大小是1字节。

    2.每个内存单元都给一个编号

 2.指针有关操作符

1.取地址操作符 ‘&’:

找到起始位置地址

int a = 0;
int* p = &a;

2.解引用操作符 ‘*’:

解引用指针变量,找到指针指向地址所存储的数据

如下,利用指针给整形变量a赋值1

int a = 0;
int* p = &a;
*p = 1;

3.指针变量

指针变量即为存放地址(指针)的变量

指针变量的创建:

int * p = NULL;

指针变量的使用:通过指针变量p间接给变量赋值、进行运算

int a=1;
int *p = &a;
*p = 2;
*p = *p + 1;

3.1指针变量大小

指针变量大小是多少一般取决于一个地址的存放需要多大空间

-32位平台,一个地址占32个bit位(4个字节)

-64位平台,一个地址占64个bit位(8个字节)

-指针变量大小与类型无关,指针变量在相同平台下大小相同

3.2指针变量类型

1.指向数据的指针

 *说明是指针变量,int说明指针指向的对象类型,

int* p    // 即int* 为指向int对象的指针类型
char* p    //char* 即为指向char对象的指针类型
float* p    //float* 即为指向float对象的指针类型

以此类推可推出指向各种数据类型的指针如long*     、   double*等        

2.指向数组的指针:数组指针

int (*p)[10]    //p是指针,指向的是数组,数组有10个元素每个元素类型是int
                 //p先与*结合说明p是指针,再与int   [10]结合说明指向的是数组

/*错误数组指针写法*/
int * p[10]      //[]运算符的优先级大于*运算符的优先级,故p会先与[]结合说明p是数组
                 //再与*结合说数组的元素为int*,故p实质为指针数组

可将 int(* )[  ] 理解为指针变量的类型

以此类推数组指针的类型有char(* )[ ]        、float(* )[ ]        、double(* )[ ]等

-字符指针:

cahr* p = "abcdef"

(1.可以想象为一个字符数组

2.当字符串出现在表达式中时,值是第一个字符的地址)

3.指向函数的指针:函数指针

int add(int a,int b)    //创建函数,可将函数类型理解为int (int,int)
{
return a+b;
}

/*函数指针的定义*/
int (*pf)(int,int)=add;    //创建函数指针pf,可理解其指针类型为int(* )(int ,int )
                              //并且将函数add的地址赋给函数指针pf

/*函数指针的调用*/
int a=1;
int b=2;

(*pf)(a,b);
pf(a,b);


/*错误调用*/
*pf(a,b);        //因为( )的优先级大于*,所以pf会先与( )结合,再与*结合

可理解pf指针类型为int(* )(int ,int )

4.void* 指针

指向的元素类型不确定

优点:可以接受任意类型的指针

缺点:void* 类型的指针不能直接进行解引用操作、+-1运算、指针运算

一般void* 类型指针用在函数参数部分

 5.其他

除上述指针外还有结构体指针等,在此不仔细讲解

3.3指针变量类型的意义

指针类型决定了对指针解引用有多大的权限(一次能访问几个字节) 

int*  的指针可以访问4个字节,即8个十六进制位,故可以将n赋值为0

char*  的指针只能访问1个字节,即2个十六进制位,故只能将一个字节的数据赋为0

所以指针类型所指定的类型(int  [10]、char [10])占几个字节,一次就只能访问几个字节

指针类型决定了指针向前走或向后走一步有多大(即+1或-1移动几个字节)

运行下面一段代码

int main()
{
	int n = 10;
	char* pc = (char*)&n;
	int* pi = &n;

	printf("n        %p\n", &n);
	printf("pc       %p\n", pc);
	printf("pc + 1   %p\n", pc + 1);
	printf("pi       %p\n", pi);
	printf("pi + 1   %p\n", pi + 1);
	return 0;
}

结果如下

可观察出char* 型指针加1只移动一个字节,int* 型指针加1移动4个字节

所以指针类型所指定的类型(int  [10]、char [10])占几个字节,一次+1或-1就移动几个字节

4.const修饰指针(常属性(属性不变))

1.const在* 左边:

int const * p = &a;

限制的是*p,意思是不能通过指针变量p修改p所指向的空间的内容,但是p不守限制

*p = 20;//错误
p = &a + 1;//正确

2.const在* 右边:

int * const p = &a;

限制的是p变量,意思是p变量不能被修改了,没办法再指向其他地址了,但是*p不受限制,还可以通过p来修改怕所指向的对象的内容

p = &a + 1;//错误
*p = 20;//正确

5.指针运算

5.1指针+- 整数

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]);
 for(i=0; i<sz; i++)
 {
 printf("%d ", *(p+i));//p+i 这⾥就是指针+整数
 }
 return 0;
}

运行结果

5.2指针-指针

指针 - 指针前提条件是两个指针指向同一块区域

int my_strlen(char *s)//模拟strlen函数
{
 char *p = s;
 while(*p != '\0' )
 p++;
 return p-s;//指针-指针,结果为两个指针之间的元素的个数
}
int main()
{
 printf("%d\n", my_strlen("abc"));
 return 0;
}

运行结果

5.3指针的关系运算

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) //指针的⼤⼩⽐较,当p到达数组最后一个元素后停止循环
 {
 printf("%d ", *p);
 p++;
 }
 return 0;
}

结果

6.野指针

6.1概念:

野指针就是指向位置不可知的(随机的、不正确的、没有明确限制的)指针

6.2成因:

1.指针未初始化

int main()
{
 int *p;//局部变量指针未初始化,默认为随机值
 *p = 20;
 return 0;
}

2.指针越界访问

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.指针指向空间被释放

int* test()
{
 int n = 100;
 return &n;
}
int main()
{
 int*p = test();//变量n所申请的空间在出函数时就已经被释放
 printf("%d\n", *p);
 return 0;
}

6.3如何规避野指针

6.3.1指针初始化

1.明确指定pa指向a

int a = 0;
int* pa = &a;

2.指针置空

若暂时无法确定p初始化谁的地址,给指针初始化为NULL

int* p = NULL;

NULL是c语言中定义的一个标识常量值为0,0也是地址(代表空值)这个地址无法使用,读写该地址会报错

为什么要用NULL呢?因为将常量赋给指针是非法的如 p = 1000; 非法

NULL在<stdio.h>中预定义

6.3.2小心指针越界

⼀个程序向内存申请了哪些空间,通过指针也就只能访问哪些空间,不能超出范围访问,超出了就是 越界访问。

6.3.3防止指向被释放空间

1.指针变量不再使用时及时置NULL,使用之前及时检查有效性。

2.避免返回局部变量的地址

7.assert断言

assert.h 头⽂件定义了宏 assert( ) ,⽤于在运⾏时确保程序符合指定条件,如果不符合,就报 错终⽌运⾏。这个宏常常被称为“断⾔”。

使用:

 assert(p != NULL);

上⾯代码在程序运⾏到这⼀⾏语句时,验证变量 p 是否等于 NULL 。如果确实不等于 NULL ,程序 继续运⾏,否则就会终⽌运⾏,并且给出报错信息提⽰。

如图

缺点:引⼊了额外的检查,增加了程序的运⾏时间

⼀般我们可以在 Debug 中使⽤,在 Release 版本中选择禁⽤ assert 就⾏,在 VS 这样的集成开 发环境中,在 Release 版本中,直接就是优化掉了。这样在debug版本写有利于程序员排查问题, 在 Release 版本不影响⽤⼾使⽤时程序的效率

  • 23
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值