当前位置: 首页 > article >正文

指针(3)

 

目录

 

1.const修饰变量

 2.指针运算

• 指针+- 整数

• 指针-指针

 模拟实现strlen  

• 指针的关系运算

 3.野指针

4. 如何规避野指针

5. assert断言

6.指针的使⽤和传址调⽤

7 传值调⽤和传址调⽤


1.const修饰变量

变量是可以修改的,如果把变量的地址交给⼀个指针变量,通过指针变量的也可以修改这个变量。 但是如果我们希望⼀个变量加上⼀些限制,不能被修改,怎么做呢?这就是const的作⽤

#include <stdio.h>
int main()
{
	 const int n = 100;//const叫做常变量,被修饰的变量本质还是变量,
	 //但是不能被修改的变量dd
	n = 200;
	printf("%d", n); 
	return 0;
}

 想要限制*p可以在* 左边加const 

#include <stdio.h>
int  main()
{
	int a = 10;
	int const * p = &a;//如果在*号左边加const 就会限制* p ,
	*p = 200;//这里的* p出现错误了
	p = 300;
	return 0;
}

当你运行时编译器会发生错误 

 

 当你把const放在* 的 右边会怎么样?

 当然运行结果和上面也是一样的错误,有同学就会说: 那如果把两变都加const会怎么样呢?

 可以跟据上面的结果推算,两个都不能使用, 如图所示

 

 2.指针运算

指针的基本运算有三种,分别是

• 指针+- 整数

 用指针加减整数 来打印数组 

#include <stdio.h>
int main()//用指针的方式来打印数组
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	int* p = &arr[0];
		for (i = 0; i < sz; i++)
	{
		printf("%d ", *p);
		p++;//指针加整数的关系
	}

	return 0;

当然也可以这样写


#include<stdio.h>
int main()//用指针的方式来打印数组
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	int* p = &arr[0];//让p指向arr[0]的地址
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *( p+i ));//这里的p是存的地址  i在循环增加,*取出数组下标的内容	
	}
	return 0;
}

• 指针-指针

 指针-指针的绝对值是计算指针间的元素个数,前提是两个指针都是同一个空间的。

可以猜一猜下面的代码运行结果是什么?

#include <stdio.h>
int main()
{

	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	printf(" %d", &arr[9] - &arr[0]);//指针减去指针求的是同一个空间指针的元素个数
	return 0;
}

结果是元素之间的个数

 

 模拟实现strlen  

#include <stdio.h>
#include  <string.h>//如果使用strlen 要包含的头文件
size_t My_strlen(char* p)//用char类型指针接收
{
	int count = 0;
	while (*p != '\0')
	{
		count++;//如果不是‘\0’就+1
		p++;//p也向后加
	}
	return count;//次数为几就返回几
 }

int main()
{
	char* p = "abcde";
	 size_t len = My_strlen(p);//创建一个自己的strlen 
	 printf("%zd ", len);
	return 0;

指针减指针 也可以实现

#include <stdio.h>
size_t My_strlen(char* p)
{
	char* start = p;//给一个变量start 成为首地址,
	char* end = p;//再给一个变量end  让他向后找。
	while (*end != '\0')//当end找到\0 就会停止
	{
		end++;
	}
	return end - start;// 返回end- start 的元素个数

 }
int main()
{

	char* arr = "asdefg";
	size_t len = My_strlen(arr);
		printf("%zd ", len);

	return 0;

}

 

• 指针的关系运算

 

#include<stdio.h> 
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int* p = arr;//数组名就是首元素的地址
	
	while (p < &arr[sz])//这里是地址的大小比较
	{
		printf("%d ", *p);
		p++;

	}
	return 0;
}

 3.野指针

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

#include <stdio.h>
int main()
{
	int arr[10] ={ 0 };
	int* p = arr;
	for (int i = 0; i <= 11; i++)//这里的越界访问会造成野指针
	{
		printf("%d ", *p);
		p++;
	}
	return 0; 
}

4. 如何规避野指针

     如果明确知道指针指向哪⾥就直接赋值地址,如果不知道指针应该指向哪⾥,可以给指针赋值NULL. NULL 是C语⾔中定义的⼀个标识符常量,值是0,0也是地址,这个地址是⽆法使⽤的,读写该地址 会报错

#include <stdio.hj>
int main()
{
	int arr= NULL;
	int* p = &arr;
	if (*p != NULL)//可以判断一下p的内容是不是空指针
	{
		*p = 100;
	}
	else
	{
		printf("是空指针\n");
	}
	return 0;
}

5. assert断言

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

