指针(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;
}
那么就大功告成了
结束了下班