C进阶
只爱编程的菜鸟
这个作者很懒,什么都没留下…
展开
-
内存泄露检测模块
下面 so easy,直接看代码领悟test.c#include <stdio.h>#include "mleak.h"void f(){ MALLOC(100); return;}int main(){ int* p = (int*)MALLOC(3 * sizeof(int)); f(); p[0] = 1; p[1] = 2; p[0] = 3; FREE(p); PRINT_LEAK_IN原创 2021-12-28 23:56:27 · 130 阅读 · 0 评论 -
函数设计原则
函数设计原则:(1)函数从意义上应该是一个独立的功能模块(2)函数名要在一定程度上反应函数的功能(3)函数参数名要能够体现参数的意义(4)尽量避免在函数中使用全局变量,实现函数是无状态调用(5)当函数参数不应该在函数体内部被修改时,应加上const声明(6)如果参数是指针,且仅作输入参数,则应加上const声明(7)不能省略返回值的类型如果函数没有返回值,那么应声明为void类型(8)对参数进行有效性检查对于指针参数的检查尤为重要(9)不要返回指向“栈内存”的指针栈内存在函数体结束时原创 2021-12-22 14:35:34 · 627 阅读 · 0 评论 -
递归函数分析
递归的思想:递归将大型复杂问题转化为与原问题相同但规模较小的问题进行处理。递归函数:(1)递归体内部可以调用自己(2)递归函数函数体内存在自我调用的函数(3)递归函数必须有递归出口,函数的无线递归将导致程序栈溢出而崩溃编程实验:递归版strlen#include <stdio.h>int strlen_r(const char* s){ if(*s) { return 1 + strlen_r(s + 1); } else原创 2021-12-22 14:10:52 · 102 阅读 · 0 评论 -
函数参数的秘密
函数参数的秘密(上)函数参数:(1)函数参数在本质上与局部变量相同在栈上分配空间(2)函数参数的初始值是函数调用时的实参值函数参数的求值顺序依赖于编译器的实现实验分析:函数参数的求值顺序#include <stdio.h>int func(int i, int j){ printf("i = %d, j = %d\n", i, j); return 0;}int f(){ return 1;}int g(){ return 2;原创 2021-12-22 12:25:52 · 98 阅读 · 0 评论 -
C++中的抽象类和接口
什么是抽象类?面向对象中的抽象类可用于表示现实世界中的抽象概念是一种只能定义类型,而不能产生对象的类只能被继承并重写相关函数直接特种是相关函数没有完整的实现抽象类只能被继承不能创建对象抽象类与纯虚函数:(1)C++语言中没有抽象类的概念(2)C++中通过纯虚函数实现抽象类(3)纯虚函数是指只定义原型的成员函数(4)一个C++类中存在纯虚函数就成为了抽象类纯虚函数的语法规则:class Shape{public: virtual double area() = 0;};原创 2021-12-11 21:54:01 · 407 阅读 · 0 评论 -
对象模型分析下
继承对象模型:(1)在C++编译器的内部类可以理解为结构体(2)子类是由父亲成员叠加子类新成员得到的编程实验:继承对象模型初探#include <iostream>#include <string>using namespace std;class Demo{protected: int mi; int mj;public: virtual void print() { cout << "mi = "原创 2021-12-07 00:16:40 · 495 阅读 · 0 评论 -
C++对象模型分析上
回归本质:class是一种特殊的struct在内存中class依旧可以看做变量的集合class与struct遵循相同的内存对齐规则class中的成员函数与成员变量是分开存放的每个对象有独立的成员变量所有对象共享类中的成员函数编程实验:对象内存布局初探#include <iostream>#include <string>using namespace std;class A{ int i; int j; char c; d原创 2021-12-05 23:07:26 · 77 阅读 · 0 评论 -
同名覆盖引发的问题
父子间的赋值兼容(兼容性):(1)子类对象可以直接赋值给父类对象(2)子类对象可以直接初始化父类对象(3)父类指针可以直接指向子类对象(4)父类引用可以直接引用子类对象实验编程:子类对象的兼容性#include <iostream>#include <string>using namespace std;class Parent{public: int mi; int add(int i) { mi += i;原创 2021-12-05 20:55:53 · 532 阅读 · 0 评论 -
父子间的冲突
问题:子类中是否可以定义父类中的同名成员?如果可以,如何区分?如果不可以,为什么?编程实验:同名成员变量#include <iostream>#include <string>using namespace std;class Parent{public: int mi;};class Child : Parent{public: int mi;};int main(){ Child c; c.mi = 100;原创 2021-12-05 17:40:06 · 496 阅读 · 0 评论 -
继承中的析构与构造
子类对象的构造:(1)子类中可以定义构造函数(2)子类构造函数必须对继承而来的成员进行初始化直接通过初始化列表或者赋值的方法进行初始化调用父类构造函数进行初始化父类构造函数在子类中的调用方式:(1)默认调用(子类在创建对象时会默认调用父类的构造函数)适用于无参构造函数和使用默认参数的构造函数(2)显示调用通过初始化列表进行调用适用于所有父类构造函数父类构造函数的调用:class Child : public Parent{public: Child() //隐式调用原创 2021-12-05 16:42:00 · 768 阅读 · 0 评论 -
有符号或无符号
计算机中的符号位:数据类型的最高位用于标识数据的符号:最高位为1,表明这个数为负数最高位为0,表明这个数为正数int sign = 0;char i = -5;short j = 5;int k = -1;sign = (i & 0x80);// sign不等于0sign = (i & 0x8000);//sign等于0sign = (k & 0x80000000);//sign不等于0实验编程:有符号数的符号位#include <stdio.h&原创 2021-12-05 00:03:02 · 66 阅读 · 0 评论 -
继承中的访问级别
问题:子类是否可以直接访问父亲的私有成员?答案:子类不能直接访问父类的私有成员#include <iostream>#include <string>using namespace std;class Parent{private: int mv;public: Parent() { mv = 100; } int value() { return mv; }};clas原创 2021-12-04 16:50:16 · 306 阅读 · 0 评论 -
继承的概念和意义
实验演示:组合关系的描述#include <iostream>#include <string>using namespace std;class Memory{public: Memory() { cout << "Memory()" << endl; } ~Memory() { cout << "~Memory()" << endl;原创 2021-12-04 15:42:16 · 284 阅读 · 0 评论 -
类型转换函数2
类类型是否能够类型转换为普通类型可以,通过类型转换函数类型转换函数:(1)C++类中可以定义类型转换函数(2)类型转换函数用于将类对象转换为其他类型(3)语法规则:operator Type (){ Type ret; //...... return ret;}类型转换函数:与转换构造函数具有同等地位使得编译器有能力将对象转化为其他类型编译器能够将隐式的使用类型转换函数#include <iostream>#include <strin原创 2021-12-04 00:26:43 · 98 阅读 · 0 评论 -
类型转换函数一
C语言编译器类型转换:标准数据类型之间会进行隐式的类型安全转换 (小类型转换为大类型支持)转换规则如下:char->(int)short->int->unsigned int->long->unsigned long->float->double实验分析:有趣的隐式类型转换#include <iostream>#include <string>using namespace std;int main(){ s原创 2021-12-03 21:59:35 · 66 阅读 · 0 评论 -
前置操作符和后置操作符
下面的代码有没有区别?为什么?i++; //i的值作为返回值,i自增1++i; //i自增1,i的值作为返回值因为没有使用上述i的返回值,编译器会对他们进行优化,优化后的二进制程序丢失了C/C++的原生语义真正的区别:对于基础类型的变量:前置++和后置++的效率基本相同对于类类型的对象:前置++的效率高于后置++尽量使用前置++操作符提高程序效率++操作符可以被重载:全局函数和成员均可进行重载重载前置++操作符不需要额外的参数重载后置++操作符需要一个int类型的占位参数实验原创 2021-12-02 00:31:15 · 131 阅读 · 0 评论 -
逗号操作符的分析
逗号操作符:逗号操作符(,)可以构成逗号表达式逗号表达式用于将多个子表达式连接为一个表达式逗号表达式的值为最后一个子表达式的值逗号表达式中的前N-1个子表达式可以没有返回值逗号表达式按照从左向右的顺序计算每个子表达式的值exp1, exp2,…,expN实例演示重载逗号操作符:(1)在C++中重载逗号操作符是合法的(2)使用全局函数对逗号操作符进行重载(3)重载函数的参数必须有一个是类类型(4)重载函数的返回值类型必须是引用Class& operator , (cosnt原创 2021-12-01 23:45:44 · 538 阅读 · 0 评论 -
逻辑操作符的陷阱
逻辑操作符的原生意义:(1)操作数只有两种值****(true 和 false)(2)逻辑表达式不用完全计算就能确定最终值(3)最终结构只能是true或者flase原创 2021-12-01 09:40:13 · 61 阅读 · 0 评论 -
智能指针分析
智能指针为了解决内存泄漏的问题而诞生内存泄漏:(1)动态申请堆空间,用完后不归还(2)C++语言中没有垃圾回收的机制(3)指针无法控制所指堆空间的生命周期编程实验:内存实验#include <iostream>#include <string>using namespace std;class Test{ int i;public: Test(int i) { this->i = i; } int v原创 2021-11-30 23:56:44 · 399 阅读 · 0 评论 -
经典问题解析三
关于赋值的疑问:什么时候需要重载赋值操作符?编译器是否提供默认的赋值操作符?答案:(1)编译器为每个类默认重载了赋值操作符(2)默认的赋值操作符仅完成浅拷贝(3)当需要进行深拷贝时必须重载赋值操作符 //赋值时携带外部资源时,需要深拷贝(4**)赋值操作符与拷贝构造函数有相同的存在意义**一般性原则:重置复制操作符,必然需要实现深拷贝编程实验:默认赋值操作符重载编程实验:编程实验:数组类的优化编译器默认提供的函数:下面两个类等价class Test{};class T原创 2021-11-28 21:53:57 · 307 阅读 · 0 评论 -
函数对象分析
解决方案:函数对象:使用具体的类的对象取代函数该类的对象具备函数调用的行为构造函数指定具体数列项的起始位置多个对象相互独立的求解数列项函数对象函数调用操作符( () )只能通过类的成员函数重载可以定义不同参数的多个重载函数小结:(1)函数调用操作符() 是可重载的(2)函数调用操作符只能通过类的成员函数重载(3)函数调用操作符可以定义不同参数的多个重载函数(4)函数对象用于在工程中取代函数指针#include <iostream>#include <st原创 2021-11-28 17:46:14 · 61 阅读 · 0 评论 -
数组操作符的重载
问题:string类对象还具备C方式字符串的灵活性吗?还能直接访问单个字串符吗?答案:是字符串类的兼容性:(1)string类最大限度的考虑了C字符串的兼容性(2)可以按照C字符串的方式使用string对象实例分析:用C方式使用string类#include <iostream>#include <string>using namespace std;int main(){ string s = "abcd1ef2g3"; int n = 0原创 2021-11-28 16:39:00 · 318 阅读 · 0 评论 -
函数的意义
函数声明与定义:(1)声明的意义在于告诉编译器程序单元的存在(2)定义则明确指示程序单元的意义 (伴随着内存分配)(3)C语言中通过extern进行程序单元的声明(4)一些程序单元在声明时可以省略extern (extern int g_var) (extern(可省略) struct Test)(5)声明和定义并不相同实例分析:声明和定义不同extern int g_var;extern struct Test; //extern可以省略int main(){ exter原创 2021-11-28 14:42:55 · 243 阅读 · 0 评论 -
内存操作经典问题二
常见内存错误(1)结构体成员指针未初始化(2)结构体成员指针未分配足够的内存(3)内存分配成功,但并未初始化 //0认为字符串结束,否则认为字符串没有结束,分配内存用于字符串操作,但没有初始化字符串,对这个字符串进行复制操作可能产生内存操作越界(4)内存操作越界在什么作用域中申请的内存空间就在什么作用域中释放free函数释放的是堆上的空间,不能释放栈上的空间#include <stdio.h>#include <malloc.h>void test(int* p,原创 2021-11-28 14:07:03 · 158 阅读 · 0 评论 -
内存操作经典问题
野指针(1)指针变量中的值是非法的内存地址,进而形成野指针(2)野指针不是NULL指针,是指向不可用内存的地址的指针(3)NULL指针并无危害,很好判断,也很好调试(4)C语言中无法判断一个指针所保存的地址是否合法野指针:(1)除了保存变量地址的指针和malloc的指针,其他是野指针(2)指针指向malloc申请的地址空间,通过free释放调后变成野指针野指针的由来:(1)局部指针变量没有被初始化(2)指针所执行的变量在指针之前被销毁(3)使用已经释放的指针(4)进行了错误的指针运算原创 2021-11-28 11:29:25 · 761 阅读 · 0 评论 -
C++中的字符串
历史遗留问题:(1)C语言不支持真正意义上的字符串(2)C语言用字符数组和一组函数实现字符串操作(3)C语言不支持自定义类型,因此无法获得字符串类型解决方案:C++引入了自定义类型C++中可以通过类完成字符串类型的定义标准库中的字符串类:C++语言直接支持C语言的所有概念C++语言中没有原生的字符串类型C++标准库提供了string类型string直接支持字符串连接string直接支持字符串的大小比较string直接支持字串查找和提取string直接支持字符串的插入和替换..原创 2021-11-27 23:47:05 · 335 阅读 · 0 评论 -
初探C++标准库
C++标准库:(1)C++标准库是由类库和函数库组成的集合(2)C++标准库中定义的类和对象都位于std命名空间中(3)C++标准库的头文件都不带.h后缀,涵盖了C库的功能(4)C++标准库包含经典算法和数据结构的实现C++编译环境的组成:...原创 2021-11-27 21:31:06 · 69 阅读 · 0 评论 -
完善的复数类
完善的复数类:利用操作符重载统一复数与实数的运算方式统一复数与实数的比较方式原创 2021-11-27 20:57:22 · 183 阅读 · 0 评论 -
操作符重载的概念
+操作符支持复数相加:操作符重载:C++中的重载能够扩展操作符的功能操作符的重载以函数的方式进行本质:用特殊形式的函数扩展操作符的功能操作符重载语法:通过operator关键字可以定义特殊的函数operator的本质是通过函数重载操作符可以将操作符重载函数定义为类的成员比全局操作符重载少一个参数(左操作数)不需要依赖又元就可以完成操作符重载编译优先在成员函数中寻找操作符重载函数小结:(1)操作符重载的本质是通过函数扩展操作符的功能(2)operator关键字是实现操作符重载的关原创 2021-11-27 15:54:40 · 237 阅读 · 0 评论 -
类的函数重载
类中的成员函数可以进行重载:构造函数的重载普通成员函数的重载静态成员函数的重载万变不离其宗:1.重载函数的本质为多个不同的函数2.函数名和参数列表是唯一的标识3.函数重载必须发生在同一个作用域中问题:全局函数,普通成员函数以及静态成员函数之间是否可以构成重载?普通成员函数和静态成员函数的作用域在类中,但是全局函数的作用域在全局命名空间中全局函数和成员函数不能构成重载关系实例分析:类的重载全面分析深度的意见:重载的意义通过函数名对函数功能进行提示通过参数列表对函数用法进行提示重原创 2021-11-27 14:58:51 · 405 阅读 · 0 评论 -
友元的尴尬能力
友元的概念(1)友元是C++中的一种关系(2)友元关系发生在函数与类之间或者类与类之间(3)友元关系是单向的,不能传递友元的用法(1)在类中以friend关键字声明为友元(2)类的友元可以是其他类或者具体函数(3)友元不是类的一部分(4)友元不受类中访问级别的限制(5)友元可以直接访问具体类的所有成员在类中用friend关键字对函数或类进行声明:class Point{ double x; double y; friend void func(Point&a原创 2021-11-26 18:05:16 · 63 阅读 · 0 评论 -
二阶构造模式
问题1.构造函数没有返回值,如何判断构造函数的执行结果?答案:没有办法判断构造函数的执行结果2.在构造函数中执行return语句会发生什么?答案:可以存在return语句,但是执行后返回,不会执行后面的语句3.构造函数执行结果是否意味着对象构造成功?答案:否编程实验:异常的构造函数#include <stdio.h>class Test{ int mi; int mj; bool mStatus;public: Test(int i, int原创 2021-11-26 16:13:01 · 70 阅读 · 0 评论 -
类的静态成员函数
(1)不依赖对象就可以访问静态成员变量(2)必须保证静态成员变量的安全性(3)方便快捷的获取静态成员变量的值在C++中可以定义静态成员函数静态策成员函数属于整个类所有可以通过类名直接访问公有静态成员函数可以通过对象名访问公有静态成员函数静态成员函数的定义:直接通过static关键字修饰成员函数static void Func(){}静态成员函数不可以直接访问成员变量小结:(1)静态成员函数没有隐藏的this参数(2)静态成员函数可以通过类名直接访问(3)静态成员函数只能直接原创 2021-11-24 23:29:38 · 2427 阅读 · 0 评论 -
类的静态成员变量
在C++中可以定义静态成员变量(1)静态成员变量属于整个类所有(2)静态成员变量的生命周期不依赖与任何对象(3)可以通过类名直接访问公有静态成员变量(4)所有对象共享类的静态成员变量(5)可以通过对象名访问公有静态成员变量静态成员变量的特性(1)在定义时直接通过static关键字修饰(2)静态成员变量需要在类外单独分配空间(3)静态成语变量在程序中位于全局数据区语法规则:Type ClassName::VarName = value;#include <stdio.h>原创 2021-11-23 23:47:19 · 321 阅读 · 0 评论 -
C++经典问题解析
当程序中存在多个对象的时候,如何确定这些对象的析构顺序?答案:对于栈对象和全局对象,类似于入栈与出栈的顺序,最后构造的对象被最先析构堆对象的析构发生在使用delete的时候,与delete的使用顺序相关单个对象创建时构造函数的调用顺序1.调用父类的构造过程2.调用成员变量的构造函数(调用顺序与声明顺序相同)3.调用类自身的构造函数析构函数与对应构造函数的调用顺序相反。多个对象析构时析构顺序与构造顺序相反#include <stdio.h>class Member{原创 2021-11-23 23:06:53 · 225 阅读 · 0 评论 -
神秘的临时对象
直接调用构造函数将长生一个临时对象临时对象的声明周期只有一条语句的时间临时对象的作用域只在一条语句中临时对象是C++中值得警惕的灰色地带有趣的实验原创 2021-11-23 21:29:44 · 343 阅读 · 0 评论 -
程序的内存布局
程序文件的一般布局(1)不同代码在可执行程序中的对应关系右边的图是可执行程序,在可执行程序中没有栈,堆的概念程序与进程程序和进程不同:程序是静态的概念,表现形式为一个可执行文件进程是动态的概念,程序由操作系统加载运行后得到进程每个程序对应多个进程每个进程只能对应一个程序一个程序可能没有对应进程,但一个进程不可能没有对应程序面试中的小问题?包含脚本代码的文本文件是一种类型的可执行程序吗?如果是,对应什么样的进程?是,但是这个进程不是操作系统直接加载得到的,而是操作系统加载这个脚本的脚本原创 2021-11-23 11:05:43 · 155 阅读 · 0 评论 -
程序中的存储区域
程序中的栈(1)栈是现代计算机程序中的最为重要的概念 (后进先出)(2)栈在程序中用于维护函数调用上下文(3)函数中的参数和局部变量存储在栈上(4)栈保存了一个函数调用所需要的维护信息参数返回地址局部变量调用上下文函数调用过程每次函数调用都对着一个栈上的活动记录调用函数的活动记录位于栈的中部被调用函数的活动记录位于栈的顶部函数调用栈上的数据(1)函数调用时,对应的栈空间在函数返回前是专用的(2)函数调用结束后,栈空间将被释放,数据不再有效#include <stdio.原创 2021-11-23 01:10:46 · 297 阅读 · 0 评论 -
对象的销毁
对象的销毁(1)一般而言,需要销毁的对象都应该做清理(2)解决方案:为每一个类都提供一个public的free函数对象不再需要时立即调用free函数进行清理class Test{ int* p;public: Test() { p = new int; } void free() { delete p; }};(3)存在的问题free只是一个普通的函数,必须显示的调用对象销毁前没有做清理,很可能造原创 2021-11-22 23:48:11 · 617 阅读 · 0 评论 -
对象的构造顺序
C++中的类可以定义多个对象?那么对象的构造顺序是怎样的?对于局部对象:当程序执行流到达对象的定义语句时进行构造#include <stdio.h>class Test{private: int mi;public: Test(int i) { mi = i; printf("Test(int i): %d\n", mi); } Test(const Test& obj) {原创 2021-11-22 23:12:40 · 92 阅读 · 0 评论