1.swap(a, b)三种方式:
1.临时变量
2. a = a + b; b = a – b; a = a – b; 可能会导致数据溢出
3.a ^ = b; b ^= a; a ^= b;
两个字符串的交换
void swap(char *& a, char *&b) { char *tmp; tmp = a; a = b; b = tmp; } void swap(char **a, char **b) { char *tmp tmp = *a; *a = *b; *b = *tmp; }
2.extern “c”含义
使c++编译器提供与c连接交换指定的符号,用来解决名字匹配问题。详细:http://baike.baidu.com/view/2816461.htm
3.main函数
main函数代表进程主线程,系统调用c/c++运行期启动函数,进行运行期库进行初始化,之后调用main,main执行完毕后,启动函数再调用exit(),将返回值传给exit(),exit()会调用ExitProcess(),结束进程。
int atexit(void (*func)(void))
atexit()注册的函数类型应为不接受任何参数的void函数,exit调用这些注册函数的顺序与它们 登记时候的顺序相反。同一个函数如若登记多次,则也会被调用多次。
#include <stdio.h>
#include <stdlib.h>
void exit_fn1(void)
{
printf("Exit function #1 called\n");
}
void exit_fn2(void)
{
printf("Exit function #2 called\n");
}
int main(void)
{
/* post exit function #1 */
atexit(exit_fn1);
/* post exit function #2 */
atexit(exit_fn2);
return 0;
}
输出:
Exit function #2 called
Exit function #1 called
进程的终止方式:
有8种方式使进程终止,其中前5种为正常终止 1:从 main 返回
2:调用 exit
3:调用 _exit 或 _Exit
4:最后一个线程从其启动例程返回
5:最后一个线程调用 pthread_exit
异常终止有3种,它们是
6:调用 abort
7:接到一个信号并终止
8:最后一个线程对取消请求做出响应
#include <stdlib.h>
void exit (int status);
void _Exit (int status);
#include <unistd.h>
void _exit (status);
其中调用 _exit,_Exit 都不会调用终止程序
异常终止也不会。
4.宏参数的连接
#define STR(s) #s //s代表字符串
#define CONS(a,b) (int)(a##e##b) //##把两个参数连在一起
STR(ABC); ABC
CONS(1, 2); 100
5.c++ const作用
1.用于定义常量,编译器可对其进行数据静态类型安全检查。
2.修饰形参,值传递改为const & 可提高效率,省去对象的构造 复制 析构过程消耗的时间。
3.修饰函数返回值,返回值不能直接被修改,且只能赋给const修饰符的同类型指针。
4.修饰类成员函数,任何不需要修改数据成员的函数都应该使用const修饰,这样即使不小心调用修改数据的非
const函数,编译器也会报错。
6.static
static全局变量只初始化一次,防止在其他单元中引用。static局部变量只初始化一次。static函数在内存中只有一份。
7.空类,编译器会安插一个char空类,标记它的每个对象,因此为1字节,空类多继承空类,大小仍为1字节。空类虚继承另一个空类,会分配一个指向父类的指针,因此不会再安插一个char,静态变量存储在静态存储区,被类共享,不算入实例化后对象大小。
8.union和struct 对齐(class内变量对齐也类似)
转:
原则1、数据成员对齐规则:结构(struct或联合union)的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。
原则2、结构体作为成员:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储。)
原则3、收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。
这三个原则具体怎样理解呢?我们看下面几个例子,通过实例来加深理解。
例1:struct {
short a1;
short a2;
short a3;
}A;struct{
long a1;
short a2;
}B;sizeof(A) = 6; 这个很好理解,三个short都为2。
sizeof(B) = 8; 这个比是不是比预想的大2个字节?long为4,short为2,整个为8,因为原则3。
例2:struct A{
int a;
char b;
short c;
};struct B{
char b;
int a;
short c;
};sizeof(A) = 8; int为4,char为1,short为2,这里用到了原则1和原则3。
sizeof(B) = 12; 是否超出预想范围?char为1,int为4,short为2,怎么会是12?还是原则1和原则3。
深究一下,为什么是这样,我们可以看看内存里的布局情况。
a b c
A的内存布局:1111, 1*, 11b a c
B的内存布局:1***, 1111, 11**其中星号*表示填充的字节。A中,b后面为何要补充一个字节?因为c为short,其起始位置要为2的倍数,就是原则1。c的后面没有补充,因为b和c正好占用4个字节,整个A占用空间为4的倍数,也就是最大成员int类型的倍数,所以不用补充。
B中,b是char为1,b后面补充了3个字节,因为a是int为4,根据原则1,起始位置要为4的倍数,所以b后面要补充3个字节。c后面补充两个字节,根据原则3,整个B占用空间要为4的倍数,c后面不补充,整个B的空间为10,不符,所以要补充2个字节。
再看一个结构中含有结构成员的例子:
例3:struct A{
int a;
double b;
float c;
};struct B{
char e[2];
int f;
double g;
short h;
struct A i;
};sizeof(A) = 24; 这个比较好理解,int为4,double为8,float为4,总长为8的倍数,补齐,所以整个A为24。
sizeof(B) = 48; 看看B的内存布局。
e f g h i
B的内存布局:11* *, 1111, 11111111, 11 * * * * * *, 1111* * * *, 11111111, 1111 * * * *i其实就是A的内存布局。i的起始位置要为24的倍数,所以h后面要补齐。把B的内存布局弄清楚,有关结构体的对齐方式基本就算掌握了。
以上讲的都是没有#pragma pack宏的情况,如果有#pragma pack宏,对齐方式按照宏的定义来。比如上面的结构体前加#pragma pack(1),内存的布局就会完全改变。sizeof(A) = 16; sizeof(B) = 32;
有了#pragma pack(1),内存不会再遵循原则1和原则3了,按1字节对齐。没错,这不是理想中的没有内存对齐的世界吗。
a b c
A的内存布局:1111, 11111111, 1111e f g h i
B的内存布局:11, 1111, 11111111, 11 , 1111, 11111111, 1111那#pragma pack(2)的结果又是多少呢?#pragma pack(4)呢?留给大家自己思考吧,相信没有问题。
还有一种常见的情况,结构体中含位域字段。位域成员不能单独被取sizeof值。C99规定int、unsigned int和bool可以作为位域类型,但编译器几乎都对此作了扩展,允许其它类型类型的存在。
使用位域的主要目的是压缩存储,其大致规则为:
1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++采取压缩方式;
4) 如果位域字段之间穿插着非位域字段,则不进行压缩;
5) 整个结构体的总大小为最宽基本类型成员大小的整数倍。还是让我们来看看例子。
例4:struct A{
char f1 : 3;
char f2 : 4;
char f3 : 5;
};a b c
A的内存布局:111, 1111 *, 11111 * * *位域类型为char,第1个字节仅能容纳下f1和f2,所以f2被压缩到第1个字节中,而f3只能从下一个字节开始。因此sizeof(A)的结果为2。
例5:struct B{
char f1 : 3;
short f2 : 4;
char f3 : 5;
};由于相邻位域类型不同,在VC6中其sizeof为6,在Dev-C++中为2。
例6:struct C{
char f1 : 3;
char f2;
char f3 : 5;
};非位域字段穿插在其中,不会产生压缩,在VC6和Dev-C++中得到的大小均为3。
考虑一个问题,为什么要设计内存对齐的处理方式呢?如果体系结构是不对齐的,成员将会一个挨一个存储,显然对齐更浪费了空间。那么为什么要使用对齐呢?体系结构的对齐和不对齐,是在时间和空间上的一个权衡。对齐节省了时间。假设一个体系结构的字长为w,那么它同时就假设了在这种体系结构上对宽度为w的数据的处理最频繁也是最重要的。它的设计也是从优先提高对w位数据操作的效率来考虑的。有兴趣的可以google一下,人家就可以跟你解释的,一大堆的道理。
最后顺便提一点,在设计结构体的时候,一般会尊照一个习惯,就是把占用空间小的类型排在前面,占用空间大的类型排在后面,这样可以相对节约一些对齐空间。
9.inline与宏
宏只是简单替换,无法进行参数有效性检测,返回值爷爷不能被强制转换为可转换的何时类型,inline消除了他的缺点,又继承其优点。
inline定义类的内联函数,函数代码放入符号表中,在使用时进行替换,没有了调用开销,效率很高,也会进行参数合法性检查,就像对待真正的函数一样。
二者区别:
1.内联函数在编译时展开,宏在预编译时展开。
2.在编译时,内联函数可以直接嵌入到目标代码中,宏只是简单的文字替换。
3.内联函数可以完成类型检测,语句是否正确等编译功能,宏不具有这样功能。
4.宏不是函数,inline函数是函数。
5.宏易出现参数二义性。
10.引用和指针
引用声明的同时必须被初始化,也只能在声明时被赋值于某个变量。
常量引用: cont int & t = a; t = 1;错误,不可通过常量引用改变目标变量值。非常量引用不能指向常量类型。对于常量类型变量,其引用也必须是常量类型。对于非常量类型,其引用常量非常量即可。
指针和引用的区别:
1.指针定义时可以不必立刻初始化。
2.引用一旦初始化,就不能再指向其他变量。
3.不存在NULL引用。
4.指针使用前要测试合法性 例:if (point == null)
复杂指针声明
用变量a给出下面定义:
定义一个指向有10个整型数的数组指针:int (*a)[10]
定义一个函数指针,该函数有一个整形参数并返回一个整形数 int (*a)(int)
定义一个10个指针数组,指针指向一个整形参数并返回一个整形数的函数 int (*a[10])(int)
注意:不同指针指向同样字符串,一般指向的是同一块数据区的内存