冷门知识

记录一下自己在学习和面试时让我头脑模糊的知识。

结构体对齐

有点无聊的知识,不过笔试常考。要想算一个结构体有多少字节数,有几个要点:

  1. 数据成员对齐规则:结构(struct)(或联合(union))的数据成员,第一个数据成员放在offset为0的地方,每个数据成员存储的起始位置要从该成员(每个成员本身)大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。

  2. 结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储.(struct a里存有struct b,b里有char,int ,double等元素,那b应该从8的整数倍开始存储.)

  3. 收尾工作:结构体的总大小,也就是sizeof的结果,.必须是其内部最大成员的整数倍(结构体成员以最大成员为代表).不足的要补齐.

各个数据类型的字节数

32,64位编译环境下的 sizeof 问题经常出现。不过,两者环境下的数据类型所占字节数都是一样的,

char:1字节;
 
short:2字节;
 
int:4字节;
 
long long:8字节;
 
float:4字节;
 
double:8字节;
 
long double:8字节;

不同的是:指针类型和 long

  • 32位:指针类型占4个字节,long 占 4个字节。

  • 64位:指针类型占8个字节,long 占 8个字节。

静态变量只会初始化一次

void foo() {
    static char *p = new char[128];
}

在多次调用 foo 函数时,p 只会被初始化一次,不是全局变量那样可以随意初始化。可以反向理解为 static 语句只会被执行一次,执行完之后随机删除。

C++ 命名规则

合法标识符规则如下:

  1. 由数字,字母,下划线组成;

  2. 不能由数字开头。即第一个字符只能是字母或下划线。

  3. 区分大小写。

  4. 不可与系统关键字相同。

const 限定符

  • _之前,则const限定的是_ptr而不限定ptr。也就是说,ptr可以改变其所指向的对象,但不能通过该指针修改其所指向对象的值。

  • 若const限定符在_之后,则const限定的是ptr而不限定_ptr。也就是说,ptr不可以改变其所指向的对象,但能通过该指针修改其所指向对象的值。

  • 若在_之前有const限定符且在_之后也有const限定符,则ptr与*ptr都被限定。也就是说,ptr既不可以改变其所指向的对象,也不能通过该指针修改其所指向对象的值。

  • 另外:只要 在*之后有 const限定符,则该指针在声明时必须初始化。

fopen 那些模式

ModelAction

r

以只读方式打开文件,该文件必须存在。

r+

以读/写方式打开文件,该文件必须存在。

rb+

以读/写方式打开一个二进制文件,只允许读/写数据。

rt+

以读/写方式打开一个文本文件,允许读和写。

w

打开只写文件,若文件存在则长度清为 0,即该文件内容消失,若不存在则创建该文件。

w+

打开可读/写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。

a

以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留(EOF 符保留)。

a+

以附加方式打开可读/写的文件。若文件不存在,则会建立该文件,如果文件存在,则写入的数据会被加到文件尾后,即文件原先的内容会被保留(原来的 EOF 符不保留)。

wb

以只写方式打开或新建一个二进制文件,只允许写数据。

wb+

以读/写方式打开或建立一个二进制文件,允许读和写。

wt+

以读/写方式打开或建立一个文本文件,允许读写。

at+

以读/写方式打开一个文本文件,允许读或在文本末追加数据。

ab+

以读/写方式打开一个二进制文件,允许读或在文件末追加数据。

sizeof

sizeof 是 C 语言中的一个操作符 (operator), 不是函数调用 , 简单的说其作用就是返回一个对象或者类型所占的内存字节数。

对象的默认拷贝构造是浅拷贝,对于指针只会赋值,不会基于空间。

fork 和 printf 的爱恨情仇

print输出字符串如果不是一"\n"为结尾的话,就不会马上输出,而是保存在缓存区中。fork函数在创建子进程的时候会复制缓存区的数据。

ios

ios类派生istream类和ostream类,istream类和ostream类共同派生出iostream类,cin和cout都是iostream类的对象

静态链接库和动态链接库

静态链接库的优点

  • 代码装载速度快,执行速度略比动态链接库快;

  • 只需保证在开发者的计算机中有正确的.LIB文件,在以二进制形式发布程序时不需考虑在用户的计算机上.LIB文件是否存在及版本问题,可避免DLL地狱等问题。

