ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

深挖C语言:深入理解指针(2)

深挖C语言:深入理解指针(2) 目录1.const修饰指针2.野指针2.1 野指针的成因2.2 规避野指针3.assert断言4.指针的使用和传值调用4.1 库函数strlen的实现4.2 传值调用和传址调用1.const修饰指针作用使操作对象不能被随意修改。操作对象普通变量指针函数形参全局常量。int m 10; m 20; // m 能被修改 const int n 20; n 10;// n 不能被修改这里虽然 n 被 const 修饰强行修改会报错但是我们可以通过指针的方式绕过const的限制修改变量n代码如下const int n 10; int* pn n; *pn 20;// n 成功被修改这种行为显然打破了语法规则如果我们不想让变量通过指针被修改那么就对指针也进行 const 修饰代码如下const int n 10; const int* pn n; * pn 20;// n不能被修改代码报错 int* const pn n; *pn 20;// n可以被修改虽然说对指针变量进行了const修饰但是const放在 * 的前面或者后面修饰的对象不相同所加的范围限制也不相同。const int n 10; int m 20; int* const pn n; *pn 30;//没问题 pn m;//报错指针变量pn不能被修改 const int* pm m; *pm 30;//报错*pm不能被修改 pm n;//没问题结论const修饰指针变量时1.如果放在 * 的左边修饰的是所存指针指向的内容使指向的内容不能被修改。2.如果放在 * 的右边修饰的是指针变量所存的内容指针变量的内容不能被修改。2.野指针概念指针指向的位置是不可知的随机的不正确的没有明确限制的称为野指针。2.1 野指针的成因1指针未初始化int n; int* pn n;// pn因变量未初始化成为野指针2指针越界访问int arr[10] { 0 }; int* parr arr[10];// parr越界访问成为野指针3指针指向的空间被释放int* test() { int n 100; return n; } int main() { int* p test();// n的空间被释放p成为野指针 printf(%d\n,*p); return 0; }注意一个程序向内存申请了哪些空间通过指针也就只能访问这些空间超出这个范围了就是越界访问。2.2 规避野指针1指针使用之前检查指针的有效性指针变量不再使用时及时置NULL。2不要返回局部变量的地址。野指针就好比是一条没有拴绳的野狗对其放任不管是很危险的对指针及时赋予NULL相当于把这条野狗给栓了起来对NULL约定俗成的一个规矩就是只要是NULL指针就不去访问规避了野指针的形成。3.assert断言使用代码如下#includeassert.h //使用assert不要忘记带上头文件 assert(p ! NULL);代码运行时assert()宏接受一个表达式作为参数如果该表达式为真返回结果非零assert()就不会产生任何作用程序继续执行。如果表达式为假返回结果为零assert(就会中止代码的运行并进行报错。如果已经确定程序没有任何问题不需要再进行断言此时在#includeassert.h前定义一个宏NDEBUG即可代码如下#define NDEBUG //消除assert() #includeassert.h注意1assert因为对程序有了检查会增加程序运行的时间。2assert一般是在Debug中使用在Release中选择禁用assert这样在Debug版本中有利于程序员排查问题在Release版本中不影响用户使用程序的效率。4.指针的使用和传值调用4.1 库函数strlen的实现作用求字符串的长度。代码如下int my_strlen(const char * str) { int count 0; assert(str); while(*str) { count; str; } return count; } int main() { int len my_strlen(abcdef); printf(%d\n,len); return 0; }4.2 传值调用和传址调用为了区分传值调用和传址调用简写交换整型变量的函数便于理解传值调用代码#includestdio.h void Swap1(int x,int y) { int tmp x; x y; y tmp; } int main() { int a 10; int b 20; Swap1(a,b); printf(%d %d,a,b);//a,b并未被交换 return 0;传址调用代码#includestdio.h void Swap2(int* x,int* y) { int tmp *x; *x *y; *y tmp; } int main() { int a 10; int b 20; Swap2(a,b); printf(%d %d,a,b);//a,b的值成功被交换 return 0; }由此我们可知实参传递给形参的时候形参会单独创建一份临时空间变量来接受实参对形参的修改不影响实参。这是Swap1失败的原因在上述函数中只能使用传址调用。结论传址调用可以让函数和主调函数之间建立真正的联系在函数内部可以修改主调函数的变量所以函数只需要主调函数中的变量值来进行计算就可以使用传值调用如果函数内部要修改主调函数中的变量值就需要使用传址调用。最后感谢阅读欢迎大家交流与指正。
返回列表