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

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、项目代码均经过严格本地测试,运行OK,确保功能稳定后才上传平台。可放心下载并立即投入使用,若遇到任何使用问题,随时欢迎私信反馈与沟通,博主会第一时间回复。 2、项目适用于计算机相关专业(如计科、信息安全、数据科学、人工智能、通信、物联网、自动化、电子信息等)的在校学生、专业教师,或企业员工,小白入门等都适用。 3、该项目不仅具有很高的学习借鉴价值,对于初学者来说,也是入门进阶的绝佳选择;当然也可以直接用于 毕设、课设、期末大作业或项目初期立项演示等。 3、开放创新:如果您有一定基础,且热爱探索钻研,可以在此代码基础上二次开发,进行修改、扩展,创造出属于自己的独特应用。 欢迎下载使用优质资源!欢迎借鉴使用,并欢迎学习交流,共同探索编程的无穷魅力! 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值