正如上述代码也可以改成下面代码

#include<stdio.h>
#include <assert.h>//包含的assert的头文件
int main()

{
	int arr = 100;//当arr等于100的时候程序什么没问题
	assert(arr != NULL);
	{
	
	}
	return 0;
}

但是当arr等于NULL的时候程序会怎么样?

 这里程序会直接崩溃

如果assert 不想使用了,那么先别着急注释掉,下面有一个好玩的东西可以像开关一样控制它。 那么就是在<assert .h>定义的头文件前定义一个 #define NDEBUG 这样的一条语句。

#include <stdio.h>
#define NDEBUG//  这个时候的assert已经被关闭了,
//如果把这条语句注释掉,assert就可以正常使用了
#include <assert.h>//包含的assert的头文件
int main()
{
	int arr = NULL;
	assert(arr != NULL);
	{
	
	}
	return 0;
}

 

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

6.指针的使⽤和传址调⽤

strlen的模拟实现 库函数strlen的功能是求字符串⻓度,统计的是字符串中 \0 之前的字符的个数

可以用assert来判断表达式,如果不想arr数组被修改 可以再变量左边加上const,配合const使用   

#include <stdio.h> 

#include <assert.h>
size_t My_strlen(char const *p)//如果期望arr字符串不被修改 可以加上const
{
    int count = 0;

    assert(*p != '\0');
    while (*p)
    {
            count++;
            p++;
    }
    return count;
 }

int main()
{
    char arr[] = "adkfjd";

    size_t len = My_strlen(arr);
    printf("% zd ", len);
    return 0;

}

7 传值调⽤和传址调⽤

 例如:写⼀个函数,交换两个整型变量的值 ⼀番思考后,我们可能写出这样的代码:

#include<stdio.h>
void  Swap(int x, int y)
{
	int t = 0;
	t = x;
	x = y;
	y = t;
	
 }
int main()
{
	int a = 0;
	int b = 0;
	scanf("%d%d", &a, &b);
	printf(" 交换前%d %d\n", a, b);
	Swap(a, b);
	 printf(" 交换后%d %d\n", a, b);
	return 0;
}

然而你发现打印来的结果并不是你想要的结果。

 这是哪里出了问题呢? 

下面可以的出,形参的改变不会影响实参 。这里交换的是x 和y 

 

 想要改变a和b 的数值,可以在函数传递时 传地址上去 代码如下:

#include <stdio.h>
void  Swap(int *pa, int* pb)//如果传的是地址 那么得用指针接收
{
	int t = 0;
	t = *pa;//t =a;
	*pa = *pb;//a=b;
	*pb = t;//b=t;
//这样就完成的交换	
 }
int main()
{
	int a = 0;
	int b = 0;
	scanf("%d%d", &a, &b);
	printf(" 交换前%d %d\n", a, b);
	Swap(&a, &b);
	 printf(" 交换后%d %d\n", a, b);
	return 0;
}

 那么就大功告成了

 结束了下班 


http://www.kler.cn/a/325044.html

相关文章:

  • 15-大模型 RAG 经验篇
  • golang开源框架:go开源验证框架validator
  • Gin HTML 模板渲染
  • nodejs+mysql+vue3 应用实例剖析
  • Java基础-组件及事件处理(中)
  • PG-DERN 解读:少样本学习、 双视角编码器、 关系图学习网络
  • 第18周 第1章Ajax基础知识
  • flutter中InkWell 和 GestureDetector快速、频繁点击时表现出不响应或“点不到”的情况分析
  • 区块链:数据安全与透明的未来触手可及
  • 制造解法 Manufactured Solutions 相关的论文的阅读笔记
  • 安全、稳定、SLA高达99.9%:Azure OpenAI数据分离与隔离优势
  • C语言编写一个五子棋游戏-代码实例讲解与分析
  • 求10 个整数中最大值
  • 基于大数据技术的宠物商品信息比价及推荐系统
  • Transformer算法7个面试常见问题
  • python中的assert语句
  • Android TextView对URL识别
  • Kafka技术详解[5]: 集群启动
  • Vue 自定义组件实现 v-model 的几种方式
  • ubuntu20.04编译安装opencv-4.9.0的cuda版本
  • RTE大会报名丨 重塑语音交互:音频技术和 Voice AI,RTE2024 技术专场第一弹!
  • 「漏洞复现」某徳知识产权管理系统 UploadFileWordTemplate 文件上传漏洞
  • SAP调用发起泛微OA流程
  • MySQL之索引基本知识
  • 大数据是不是需要用很多ip
  • js逆向——webpack实战案例(一)