在这里插入图片描述
int main()
{
int a = 10;
int b = 20;
int * const p = &a;
*p =100;
printf("%d\n",a);
//结果a变成100
return 0;
}
const 修饰指针变量的时候放在*的右边,修饰的是指针变量本身,指针变量不能再指向其他变量,
但是可以通过指针变量,修改指针变量指向的内容
int main()
{
int a = 10;
int b = 20;
int const * p = &a;
*p =100;
printf("%d\n",a);
//结果a不变
return 0;
}
const 修饰指针变量的时候放在*的左边,修饰的是指针指向的内容(即 *p),指针不能再修改其指向内容,
但是可以修改指针变量本身的值(修改指针变量的指向)
//现在举个例子
int main()
{
int n = 10;
int m = 20;
int * p = &n;
//现在假设n与p是男女朋友,n有十块钱,现在p想吃凉皮动作就是
//*p = 0;
//但是 n还想留着去网吧
//所以就相当于 int const * p = &n; 让*p = 0;这个动作不能发生
//于是p就要换男朋友 p = &m; 这样就可以吃凉皮
//n 一想不划算,为了10快丢啦媳妇 于是就改为 int * const p = &n;
// *p = 0;就可以完成 但是 p = &n;就无法完成了
// 要是*左右两边都有const, 既不能花钱又不能换对象
return 0;
}
指针运算
一 指针加减整数
// 指针加整数
// 指针加一
// int a = 10;
// int *p = &a;
// p+ --> 跳过四个字节(1*sizeof(int))
// type*p; p+1 --> 跳过1*sizeof(type)
#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]);
for(i=0; i<sz; i++)
{
//printf("%d ",arr[i]);
printf("%d ", *(p+i));//前提是数组在内存中是连续存在的
//p+i 这⾥就是指针+整数 就是跳过几个整形类型
}
return 0;
}
二 指针减指针
指针减指针的前提条件是:两个指针指向了同一空间
#include <stdio.h>
int main()
{
int arr[10] ={0};
printf("%d\n",&arr[0]-&arr[9]); //-9
printf("%d\n",&arr[9]-&arr[0]); // 9
return 0;
}
用指针模拟实现strlen函数
int my_strlen(char*str) {
int count = 0;
// char * start = str;
//while(str!='\0'){
// str++;
// } 用指针减指针
//return str-start;
while (*str != '\0') {
count++;
str++;
}
return count;
}
int main()
{
char arr[] = "ahsjad";
//strlen 统计的是\0之前的
int len = my_strlen(arr);
//数组名arr是首元素arr[0]地址 arr = &arr[0]
printf("%d ", len);
}
三 指针的运算关系
指针和指针比较大小 地址和地址比较大小
#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;
}
野指针
野指针是指针指向的位置是不可知的(随机,不正确的,没有限制的)
一 指针成因
1. 指针未初始化
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;
}
二 规避野指针
1. 指针初始化
如果明确知道指针指向哪⾥就直接赋值地址,如果不知道指针应该指向哪⾥,可以给指针赋值NULL. NULL 是C语⾔中定义的⼀个标识符常量,值是0,0也是地址,这个地址是⽆法使⽤的,读写该地址 会报错。
#include <stdio.h>
int main()
{
int num = 10;
int*p1 = #
int*p2 = NULL;
return 0;
}
2.小心指针越界
⼀个程序向内存申请了哪些空间,通过指针也就只能访问哪些空间,不能超出范围访问,超出了就是 越界访问。
3.指针不再使用时,及时置为null,指针使用前检查有效性
当指针变量指向⼀块区域的时候,我们可以通过指针访问该区域,后期不再使⽤这个指针访问空间的 时候,我们可以把该指针置为NULL。因为约定俗成的⼀个规则就是:只要是NULL指针就不去访问, 同时使⽤指针之前可以判断指针是否为NULL。
4. 避免返回局部变量的地址
不要返回局部变量的地址。
asssert断言
assert.h 头⽂件定义了宏 assert() ,⽤于在运⾏时确保程序符合指定条件,如果不符合,就报 错终⽌运⾏。这个宏常常被称为“断⾔”。
assert(p != NULL);验证变量 p 是否等于 NULL 。如果确实不等于 NULL ,程序 继续运⾏,否则就会终⽌运⾏,并且给出报错信息提⽰
assert() 的使⽤对程序员是⾮常友好的,使⽤ assert() 有⼏个好处:它不仅能⾃动标识⽂件和 出问题的⾏号,还有⼀种⽆需更改代码就能开启或关闭 assert() 的机制。如果已经确认程序没有问 题,不需要再做断⾔,就在 #include 语句的前⾯,定义⼀个宏 NDEBUG 。
#define NDEBUG
#include <assert.h>
然后,重新编译程序,编译器就会禁⽤⽂件中所有的 assert() 语句。如果程序⼜出现问题,可以移 除这条 #define NDBUG 指令(或者把它注释掉),再次编译,这样就重新启⽤了 assert() 语 句。
assert() 的缺点是,因为引⼊了额外的检查,增加了程序的运⾏时间。
传值调用和传址调用
7)
没有完成交换两个数的任务 因为当实参传递给形参是,形参实惨的一份临时拷贝,对形参的修改不会影响实参
所以现在要用传址调用