C/C++读书笔记

来源:互联网 发布:antminer 代抢软件 编辑:程序博客网 时间:2024/04/29 18:00

C/C++基础又忘记了,以前做的笔记没在身边还是写在网上。主要是根据《C面试宝典》、《C++面试宝典》、《程序员面试宝典》总结。以前不想看面试书现在迫不得已了,最后再加点《C++ Primer》的知识点。

----------------------------------------------------------------------------------------------------------------------------------------------

第1部分 C/C++程序设计

第1章 程序设计基本概念

1.1 赋值语句

x ==(y=z=2);cout<<::val<<endl;

1.2 i++

题1:

i? i++ : ++j;    //前自增、后自增
!a && b++;      //短路原则x+++y;    // (x++) + y 优先级

题2:x = x+1, x += 1, x++,哪个效率最高?为什么?

解析:

x = x+1最低,因为它执行过程如下:

1> 读取右x的地址;

2> x+1;

3> 读取左x的地址;

4> 将右值传给左边的x

x += 1其次,其执行过程如下:

1> 读取右x的地址;

2> x+1;

3> 将得到的值传给x。

x++效率最高,其执行过程如下:

1> 读取右x的地址;

2> x自增1.

1.3 类型转换

隐式类型转换发生在下列这些典型情况下:

1> 在混合类型的算术表达式中;

2> 用一种类型的表达式赋值给另一种类型的对象;

3> 把一个表达式传递给一个函数,调用表达式的类型与形式参数的类型不相同;

4> 从一个函数返回一个表达式的类型与返回类型不同。

算术转换保证了二院操作符被提升为共同的类型,然后再用它表示结果的类型。两个通用的指导原则如下:

1> 为防止精度损失,如果必要的话,类型总是被提升为较宽的类型;

2> 所有含有小于整形的算术表达式在计算之前其类型都会被转换成整形。

这些规则定义了一个类型转换层次结构。long double、double、float。如果一个操作数的类型是long double,那么另一个操作数无论是什么类型都将被换成long double。依次就是double和float。

否则如果两个操作数都不是3中浮点类型之一,它们一定是某种整值类型。在确定共同的目标提升类型之前,编译前将所有小于int的整值类型上施加一个被称为整值提升的过程。

如果一个操作数是long型而另一个是unsigned int型,那么只有机器上的long型的长度足以存放unsigned int的所有值时,unsigned int才会被转换为long型,否则两个操作数都被提升为unsigned long型。若两个操作数都不是long型而其中一个是unsigned int型,则另一个也被转换成unsigned int型,否则两个操作数一定都是int型。

1.4 a、b交换

题1:不用判断语句找出两个数中间比较大的。

解:int max = ((a+b) + abs(a-b) )>>1;

题2:交换a、b,不使用任何中间变量

解:a ^= b; b ^= a; a ^= b;//这种方式不用考虑越界情况。

1.5 C和C++的关系

题1:在C++程序中调用被C编译器编译后的函数,为什么要加extern "C"?

解:C++语言支持函数重载,C语言不支持函数重载。函数被C++编译后在库中的名字与C语言的不同。C++提供了C连接交换制定符号extern"C"解决名字匹配问题。

题2:头文件中的ifndef/define/endif是做什么用的?

解:防止该头文件被重复引用。

题3:#include<filename.h>和#include "filename.h"有什么区别?

解:对前者,编译器从标准库路径开始搜索;后者从用户的工作路径开始搜索。

题4:如何判断一段程序是由C编译程序还是由C++编译程序编译的?

解:C++编译时定义了 _cplusplus;C编译时定义了_STDC_

题5:new、delete、malloc、free

解:new、delete调用构造函数和析构函数

题6:C++是不是类型安全的

解:不是。两个不同类型的指针之间可以强制转化。

题7:C++中的4种类型转换方式

解:static_cast、dynamic_cast、reinterpret_cast

题8:对于一个频繁使用的小函数,C/C++中的实现分别是什么

解:宏,inline

1.6 程序设计其他问题

题1:程序执行以前还会执行什么

解:全局对象的构造函数会在main函数之前执行。

题2:main函数执行完毕后,是否可能会再执行一段代码?给出说明。

解:atexit( f );  //f为函数名

题3:程序的内存分配

解:1>全局区(静态区)(static) -- 全局变量和静态变量的存储时放在一块的,初始化的全局变量和静态变量在一块区域,为初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放

2>堆区(heap) -- 一般有程序员分配释放,若程序员不释放,程序结束时可能由OS回收。

