VS调试(2),const修饰,assert()函数

tips

1. 比如说原先已经初始化好了一个字符数组,对这个数组连续两次进行gets输入,第一次之间与第二次之间不是覆盖关系,而是当你进行第二次gets输入的时候,在这之前会把数组再次清空

2,\0的ACSII码值为0

3. ++与--都是有副作用的

4. 而那些与位有关的操作符是无副作用的

5. ++与--的优先级需要高于解引用符号*的

6. 在打印一个字符串的时候,只需要提供一个起始地址就可以了。然后他就会顺着这起始地址一直一直打印下去,直到碰到\0为止,\0不打印。strlen()道理也是一样的

7. 如果某个函数的返回值是size_t,size_t是什么呢?是个无符号的整型,unsigned int,它的取值永远都是大于等于0的

8. size_t的本质其实就是unsigned int,但是也有微小的区别,这个后期会讲。

一个调试实例

这个是死循环的,你会发现

i本来是要跳出的,因为理论上是马上要大于12了,但是突然变成0了,好生奇怪!

原因解释

1. 关于那个死循环更加本质的东西:
首先,局部变量是放在内存中的栈区的,而内存之栈区的使用习惯先使用高地址处的空间,在使用低地址处的空间,这是栈区空间的使用习惯。然后之前讲过的不要忘记:数组随着下标的增加,地址是由低变高的;对于一个占多个字节(内存单元)的变量,对它进行取地址,取出的是所占内存单元中最低地址内存单元的地址。


2. 然后假设一个内存,内存分为三个区,一个是栈区,一个是堆区,一个是静态区。然后我把栈区单独拎出来。


3. 当数组创建的时候,设定是十个元素,那么程序就会向内存申请十个元素大小的内存空间。其他的空间不属于程序向内存申请的,内存里面的其他空间是属于操作系统的。比如如果在创建数组的之,又创建了一个变量i。由内存栈区的使用习惯可知:当我内存栈区的地址由低到高的时候,先是数组的专属内存空间,然后再过几个字节,就是i的专属内存空间。如图:

 4. 如果我硬要对数组进行越界访问赋值啊等等操作,你弄着弄着可能就走到了i的专属内存空间区域,然后会破坏那个内存空间里面好好存在着的i的数据。因此,数组越界访问会产生很多隐患。

Debug与Release再次区分

1. 然后还记得在之前讲过Debug与Release,因为之前有讲过Release往往对代码进行各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户更好的使用。

2. 上述问题代码在Debug下是死循环的,一旦在iRelease环境下就是可以的了。因为Release的话进行了各种优化。

如何写出好的,易于调试的代码呢?

接下来,我要模拟实现库函数strcpy()

官方说明

 

参考效果

这时候又需要注意:数组名就是数组元素的地址,字符串常量本身也是一个地址,字符串常量本身代表的是第一个字符的地址 

版本1(无修改)

两次修改  

这时候就突然想到,能不能对传进来的指针进行有效性判断?因为假设是NULL,那这个不能进行解引用操作的,程序会崩溃的

在使用指针的时候最好先判断一下使用的指针是不是空指针。可以用if,但是太low了,来个新东西,assert

assert()

1. assert()被称为断言它的后面可以放一个表达式表达式的结果如果为假,就会弹出一个报错界面,如果为真,无事发生。assert()需要的头文件是assert.h

2. assert()其实在Release版本已经优化掉了,程序员在Debug的时候assert工作就可以了,Release里面它不用工作,为啥?Release里面已经会把bug修掉 

3. assert()里面放的是一个表达式,未来比如说你想要规避哪种情况,就在assert里面放对应设置情况,一旦表达式为假,它这边就会报错。注意,是表达式为假然后assert会报错!比如说就可以用来断言指针的有效性.

再次修改

const修饰

1. 字符串常量是不能做修改的常量字符串的内容是不能改的,因此如果说你去访问常量字符串所在内存空间里的某个内存单元,然后尝试把那个内存单元里面的字符给换掉,这样做是行不通的,因为字符串常量是无法修改。什么是字符串常量,比如说“Elon Musk”,但是值得一提是,字符数组虽然可以等价于字符串,但是字符数组是可以修改的 

