C/C++基础刷题易错(长期更新)

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需为二级指针。

  1. 为了提高程序的运行速度,在函数中对于整型或指针可以使用(register)型的变量。
    auto 变量类型推演;
    register 建议编译器将该变量放入CPU的寄存器中(寄存器速度比内存快),例:逻 辑中需要频繁操作的变量;
    使用方法:register int instance;
    static 静态变量;
    extern 声明变量,常用于多文件需要使用同一变量时;

3.Test b();只是声明了一个函数,并没有创建对象,所以通过b来调用成员函数会出错。
PS:Test为类名。

  1. C 语言程序中,若对函数类型未加显式说明,则函数的隐含说明类型为(int)。

  2. 一个算法应该具有以下5个特性:①有穷性、②确定性、③可行性、④有0个或多个输入、⑤有一个或多个输出。因此一个算法可以没有输入(程序的功能确定),但必须要有输出,没有输出的算法是没有意义的。

  3. gets()遇到空格或tab不会结束,只有遇到回车才结束;scanf遇到空格,tab,回车即结束输入。

  4. 条件为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 的倍数
    }

  5. (动态类型转换)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 (b); // 将本质是B的东西,转变为子类对象,所以失败。即c=0.

更灵活一点:
A(基类) <- B <- C <- D <- EA* a = new C; // A本质为CB* b = dynamic_cast (a); // 成功,因为B是C的父类E* e = dynamic_cast (a); // 失败,因为E是C的子类

2019/6/15

  1. 类的私有成员只能通过本类成员函数或者友元函数访问,本类对象不能直接访问。当存在类继承时,无论继承时是公有继承,保护继承还是私有继承,派生类的成员函数和派生对象均不能直接访问基类私有成员。(私有成员还是会被继承的)

  2. 函数模板必须由编译器根据程序员的调用类型实例化为可执行的函数。
    类模板的成员函数都是函数模板。
    没使用过的成员函数(即函数模板)不会被实例化。(也就是不会占用内存)

  3. 无论是static还是非static的全局变量,如果不加限制随意访问的话容易出现同步问题。
    无论是static还是非static的局部变量,每个线程都是私有的,其他线程不会对其进行干扰。

  4. 析构函数没有参数列表,无法重载(overload),但是可以重写(override)。

  5. private和protected的区别就在继承的时候体现了:
    不同点:派生类可以访问protected属性不可以访问private属性。
    相同点:对象都没有访问权限。

  6. malloc在其内存分配失败时返回的是一个NULL指针。(安全起见还需要将指针置空)

  7. 默认类型转换:当出现在表达式里时,有符号和无符号的char和short都会被自动转换为int类型,在需要的情况下,将自动转换为unsigned int类型。

  8. printf("%-6.2e\n",x);
    %:表示格式说明的起始符号,也是转义符号,有一题 printf(“%%%%”)输出几个?答案输出%% 两个
    -:有-表示左对齐输出,如省略表示右对齐输出
    0:有0表示指定空位填0,如省略表示指定空位不填
    m.n :m指域宽,即对应的输出项在输出设备上所占的字符数;n指精度,用于说明输出的实型数的小数位数。没有指定n时,隐含的精度为n==6位
    e:表示以指数形式输出实数。

9.形参与实参类型不一致,以形参类型为准。

  1. 通用多态分为参数多态(包含函数模板和类模板)和包含多态(virtual)。---------允许对不同类型的值执行相同的代码;
    特定多态分为重载多态(重载)和强制多态(强制类型转换)。-----------只对有限数量的类型有效,对不同类型的值执行不同的代码。

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

  1. 函数调用语句fseek(fp,-20L,2);表示将文件位置指针从文件末尾处退后20 个字节,fseek函数的第三个参数有3个值可选:0表示文件的开始处,1表示当前位置,2表示文件尾,位移量为负,表示将文件位置指针往前移动(向文件头方向)。
    fseek(fp,位移量,起始点)

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指针。(数据被移动了)。

  1. feof( fp)的函数返回值未到文件末尾返回0,到了返回非0值。

