2019/6/14
1.数组名不是指针,数组名只是数组首地址,地址和指针不是一回事。
int arr[5] = {0,1,2,3,4};
int ptr = arr; //int型的ptr指向int型的数组没错
ptr++; // OK, ptr是左值.
arr++; // Error,地址不能是左值,不能像指针一样做自增运算。
具有相同类型的指针类型变量p与数组a,不能进行的操作p=&a,p需为二级指针。
3.Test b();只是声明了一个函数,并没有创建对象,所以通过b来调用成员函数会出错。
PS:Test为类名。
C 语言程序中,若对函数类型未加显式说明,则函数的隐含说明类型为(int)。
一个算法应该具有以下5个特性:①有穷性、②确定性、③可行性、④有0个或多个输入、⑤有一个或多个输出。因此一个算法可以没有输入(程序的功能确定),但必须要有输出,没有输出的算法是没有意义的。
gets()遇到空格或tab不会结束,只有遇到回车才结束;scanf遇到空格,tab,回车即结束输入。
条件为32位操作系统,编译4字节对齐。
// 对齐原则:每一成员需对齐为后一成员类型的倍数
// 补齐原则:最终大小补齐为成员类型最大值的倍数
Struct A
{
Int a; // 4
Short b; // (4) + 2 = 6 下一元素为 int,需对齐为 4 的倍数, 6 + (2) = 8
Int c; // (8) + 4 = (12)
Char d; // (12) + 1 = 13, 需补齐为 4 的倍数,13 + (3) = 16
};
Struct B
{
Int a; // 4
Short b; // (4) + 2 = 6,下一成员为 char 类型,不考虑对齐
Char c; // (6) + 1 = 7,下一成员为 int 类型,需对其为 4 的倍数,7 + (1) = 8
Int d; // (8) + 4 = 12,已是 4 的倍数
}
(动态类型转换)dynamic_cast (expression) 该运算符把父类投射到子类时,父类必须要有虚函数,Type-id 必须是类的指针、类的引用或者void*;需要用到RTTI(运行时类型信息)
主要用途:将基类的指针或引用安全地转换成派生类的指针或引用,并用派生类的指针或引用调用非虚函数。如果是基类指针或引用调用的是虚函数无需转换就能在运行时调用派生类的虚函数。
如果 type-id 是类指针类型,那么expression也必须是一个指针,如果 type-id 是一个引用,那么 expression 也必须是一个引用;
向上(基类)转换一定成功,向下(子类)转换不一定成功。向下转换父类必须存在虚函数,不然编译错误。
假如集成关系如下:
A(基类) <- B <- C
变量定义如下:
A a = new B; // 本来new了个B,向上转换为AB b = dynamic_cast (a); //从A再转换为B,因为本质是B,回到自身,所以成功C* c = dynamic_cast
更灵活一点:
A(基类) <- B <- C <- D <- EA* a = new C; // A本质为CB* b = dynamic_cast (a); // 成功,因为B是C的父类E* e = dynamic_cast
2019/6/15
类的私有成员只能通过本类成员函数或者友元函数访问,本类对象不能直接访问。当存在类继承时,无论继承时是公有继承,保护继承还是私有继承,派生类的成员函数和派生对象均不能直接访问基类私有成员。(私有成员还是会被继承的)
函数模板必须由编译器根据程序员的调用类型实例化为可执行的函数。
类模板的成员函数都是函数模板。
没使用过的成员函数(即函数模板)不会被实例化。(也就是不会占用内存)
无论是static还是非static的全局变量,如果不加限制随意访问的话容易出现同步问题。
无论是static还是非static的局部变量,每个线程都是私有的,其他线程不会对其进行干扰。
析构函数没有参数列表,无法重载(overload),但是可以重写(override)。
private和protected的区别就在继承的时候体现了:
不同点:派生类可以访问protected属性不可以访问private属性。
相同点:对象都没有访问权限。
malloc在其内存分配失败时返回的是一个NULL指针。(安全起见还需要将指针置空)
默认类型转换:当出现在表达式里时,有符号和无符号的char和short都会被自动转换为int类型,在需要的情况下,将自动转换为unsigned int类型。
printf("%-6.2e\n",x);
%:表示格式说明的起始符号,也是转义符号,有一题 printf(“%%%%”)输出几个?答案输出%% 两个
-:有-表示左对齐输出,如省略表示右对齐输出
0:有0表示指定空位填0,如省略表示指定空位不填
m.n :m指域宽,即对应的输出项在输出设备上所占的字符数;n指精度,用于说明输出的实型数的小数位数。没有指定n时,隐含的精度为n==6位
e:表示以指数形式输出实数。
9.形参与实参类型不一致,以形参类型为准。
11.setbase格式控制符既可以用于输入也可以用于输出,作用为将数字转换为n(n=8,10,16)进制。(需要iostream和iomanip头文件)
12.已初始化的static变量分配在.data段,未初始化的static变量分配在.bss段。
2019/6/19
1.编辑:也就是编写C/C++程序。
2.预处理:相当于根据预处理指令组装新的C/C++程序。经过预处理,会产生一个没有宏定义,没有条件编译指令,没有特殊符号的输出文件,这个文件的含义同原本的文件无异,只是内容上有所不同。
3.编译:将预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后,产生相应的汇编代码文件(调试状态)。
4.链接:通过链接器将一个个目标文件(或许还会有库文件)链接在一起生成一个完整的可执行程序。 链接程序的主要工作就是将有关的目标文件彼此相连接,也就是将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体。在此过程中会发现被调用的函数未被定义。
2019/6/24
2.const int i是在编译阶段做到i只读的。
3. malloc,calloc,realloc,free属于C函数库,而new/delete则是C++操作符(也是函数实现);
多个-alloc的比较:
alloc:唯一在栈上申请内存的,无需释放;
malloc:在堆上申请内存,最常用;
calloc:相当于malloc申请内存加上自动初始化为0;
realloc:将原本申请的内存区域扩容,参数size大小即为扩容后大小,因此此函数要求size大小必须大于ptr指向的内存大小。函数原型:
#include
extern void *realloc(void *mem_address, unsigned int newsize)
改变mem_address所指内存区域的大小为newsize长度:
①如果原先内存大小后面有足够的空间来扩大mem_adress指向的内存块,则分配额外的内存,并且返回mem_adress;数据后面空闲的空间+原来的内存空间==newsize,那么整体得到的是一块连续的内存。
②如果原先的内存大小后面没有足够的空闲空间用来分配,那么从堆中另外找一块newsize大小的内存。并把原来大小内存空间中的内容复制到newsize中。返回新的mem_address指针。(数据被移动了)。
2019/7/1
1.charstrcat(char,const char*); //第一个参数必须可以修改,第二个参数只读
例:如果p1,p2都是指向字符串常量的指针,strcat(p1,p2)则会出错。
3.局部变量出了花括号范围(即使不是函数或是循环作用域,只是一个空的花括号)就不管用了。
5.#define SEQ后面只有一个参数(SEQ)表示将仅有的一个参数用空串替换(也就是删除)。
2019/7/7
2.构造函数和析构函数都可以抛出异常,但是析构函数不推荐抛出异常。
3.union成员共享一段内存空间,一个union的长度等于各成员中最长的长度;上例子:
这里d.x、d.y和d.s.x的地址是一样的所以给其中一个变量赋值等同于同时给其他变量赋值。
4.无论是在构造函数还是在析构函数中调用自己类的虚函数,虚函数的动态绑定机制都不会生效了。解释:
①类的构造次序是从基类到子类,虚函数显然不会呈现多态;
②类的析构是从子类到基类,当调用析构函数中的虚函数时往往意味着派生类部分已经被析构掉了。
5.signed和unsigned相加都变为unsigned
堆和栈都可以动态分配内存(“执行”过程进行),栈分配使用函数alloc()函数,且内存由编译器自己释放;堆在释放内存的时候会产生碎片。
6.派生类需要使用成员初始化列表:①成员变量或基类没有声明默认构造函数;
②const成员变量;
③引用类型的成员变量;
PS:数据成员按类中的声明顺序来初始化,而不是初始化列表中的出现顺序,这里要区别派生类的继承列表(构造和析构函数的调用顺序);
2019/7/8
1.TCP_NODELAY(TCP的套接字)与nagle算法的开启和关闭有关。
Nagle算法:避免网络中充塞小封包,从而提高网络利用率。
2.throw(抛出异常的关键字)、
try(尝试执行可能有异常代码的关键字)、
catch(捕获异常的关键字)多级catch捕获不同级别的异常;
被捕获的异常可以再次抛出;
4.C语言执行默认从main函数开始。(入口地址可以修改)
“死代码”主要指的是①执行不到的代码②执行的到但是毫无作用的代码
2019/7/10
1.在C++中,每个成员函数都有一个this指针,不同类中的成员函数指针不一样,所以可以根据指针类型来识别不同类中定义的虚函数版本。
2.if[ $2 -a $2 = ”test”]中-a是并且的意思。(-a:且 -o:或 !:非)
PS:不晓得是什么意思;
3.int main(int argc,char **argv)
第一个参数:记录从命令行输入的参数个数;
第二个参数:字符型指针数组,每一个元素分别指向命令行输入的各字符串。
4.“%”运算符两边必须为整型数据。
5.#include命令的功能是在命令处插入一个文本文件。(切记不是在文件首部插入一个头文件)原理:预处理器在发现#include后就会寻找<>里面的文件名(如果文件存在),并且把这个文件的内容包含到当前的文件中,被包含文件中的文本将会替换源码文件中#include指令。
int main(void){ int i; for (i = 0; i < 2; i++) { fork(); printf("-"); } return 0; }
7.格式控制符必须小写
8.构造函数为什么不能声明为虚函数?析构函数为什么最好声明为虚函数?
答(1):①反证,创建对象时要先确定对象的类型,虚函数是在运行时确定其类型的,在构造一个对象时,由于对象还没有创建成功,所以编译器无法知道对象的实际类型。②虚函数对应一个虚表,虚表指针存储在对象的内存空间,如果构造函数为虚,那么就需要虚表来调用,可是对象还没有实例化,也就是还没有内存空间,虚表也就不存在,所以构造函数不能为虚。
答(2):析构函数声明为虚函数,那么派生类的析构函数也就自动成为了虚函数;当delete掉一个指向派生类对象的基类指针时,如果基类指针不为虚那么派生类的析构函数无法被调用,也就是说仅仅析构掉了对象继承基类的那部分,自己的那部分没有被析构从而造成内存泄漏。
2019/7/11
1.三个变量共同占用四个字节:
struct s{ int x: 3; int y: 4; int z: 5; double a;}Sizeof(s)为16字节
2.饿汉式单实例模式(一开始就实例化)
基本特点: ①定义类的时候就把静态类型的类类型指针指向新的对象;
②在访问量较大或者可能访问的线程较多时,采用饿汉式有更好的性能;
③空间换时间的方法。
线程安全的懒汉式单实例模式(不到万不得已不实例化)
基本特点: ①首次用到类实例才去实例化;
②访问量较小或者不访问,采用懒汉式实现;
③时间换取空间。
2019/7/14
2019/8/28
1.后缀表达式(逆波兰式)
规则:从左到右遍历每个字符和符号,遇到数字即进栈,遇到符号则处栈顶的两个数字出栈进行运算,运算结果再进栈,直到获得结果。
PS:先入栈的作为被减(+*/)数。
2.中缀表达式
规则:(编程过程中第一个一定是数字)先乘除再加减,就是正常的数学表达式;
3.前缀表达式(波兰式)
规则:和后缀表达式没有本质区别,仅仅表达形式不同,运算符写在前面。
4.为什么要使用(逆)波兰式?
计算机觉得中缀表达式是非常复杂的结构,(逆)波兰式是非常简单的结构,因为计算机的内存结构普遍是栈式结构,而波兰式在存储和计算上正好都具有栈的特性;
5.TCP与UDP的区别?
①TCP是面向连接的协议,它有确认重传机制和流量控制机制;
UDP是非面向连接的协议,传送数据总是尽力而为的,重传由上层协议或者connect()控制。
②从头部结构来说,TCP因为有选项部分,所以有首部长度字段;
UDP没有选项部分,所以不需要首部长度字段。
③TCP的checksum(总和校验码)部分是必须的;UDP的选项部分是可选的,不填充的话默认全为0;
④TCP需要避免分段,因为重传的机制本来就浪费了一些带宽,一旦出现分段,那么重传会大量增加,浪费大量带宽和严重降低传输效率。
⑤从传输数据的方式来说,TCP是基于数据流传输的,所以应用程序产生的全体数据和真正发送的单个IP数据报没有太大的关系;UDP是面向数据报的传输层协议,进程的每一个操作正好产生一个UDP数据报,并组装成一个IP数据报发送;
6.udp协议发送给目标机数据包后,怎么知道目标机是否得到了数据包?