3>栈区(stack) -- 有编译器自动分配释放,存放函数的参数值,局部变量的值等。操作模式累世数据结构中的栈。

4>文字常量区 -- 常量字符串就是放这里的。程序结束后有系统释放。

5>程序代码区 -- 存放函数体的二进制代码。

题4:堆栈溢出一般是由什么原因导致的

解:1>没有回收垃圾资源

2>层次太深的递归调用


1.7 C/C++

题1:static

解:1>函数体内声明为static的变量在这一函数被调用过程中维持不变。

        2>static全局变量,只能被本文件访问

3>类static函数值可被本类访问。

题2:不能做switch参数类型

解:实型,必须为能转换为int的类型做参数

题3:如何引用一个已经定义过的全局变量

解:可以用引用头文件的方式,也可以用extern关键字,如果用引用头文件方式会在编译期间报错;如果用extern方式,会在连接期间报错。

题4:全局变量可不可以定义在多个.c头文件中

解:可以,在不同的C文件中以static形式来声明同名全局变量。可以在不同的C文件中声明同名的全局变量,前提是其中智能有一个C文件对此变量赋初值,此时连接不会出错。

题5:什么是预编译,何时需要预编译

解:预编译又称预处理,是做代码文本的替换工作。C提供的预处理功能主要有以下:宏定义、文件包含、条件编译。

第2章 预处理、const与sizeof

2.1 宏定义

宏定义函数记着全部加上括号,有时即使加上了也会错;宏定义变量也记着加上括号。

与const、inline、enum的区别(见effective C++)。

2.2 const

2.3 sizeof

1> 数组

char ss1[] = "012345678\n";char ss2[100] = "0123456789";sizeof(ss1)、sizeof(ss2)、sizeof(ss1[0])、sizeof(&ss1[0])
2> 指针

char s = "0123456789";sizeof(s);void f(char s[]){    sizeof(s);}
3> 指针数组

int **p[3][4];


4>结构体

struct node{    int a;    char ch;    double d;};//各变量要对齐,最后总长度也要对齐struct node{};    //sizeof(node) = 1;
5>类

class A{public:    static int a = 4;    char c;};    //静态变量不在栈中,值算char的长度class A{public:    virtual ~A();};    //virtual函数是个联合指针,至少有一个virtuan就为4
sizeof与strlen的区别:

char *p = "012345689";    //10char s[100] = "0123456789";    //10void f(char a[]){    strlen(s);    //不是4了}

第3章 指针与引用

3.1 指针基本问题

题1:指针和引用的差别

解:1>引用必须被初始化,指针不必

2>引用初始化后不能被改变,指针可以改变所指的对象

3>不存在指向空值的引用,但是存在指向空值的指针

题2:数组和指针的区别

解:1>修改内容上的差别 -- s[3] p+3

2>sizeof长度不同

题3:各种指针

3.2 传递动态内存

题1

void getMemory(char *p, int num){    p = (char *) malloc(sizeof(char) * num);};int main(){    char *str = NULL;    getMemory(str,100);    strcpy(str,"hello");}
分析:getMemory中的*p实际上是主函数中的str的一个副本,编译器总是要为函数的每个参数制作临时副本。在本例中,p申请了新的内存,只是把p所指的内存地址改变了,但是str丝毫未变。因为函数没有返回值,因此str并不指向p所申请的那段内存,所以函数并不能输出任何东西。实际上,没执行一次函数就会申请一块内存,但申请的内存却不能有效释放,结果是内存一直被霸占,最终造成内存泄露。
如果一定要用指针参数去申请内存,那么应该采用指向指针的指针,传str的地址给函数。由于“指向指针的指针”这个概念不容易理解,我们可以用函数返回值来传递动态内存。这种方法更加简单。

解:崩溃,str一直是NULL。

题2

char* getMemory(){    char p[] = "hello world";    return p;}int main(){    char *str = NULL;    str = getMemory();    cout<<str;}
解:可能乱码,也可能正常输出。因为函数返回的是指向“栈内存”的指针,该指针的地址不是NULL,但其原来的内容已经被清除,新内容不可知。
题3:指针与引用参与的参数传递问题
void swap(int *a, int &b, int c){    c = *a;    b = 3;    *a = a;}

3.3 函数指针、指针数组和数组指针

1> double (**p)[10];    p是一个二级指针,它指向的是一个一位数组的指针,数组的元素都是double2> double *(*p)[10];    p是一个指针,它指向一个一维数组,数组元素都是double*。3> double (*f[10])();    f是一个数组,f有10个元素,元素都是函数指针,指向的函数类型是没有参数且返回double的函数。4> int *((*b)[10]);    同int *(*b)[10];5> int (*fun)(int);    函数指针6> int (*(*f)(int,int))(int);    f是一个函数指针,指向的函数的类型是有两个int参数并且返回一个函数指针的函数,返回的函数指针指向有一个int参数且返回int的函数。