2019/7/1
1.charstrcat(char,const char*); //第一个参数必须可以修改,第二个参数只读
例:如果p1,p2都是指向字符串常量的指针,strcat(p1,p2)则会出错。

  1. 事件、临界区、互斥量、信号量可以实现线程同步。
    事件:用于通知其他线程事件已发生,从而启动线程
    信号量:可以对资源进行计数,允许多个线程共享资源,限制了同一时刻多个线程能访问的最大资源数。
    临界区:在任一时刻仅允许一个线程使用临界区资源。四者中仅有临界区不是内核对象。适合于数据访问的控制。
    互斥量:拥有互斥量才可以访问共享资源,因此共享资源不会被多个线程所共享。

3.局部变量出了花括号范围(即使不是函数或是循环作用域,只是一个空的花括号)就不管用了。

  1. 编译错误是编译阶段发生的错误;
    连接错误是连接阶段发生的错误;
    运行错误是运行阶段发生的错误;
    以上三种错误编译器都会给出,机器帮助我们发现。
    逻辑错误机器不能帮助我们发现,也就是俗称的bug,
    逻辑错误需要经过测试或使用积累来发现。
    编过程序都经历过“溢出”现象,是编译器可以发现的。

5.#define SEQ后面只有一个参数(SEQ)表示将仅有的一个参数用空串替换(也就是删除)。

  1. a.成员函数被重载的特征:(同类同名不同参)
    (1)相同的范围(在同一个类中);
    (2)函数名字相同;
    (3)参数不同;(参数类型或者个数不同)
    (4)virtual 关键字可有可无。
    b.覆盖(重写)是指派生类函数覆盖基类函数,特征是:(异类同名同参基含虚)
    (1)不同的范围(分别位于派生类与基类);
    (2)函数名字相同;
    (3)参数相同;
    (4)基类函数必须有virtual 关键字。
    c.“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
    (1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。(异类同名不同参)
    (2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual 关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)(异类同名同参基不含虚)

2019/7/7

  1. 在一个双向循环链表中,指针p所指向的节点(非尾节点)之后插入指针s指向的节点,其修改指针的操作是:s->prev=p; s->next=p->next; p->next->prev=s; p->next=s;
    插入一个结点的时候,要先把待插入结点的前后安排好,具体为s->prev…,s->next…。再将已经安排好的待插入结点与队列中的结点相关联。

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捕获不同级别的异常;
被捕获的异常可以再次抛出;

  1. 中间件是一种独立的系统软件或服务程序,分布式应用软件借助这种软件在不同的技术之间共享资源。中间件位于客户机/ 服务器的操作系统之上,管理计算机资源和网络通讯。是连接两个独立应用程序或独立系统的软件。相连接的系统,即使它们具有不同的接口,但通过中间件相互之间仍能交换信息。执行中间件的一个关键途径是信息传递。通过中间件,应用程序可以工作于多平台或OS环境。
    中间件特点的描述:
    ①中间件应支持标准的协议和接口
    ②中间件可运行于多种硬件和操作系统平台上
    ③跨越网络,硬件,操作系统平台的应用或服务可通过中间件透明交互。

4.C语言执行默认从main函数开始。(入口地址可以修改)
“死代码”主要指的是①执行不到的代码②执行的到但是毫无作用的代码

  1. 从输入流中提取指定长度的字节序列的函数read()。

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指令。

  1. fork()系统调用是Unix下以自身进程创建子进程的系统调用,一次调用,两次返回,如果返回是0,则是子进程,如果返回值>0,则是父进程(返回值是子进程的pid),这是众为周知的。
    还有一个很重要的东西是,在fork()的调用处,整个父进程空间会原模原样地复制到子进程中,包括指令,变量值,程序调用栈,环境变量,缓冲区,等等。

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

  1. 拷贝初始化不仅在用=定义变量时发生,在下列情形也会发生:
    ①将一个对象作为实参传递给一个非引用类型的形参
    ②从一个返回类型为非引用类型的函数返回一个对象
    ③用花括号列表初始化一个数组中的元素或一个聚合类中的成员
    ④某些类类型还会对它们所分配的对象使用拷贝初始化。如初始化标准库容器或调用其insert或push成员时,容器会对其元素进行拷贝初始化。而emplace创建的元素都是直接初始化。
    ⑤拷贝构造函数被用来初始化非引用类类型参数,所以拷贝构造函数自身的参数必须是引用类型。不然的话,就二者矛盾而无限循环了;为了调用拷贝构造函数,我们必须拷贝它的实参,但为了拷贝实参,我们又需要调用拷贝构造函数。

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协议发送给目标机数据包后,怎么知道目标机是否得到了数据包?

你可能感兴趣的:(C/C++,C,C++,面试)