数组越界:
参考书籍:《C陷阱和缺陷》
//在VS2019, x86, Debug环境下
int main()
{
int i = 0;
int arr[3] = { 1, 2, 3};
for(i = 0; i <= 4; i++)
{
arr[i] = 0;
}
// i = 0, 1, 2 : arr[i] = 0
// i = 3 : 此时数组已经越界,arr[3] 指向一个未知的空间,并将其设为0.
// i = 4 : 此时 i 的地址为arr[4]。 i 被设为0,进行新一轮循环
// i = 0 : 重新循环,造成死循环。
}
这段代码出现这样的结果是在特定的环境下进行的,也就是设计好的。
但这也证明了代码调试的重要性,在较多代码的情况下,如果没有及时修正错误,用户在使用的过程中不可避免地出现错误。
优秀的代码具有的特点:
最起码要做到 代码运行正常以及减少bug的出现。
不仅如此,还要:
1、效率高:不同代码所消耗系统资源和时间都是不同的,有一些代码可以造成资源浪费现象严重,而有些代码”短小精悍“,在资源分配上十分合理。
2、可读性高,注释清晰:这方便程序员及后期的测试人员去优化代码。
常见的coding技巧:
1、使用assert
2、尽量使用const
3、养成良好的编程习惯:变量取名尽量规范明了、注释清晰,说明白代码在干什么。
4、熟练掌握F10等调试技巧。
使用assert()来报错:
assert(表达式)
如果程序运行到该语句时发生错误,这回发出表达式所出现的问题。
并且在release版本中会被优化(不会发出错误报告)。
// 该语句的实现需要引入头文件 <cassert>
int* p = NULL;
assert(p != NULL);//发生错误
//assert() 表达式为假就会报错,为真那就什么事都不会发生。
使用const来固定变量:
const int num = 1;
int a = 0;
//num = 20; 由const修饰的变量无法修改
int* p = #
*p = 12;//但使用指针来间接修改试允许的
//这种情况可以给指针也用const进行修饰
//一种情况是 const 放在 * 的左边, const 修饰指针所指向的对象
//const int* p = #
//*p = 123; err
//p = &a; pass 可以修改本身指针所指向的对象
//第二种情况时const 放在 * 的右边, const 修饰指针本身
//int* const p = #
//*p = 123; pass 此时可以修改指针指向对象的内容
//p = &a; err
//最后一种情况是 * 左右两边都有 const进行修饰,那就将指针和其指向的内容都锁死。
综合利用:
编写函数,实现求得字符串长度:
int slen(const int* str)
{
int count = 0;
assert(str != NULL);
while(*str++ != '\0')//先执行*str, 再执行str++
{
count++;
}
return count;
}
常见运行错误:
语法型错误:
一般看编译器错误提示信息,可以双击直接定位到问题所在,例如使用中文符号等错误。
链接型错误:
看错误提示信息,找到标识符所在位置(可以使用 Ctrl + f 打开查找功能),一般是标识符名不存在或者拼写错误。
运行时错误:
需要经验积累,这是最常见的也是最难解决的错误,例如数组越界等问题。