电子商务型网站,湖南网站推广哪家专业,江宁住房和城乡建设局网站,微营销app这里要学习的有以下内容
1. const修饰指针
2. 野指针
3. assert断⾔
4. 指针的使⽤和传址调⽤
那么从这里开始
1. const 修饰指针
const修饰变量 首先我们知道变量是可以修改的#xff0c;如果把变量的地址交给⼀个指针变量#xff0c;通过指针变量的也可以修改这个变…这里要学习的有以下内容
1. const修饰指针
2. 野指针
3. assert断⾔
4. 指针的使⽤和传址调⽤
那么从这里开始
1. const 修饰指针
const修饰变量 首先我们知道变量是可以修改的如果把变量的地址交给⼀个指针变量通过指针变量的也可以修改这个变量。 但是如果我们不希望这个变量被修改那我们该怎么做呢
那么在这里就可以使用const。const的作用就是保持变量不被更改。 #include int main()
{int m 0;m 20;//m是可以修改的 const int n 0; n 20;//n是不能被修改的 return 0;
} 上述代码中n是不能被修改的其实n本质是变量只不过被const修饰后在语法上加了限制只要我 们在代码中对n进⾏修改就不符合语法规则就报错致使没法直接修改n。
那么在这里我害怕不小心将n修改所以给n加上const但是我又需要对n进行修改那么给n加上const就一定写死无法更改了吗 当然不是这时我们可以绕过n使⽤n的地址去修改n就能做到了虽然这样做是在打破语法规则。
#include int main()
{ const int n 0;printf(n %d\n, n); int* p n; *p 20; printf(n %d\n, n); return 0;
}
输出结果 程序运⾏结果 我们可以看到这⾥⼀个确实修改了但是我们还是要思考⼀下为什么n要被const修饰呢就是为了 不能被修改如果p拿到n的地址就能修改n这样就打破了const的限制这是不合理的所以应该让 p拿到n的地址也不能修改n那接下来怎么做呢 const 修饰指针变量 ⼀般来讲const修饰指针变量可以放在*的左边也可以放在*的右边意义是不⼀样的。 int * p;//没有const修饰 int const * p;//const 放在*的左边做修饰 int * const p;//const 放在*的右边做修饰 我们看下⾯代码来分析具体分析⼀下
#includestdio.h//代码1 - 测试⽆const修饰的情况
void test1()
{ int n 10; int m 20; int* p n; *p 20;//可更改 p m; //可更改
}
//代码2 - 测试const放在*的左边情况
void test2()
{ int n 10; int m 20; const int* p n; *p 20;//不可更改p m; //可更改
}
//代码3 - 测试const放在*的右边情况
void test3()
{ int n 10; int m 20; int * const p n; *p 20; //可更改p m; //不可更改
}//代码4 - 测试*的左右两边都有const
void test4()
{ int n 10; int m 20; int const * const p n; *p 20; //不可更改 p m; //不可更改
}int main()
{ //测试⽆const修饰的情况 test1(); //测试const放在*的左边情况 test2(); //测试const放在*的右边情况 test3(); //测试*的左右两边都有const test4(); return 0;
} 结论const修饰指针变量的时候 • const如果放在*的左边修饰的是指针指向的内容保证指针指向的内容不能通过指针来改变。 但是指针变量本⾝的内容可变。 • const如果放在*的右边修饰的是指针变量本⾝保证了指针变量的内容不能修改但是指针指 向的内容可以通过指针改变。
2. 野指针 概念 野指针就是指针指向的位置是不可知的随机的、不正确的、没有明确限制的 2.1 野指针成因 1. 指针未初始化
#include stdio.hint main()
{ int *p;//局部变量指针未初始化默认为随机值 这时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;
}2.2 如何规避野指针 2.2.1 指针初始化 如果明确知道指针指向哪⾥就直接赋值地址如果不知道指针应该指向哪⾥可以给指针赋值NULL. NULL 是C语⾔中定义的⼀个标识符常量值是00也是地址这个地址是⽆法使⽤的读写该地址 会报错。 初始化如下 #include stdio.h
int main()
{ int num 10; int*p1 num;int*p2 NULL; return 0;
} 2.2.2 ⼩⼼指针越界 ⼀个程序向内存申请了哪些空间通过指针也就只能访问哪些空间不能超出范围访问超出了就是 越界访问也就成为了野指针 2.2.3 将指针置为空 在指针使用结束后要将指针置为NULL避免他对代码以后的使用的过程中造成影响。
int main()
{int arr[10] {1,2,3,4,5,6,7,8,9,10};int *p arr[0];int i 0;for(i0; i10; i)
{*(p) i;
}//此时p已经越界了可以把p置为NULLp NULL;//下次使⽤的时候判断p不为NULL的时候再使⽤//...p arr[0];//重新让p获得地址if(p ! NULL) //判断{//...}return 0;
} 2.2.4 避免返回局部变量的地址 如造成野指针的第3个例⼦不要返回局部变量的地址。
3. assert 断⾔ assert.h 头⽂件定义了宏 assert() ⽤于在运⾏时确保程序符合指定条件如果不符合就报 错终⽌运⾏。这个宏常常被称为“断⾔”。 assert(p ! NULL); 上⾯代码在程序运⾏到这⼀⾏语句时验证变量 p 是否等于 NULL 。如果确实不等于 NULL 程序 继续运⾏否则就会终⽌运⾏并且给出报错信息提⽰。 assert() 宏接受⼀个表达式作为参数。 如果该表达式为真返回值⾮零 assert() 不会产⽣ 任何作⽤程序继续运⾏。 如果该表达式为假返回值为零 assert() 就会报错在标准错误 流 stderr 中写⼊⼀条错误信息显⽰没有通过的表达式以及包含这个表达式的⽂件名和⾏号。 使⽤ assert() 有⼏个好处它不仅能⾃动标识⽂件和 出问题的⾏号还有⼀种⽆需更改代码就能开启或关闭 assert() 的机制。 当我们已经确认程序没有问题不需要再做断⾔只需要在 #include 语句的前⾯定义⼀个宏 NDEBUG 就可以了无需再进行其他操作。
#define NDEBUG
#include assert.h 然后重新编译程序编译器就会禁⽤⽂件中所有的 assert() 语句。 当程序⼜出现问题只需要移除这条 #define NDEBUG 指令或者把它注释掉再次编译这样就重新启⽤了 assert() 语句。 然而assert也是有缺点的 assert() 的缺点是因为引⼊了额外的检查增加了程序的运⾏时间。 为了解决这个问题⼀般我们可以在 Debug 中使⽤在 Release 版本中选择禁⽤ assert 就⾏ 在 VS 这样的集成开发环境的 Release 版本中assert就直接被优化掉了。这样在debug版本写有利于程序员排查问题 在 Release 版本不影响⽤⼾使⽤时程序的效率。 4. 指针的使⽤和传址调⽤ 4.1 传值调⽤和传址调⽤
学习指针的⽬的是使⽤指针解决问题那什么问题⾮指针不可呢
例如写⼀个函数交换两个整型变量的值 ⼀番思考后我们可能写出这样的代码
#include stdio.h
void Swap1(int x, int y)
{int tmp x;x y;y tmp;
}
int main()
{int a 0;int b 0;scanf(%d %d, a, b);printf(交换前a%d b%d\n, a, b);Swap1(a, b);printf(交换后a%d b%d\n, a, b);return 0;
}我们发现在main函数内部创建了a和b这里假设地址 a的地址是0x00cffdd0b的地址是0x00cffdc4。在调⽤ Swap1函数时将a和b传递给了Swap1函数在Swap1函数内部创建了形参x和y接收a和b的值但是 x的地址是0x00cffcecy的地址是0x00cffcf0x和y确实接收到了a和b的值不过x的地址和a的地址不 ⼀样y的地址和b的地址不⼀样相当于x和y是独⽴的空间那么在Swap1函数内部交换x和y的值 ⾃然不会影响a和b当Swap1函数调⽤结束后回到main函数a和b实际上没有交换。Swap1函数在使⽤ 的时候是把变量本⾝直接传递给了函数这种调⽤函数的⽅式我们之前在函数的时候就知道了这 种叫传值调⽤。 结论实参传递给形参的时候形参会单独创建⼀份临时空间来接收实参对形参的修改不影响实 参。
在这里可以使用传址调用
#include stdio.h
void Swap2(int*px, int*py)
{int tmp 0;tmp *px;*px *py;*py tmp;
}
int main()
{int a 0;int b 0;scanf(%d %d, a, b);printf(交换前a%d b%d\n, a, b);Swap2(a, b);printf(交换后a%d b%d\n, a, b);return 0;
}我们可以看到实现成Swap2的⽅式顺利完成了任务这⾥调⽤Swap2函数的时候是将变量的地址传 递给了函数这样就直接改变了原地址处的数据。
这种函数调⽤⽅式叫传址调⽤。
在这里可以使用这个方法来实现strlen函数的实现这里可以参看以前的博客C语言学习速通字符串函数-CSDN博客文章浏览阅读880次点赞68次收藏14次。在编程的过程中我们不免会对字符穿进行操作那么我们就会经常使用字符串函数。那么这里我就给分享四个字符串函数。https://blog.csdn.net/2402_87907999/article/details/143661229?fromshareblogdetailsharetypeblogdetailsharerId143661229sharereferPCsharesource2402_87907999sharefromfrom_link 感谢观看有兴趣的话点赞收藏关注来一波吧。制作过程中如有错误希望可以慷慨指出。最后有更多想法可以一起在评论区聊一聊私信我也可以哦