题1:int a[9]; int *p; p = a;

请问哪一个不能表示a[1]; A. p+1    B. p++    C. a++    D. a+1

解:数组名a作为代表数组的首地址,是一个常量指针。所以a++是错的。

3.4 迷途指针

编程中一种很难发现的错误时迷途指针。迷途指针也叫悬浮指针、失控指针,是当对一个指针进行delete操作后--这样会释放它所指向的内存--并没有把它设置为空时产生的。而后,如果你没有重新赋值就试图再次使用该指针,引起的结果是不可预料的。程序崩溃算你走运。为了安全起见,在删除一个指针后,把它设为空指针这样就可以消除它的危害。

题1:空指针和迷途指针的区别。

解:当delete一个指针的时候,实际上仅是让编译器释放内存,但指针本身依然存在。这时他就是一个迷途指针。当使用一下语句时,可以把迷途指针改为空指针:p = 0;

通常,如果在删除一个指针后又把它删除一次,程序就会变得非常不稳定,任何情况都有可能发生。但是如果你只是删除了一个空指针,则什么什么事都不会发生,这样做非常安全。

使用迷途指针或空指针是非法的,而且有可能造成程序崩溃。如果指针式空指针,尽管同样是崩溃,但它同迷途指针造成对象崩溃相比是一种可预料的崩溃。这样调试起来会方便很多。

题2:malloc/free和new/delete的区别

解:malloc/free是C/C++语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。对于非内部数据类型的对象而言,光用malloc/free无法满足动态对象的要求。new/delete在对象创建和消亡时可以调用构造函数和析构函数。

3.5 指针和句柄

句柄是一个unsigned int,实际上是Windows在内存中维护的一个对象(窗口等)内存物理地址列表的整数索引。因为Windows的内存管理经常会将当前空闲对象的内存释放掉,当需要访问时再重新提交到物理内存,所以对象的物理地址是变化的,不允许程序直接通过物理地址来访问对象。句柄是一种指向指针的指针。指针则标记某个物理内存地址,是不同的概念。

第4章 STL与容器

vector指针失效的问题。类模板:template <typename T>

第5章 类和对象

题1:OOP的基本概念(不记这题最好,都是考下面那个)

解:类、对象和继承

题2:面向对象的特点

解:封装、继承和多态。

题3:class和struct的区别

解:唯一区别为struct默认是public而class是private。继承虚函数等都有。

题4:什么函数不能声明为虚函数

解:构造函数

题5:联合体共用体

解:低位低地址,高位高地址

题6:哪几种情况只能用初始化列表

解:const、引用等就需要

第6章 继承与接口

题1:类成员函数的重载、覆盖和隐藏区别

解:a.成员函数重载的特征:

1>相同的范围

2>函数名字相同

3>参数不同

4>virtual关键字可有可无

b.覆盖是指派生类函数覆盖基类函数,特征是:

1>不同的范围

2>函数名字相同

3>参数相同

4>基类必须有virtual

c.隐藏式指派生类的函数屏蔽了与其同名的基类函数,其规则如下:

1>如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字都隐藏。

2>如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。隐藏。

题2:多态

解:必须通过传入基类的引用或者指针才能实现多态。

第7章 位运算

第2部分 数据结构、设计模式和排序算法

第3部分 操作系统、数据库和网络

题1:进程与线程的区别

解:进程:进程是程序的一次执行,线程可以理解为进程中执行的一段程序片段。

1>地址空间:进程独立内存空间;线程共享进程的地址空间;

2>进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源;

3>线程是处理器调度的基本单位,进程不是。

4>二者均可并发执行

题2:OSI七层和TCP/IP五层

解:物理层、数据链路层(交换机)、网络层(路由器)、传输层、会话层、表示层、应用层。

物理层、数据链路层、网络层、传输层、应用层。

题3:实时系统的基本特征

解:在特定时间内完成特定的任务,实时性与可靠性。

题4:Internet采用哪种网络协议?主要层次

解:TCP/IP协议,物理层、数据链路层、网络层、传输层、应用层。

题5:Internet物理地址和IP地址转换采用什么协议?

解:ARP地址解析协议

题6:IP地址的编码分为哪两部分

解:网络号和主机号。不过要和“子网掩码”按位与之后才能区分

题7:进程间通信类型:

解:







原创粉丝点击