2. 接下来要知道const的用途。打一个比方,如果在int类型的变量num之前用一个const修饰,这时候相当于这个整数已经变成了一个无法修改的数了,这时候比如说你想要num=100强行进行修改,是无法编译过去的(连编译都没有成功,就跟提的bug了,bug根本就没机会出现

3. 但是还是有人会抓漏洞,创建一个指针变量,把num的地址存起来,然后通过对指针变量进行应用操作,修改num的值,发现还真的得手了 

违背了我们正常的规则,然后这时候惊讶的发现,诶居然通过指针把它改掉了。const语法设计出来本来就是来约束变量,让这个变量是无法修改的。

4. 那么该到底如何堵住这个漏洞呢?既然通过指针可以修改,我如果对指针套上一层枷锁,结果这时候发现如果我对指针变量也用const修饰套枷锁,发现就把漏洞给堵住了。

 

5. 但是这时候又容易混淆一点。当const修饰指针变量的时候,有两种情况。一种情况就是把const放在*号的左边:const int* pa = # 注意在这个时候限制的是*pa,想要通过*pa修改num已经不行了,而pa本身还是可以修改。那这时候有人就会说int const* pa = num这样子呢?道理与之前的一模一样,因为反正const是在*的左边。

6. 但如果我要去限制pa呢?因为在上面是*pa也就是通过pa这个指针解引用进行修改的操作不行,但是pa本身还是可以修改来修改去。如果我想要去限制的是pa,那我就把const放到*的右边,但这时候是没有限制*pa的,*pa是可以解引用操作大肆修改num的,但是pa是不能修改的值了,比如说我来个p = &num1,这样的操作是不行了的。

7. 然后我们总结一下,就是这样。当const去修饰值的时候,比如说要把const加到int *pa = &num,可以把它放在*的左边也可以放在右边。当把const放在*左边的时候,修饰的是指针所指向的内容,表示指针所指向的内容不能通过指针来改变,但是指针变量本身可以修改。如果const放在*右边,const修饰的是指针变量本身,表示指针变量本身的内容不能被修改。但是指针指向的内容可以通过指针来改变。
反正总而言之,就是要搞清楚对于一个指针变量而言,指针变量本身的内容(其实也就是它里面存放的地址本身)与其指向的内容(其实也就是它里面存放的地址所对应到内存条里面存放的数据) 

总而言之:就看const在*的左边还是右边

注:

1. 加上const可以对某些数据进行保护,一般可以用在创建变量或指针等等,也常常出现在函数的形参内部。

2. 有时候把运行起来的错误直接在编译阶段扼杀掉是个很好的选择。 

 

再次修改

 

最后想着如果返回值是那个被拷贝进去的字符串的地址就好了,于是 

最终版本

 

(别中招,++与--都是有副作用的)

那么接下里给我模拟一个strlen()函数

编程常见错误

1. 语法错误都是编译型错误 。

2. 语法错误的特点就是在编译的时候就已经编不过去。

当我们写一个代码的时候,比如说源文件是test.c,我们最终运行的是test.exe,原代码到可执行程序,中间需要编译+链接,然后最终会生成可执行程序,可执行程序才可以运行。

这种错误有一个什么特点,双击那个错误提示,就会有一个箭头指向代码,指向错误的附近,错误代码附近的上下文环境,同时也会告诉你,在哪一个文件的哪一行。双击它的话也会有反应

3. 还有一种错误叫链接型错误。

其实说白了,就是你可能名字写错了,或者说压根不存在,或者说没有包含头文件等等,反正编译器就是不认识。链接型错误所有报的错误的名字都是无法解析的外部符号......,都是这一类型的错误,还有比如说函数写在代码里面,但是没有去定义它等等。总而言之,就是编译器不认识。但是有个遗憾的是,它报错的信息不怎么有用,行数,没用,双击了也没有什么反应。那这种问题的解决办法是什么?就是去搜,那该如何在编译器里面去搜索呢?按下Ctrl F

4. 还有一种是运行时错误,就是说,没有编译问题,没有链接问题,已经生成可执行程序文件,并且运行起来了,但就是结果有问题,只能用调试。

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

絕知此事要躬行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值