C++程序设计之指针与调试
本人学习C++的过程经验及总结,本文内容:
调试与动态内存分配
C中的内存管理
- 01
C语言提供了一些内存管理函数,这些内存管理函数可以按需要动态地分配内存空间,也可把不再使用的空间回收待用,为有效地利用内存资源提供了手段。 malloc calloc free realloc
- 02
malloc,在内存的动态存储区中分配一块长度为“size”字节的连续区域。 函数原型 (类型说明符*) malloc (size)
- 03
使用malloc函数需要注意。 1、内存分配函数向系统申请内存空间,它的地址是随机的,取决于内存占用的状态。 2、函数调用后,分配的内存空间的内容并不初始化,因此它的取值是无法确定的。 3、如果内存不足,内存分配失败,函数返回值将是一个空指针。 4、分配的内存空间可以认为是各种数据类型的,也可以认为是数组。 5、参数size一般需要借助sizeof运算符计算类型大小,也可以直接指定内存块的字节数。
- 04
calloc,在内存动态存储区中分配n块长度为“size”字节的连续区域。 函数原型 (类型说明符*)calloc(n,size)
- 05
free,释放ptr所指向的一块内存空间。 函数原型 void free(void* ptr); 注:ptr指向的必须是一块用malloc或calloc分配的内存空间。
- 06
realloc,当一块内存大小不够使用时,可以用realloc函数进行重新分配。 函数原型 void *realloc( void *ptr, size_t size ); 注:realloc改变了内存块的大小,但并不保证新的内存块和旧内存块是从同一个位置开始的。
C++中的内存管理
- 01
C++动态内存分配运算符new和delete比C语言的malloc和free函数更简洁,语法更灵活。 new delete
- 02
new,返回一个该类型的指针值,程序通过指针对这个变量进行操作。
- 03
new与malloc对比。 都要指出变量的类型,类型名要放在new之后。 都可以赋初值,不是用“=初值”的方式而是用“(初值) ”的方式,
- 04
可以定义为数组,加数组运算符[ ],用[ ]运算符申请的内存称为动态数组。 char *pc=new char[4]; //生成动态char 类型数组 动态变量没有变量名,须用指针变量接收到它的地址后,通过指针运算符“*”进行操作。 *pi=a*a; //动态变量*pi 被赋值
- 05
delete,delete运算用来撤消或释放由new生成的动态变量。
- 06
使用内存管理函数时需要注意 1、释放动态数组时,应该在指针前加[]。 2、内存管理函数是一一对应,new与delete对应;malloc与free对应。 3、动态变量与一般变量的主要区别就是它可以在程序运行过程中任意被撤销。而一般变量则必须在其所定义的程序块结束时自动撤销。
内存区域
- 01
在C/C++程序中,不管是局部变量,全局变量、或是动态变量,在内存中都占用一定的空间,但是它们占用不同的空间。 一个程序将操作系统分配给其运行的内存分成4个区域。
- 02
•代码区 存储程序代码,这些代码是一些计算机指令,它们将被送到CPU中执行。代码区由程序中各个函数的代码组成。 •全局数据区 存储程序的全局数据和静态数据。全局数据区的数据在程序启动时初始化,占用的内存在程序结束时才释放。 •堆区 存储程序的动态数据,包括用new和malloc等函数分配的动态内存,它们是可以在程序运行的任何时候分配和释放的。 •栈区 存储程序的局部数据,即各个函数内部使用的数据。例如在函数形参和函数内部定义的变量。函数在执行完毕后,函数内部使用的内存将会被自动释放
- 03
未初始化的指针 指针未初始化就进行间接引用是错误的。
常见指针错误
- 01
使用坏指针 当指针指向的内存被释放后,指针仍然指向那个地址,这个指针通常被称作“坏指针”。 当动态内存被释放后,不能再使用指针访问该内存。
- 02
函数返回局部变量的指针 指向局部变量的指针,指向它的指针只能在函数调用期间(函数返回前)进行间接引用。
- 03
空指针的间接引用 一般地,在函数开头要对指针进行判断。如果函数不允许空指针,那么更好的方法是使用assert增加断言,方便程序的调试,也不影响程序的发布版本的效率。
内存泄漏
- 01
在使用动态变量时应注意的是,要保护动态变量的地址。 动态内存地址丢失,导致内存无法使用,这种问题被称为内存泄漏。 内存泄漏造成程序运行缓慢。动态内存泄漏会抢占内存资源,降低运行速度,甚至导致蓝屏死机。
- 02
在访问一段连续的内存时,如果访问了这块连续存储空间以外的内存,那么就发生了内存访问越界。内存访问越界将会造成未知错误,极易引起程序崩溃。 内存访问越界通常发生在使用数组时。
Visual C++.NET调试
- 01
代码编写完成后,源代码要被转换为计算机可以识别的指令,每个源文件都要被编译为中间文件,然后,所有的中间文件被链接称为.exe文件。 •【生成解决方案】 让Visual Studio.NET编译各个源文件,并将它们链接成为.exe文件或其他形式的程序文件。 •【重新生成解决方案】 让编译器重新编译所有源文件。 •【清理解决方案】 清除解决方案在以前生成可执行文件时产生的旧文件。第一次生成解决方案时,源文件都会被编译一次,此后再生成解决方案时,如果某个源文件没被修改过,这个源文件将不会被重新编译。 生成一个解决方案后,会在【输出窗口】中显示日志,编译中发现的错误将会被列出。双击包含“error”的行,可以迅速切换到出错的代码。编译文件时,还可能出现另一种信息,即警告信息,这些警告提示了程序中潜在的错误。
- 02
断点的使用 在程序代码中某些位置设置一些断点,令程序执行到这些代码时暂停,此时可以查看程序运行中变量的变化情况,以便判断程序运行是否正常。 在Visual Studio.NET中,将光标移动到一行代码,按“F9”键,即在这行设置了断点,在代码行上会有一个红色圆点。还可以右击弹出快捷菜单,选择菜单中的【插入断点】。
- 03
单击菜单【调试】→【启动】运行程序后,当程序运行到断点行时暂停执行。暂停执行时,当前代码行用箭头标示。鼠标移动到变量上时,会提示变量内容。同时也可以借助其他工具查看内存内容。
- 04
•【局部变量】 在代码编辑器下边的【局部变量】标签中,可以查看断点所在函数的局部变量。这些变量的名称、值和类型会在表格中列出。变量的值可以在表格中修改。 •【自动窗口】 【自动窗口】中可以显示当前代码附近的变量的状态。 •【监视】 还可以使用【监视】窗口随时查看指定的变量。单击表格中的空行,在名称中输入变量名或表达式,即可在表格中显示这个变量的值和类型。另一种方法是选择变量,然后按“Alt+Ctrl+Q”在弹出的对话框中添加监视。
- 05
单步执行 使用单步命令让程序一次只执行一行或多行代码 •逐语句 即每次执行一条语句,如果遇到函数,跳转到函数内部的语句。快捷键为“F11”。 •逐过程 在当前函数内执行一条语句或函数(函数又称过程),不跳转到被调用的函数中。快捷键为“F10” •跳出 让程序继续执行,在当前函数调的调用语句后的一条语句暂停执行。
- 06
调用当前函数的状态被保存在一个叫做“调用堆栈”的数据结构中,查看它,可以了解程序中函数的调用次序。在【调用堆栈】窗口中,可以查看函数的调用过程,也可以双击窗口中的函数名称显示对应的函数代码。