动态链接库的优点

  • 更加节省内存并减少页面交换;

  • DLL文件与EXE文件独立,只要输出接口不变(即名称、参数、返回值类型和调用约定不变),更换DLL文件不会对EXE文件造成任何影响,因而极大地提高了可维护性和可扩展性;

  • 不同编程语言编写的程序只要按照函数调用约定就可以调用同一个DLL函数;

  • 适用于大规模的软件开发,使开发过程独立、耦合度小,便于不同开发者和开发组织之间进行开发和测试。

不足之处

  • 使用静态链接生成的可执行文件体积较大,包含相同的公共代码,造成浪费;

  • 使用动态链接库的应用程序不是自完备的,它依赖的DLL模块也要存在,如果使用载入时动态链接,程序启动时发现DLL不存在,系统将终止程序并给出错误信息。而使用运行时动态链接,系统不会终止,但由于DLL中的导出函数不可用,程序会加载失败;速度比静态链接慢。当某个模块更新后,如果新模块与旧的模块不兼容,那么那些需要该模块才能运行的软件,统统撕掉。这在早期Windows中很常见。

格式化输出

使用操作符对数据进行格式输出时,必须包含iomanip.h头文件

基类指针指向派生类

若想调用派生类虚函数,直接调用。 若想调用基类虚函数,添加域名。

static 变量初始化(出自此处)

编译时初始化:如果静态变量本身是基本数据类型(POD),且初始化值是常量,那么这个初始化过程是在编译期间完成的。

static int *p = new int[1024];

int x = 3;
int y = 4;
static int z = x + y;

加载时初始化:程序被加载时立即进行的初始化。这个初始化发生在main函数之前。即使程序任何地方都没访问过该变量, 仍然会进行初始化,因此形象地称之为"饿汉式初始化"。

  1. 静态变量是一个基本数据类型,但是初始值非常量

  2. 静态变量是一个类对象,这种情况下即使是使用常量初始化,也是加载时初始化,而不是编译时初始化

static std::string str = "Hello world !";

class MyClass {
public:	
	MyClass();    
	MyClass(int a, int b)
;};

static MyClass* MyClass1 = new MyClass();
static MyClass MyClass2;

运行时初始化:这个初始化发生在变量第一次被引用。也就是说,从程序执行模型角度看,程序所在进程空间中,哪个线程先访问了这个变量,就是哪个线程来初始化这个变量。因此,相对于加载初始化来说,这种初始化是把真正的初始化动作推迟到第一次被访问时,因而形象地称为"懒汉式初始化"。

int myfunc()
{     	
    static std::string msg = "hello world !";    //运行时初始化
}

拷贝构造函数和赋值运算符(出自此处

拷贝构造函数和赋值运算符的行为比较相似,都是将一个对象的值复制给另一个对象;但是其结果却有些不同,

拷贝构造函数使用传入对象的值生成一个新的对象的实例,而赋值运算符是将对象的值复制给一个已经存在的实例。

这种区别从两者的名字也可以很轻易的分辨出来,拷贝构造函数也是一种构造函数,那么它的功能就是创建一个新的对象实例;赋值运算符是执行某种运算,将一个对象的值复制给一个已经存在的实例。

调用的是拷贝构造函数还是赋值运算符,主要是看是否有新的对象实例产生。如果产生了新的对象实例,那调用的就是拷贝构造函数;如果没有,那就是对已有的对象赋值,调用的是赋值运算符。

<<<<<<< HEAD

C 和 C++ 中结构体的区别

  • C中的结构体没有函数 但是C++的结构体可以有函数。

  • C++的结构体可以被类取代,因为类相对于结构体 具有封装性等优势。 C++中结构体与类的区别:

  • 结构体中的成员访问权限不声明时候默认是 公开的;而类 是私有的

#import 和 #include 的区别

三个文件:文件A.h 文件B.h 文件C.h 文件C.h需要引入A.h、B.h ,文件B.h需要引入文件A.h,这样就重复引用了A.h两次,使用#import可以进行优化。

  • Objective-C 中 #import 和 #include 的区别: 预编译指令 Objective-C:#import C,C++:#include #import由gcc编译器支持 在 Objective-C 中,#import 被当成 #include 指令的改良版本来使用。除此之外,#import 确定一个文件只能被导入一次,这使你在递归包含中不会出现问题。 使用哪一个还是由你来决定。一般来说,在导入 Objective-C 头文件的时候使用 #import,包含 C 头文件时使用 #include。 #import比起#include的好处就是不会引起交叉编译

编译器自动生成的函数

空类编译器会自动生成一个默认的无参构造函数、默认的析构函数、默认的拷贝构造函数以及默认赋值函数。

最后更新于