要写纯粹的,脱离低级趣味的 函数!
GNU(GNU’s Not Unix)
GNU操作系统是一种由自由软件构成的类Unix操作系统,该系统基于 Linux 内核,目标在于建立一个完全相容于UNIX的自由软件环境。
GNU工程创始于1984年,旨在开发一个完整 GNU系统,GNU这个名字是 “GNU’s Not Unix!”的递归首字母缩写词,”GNU” 的发音为g’noo,只有一个音节,发音很像“grew”,但需要把其中的 r 音替换为n音,系统的名称,GNU是一个递归的缩写,意为GNU不是Unix的一种致敬Unix的技术思路,而在同一时间说GNU是不同的东西。
CONST
CONST定义的是变量,是不可改变的变量。因为是不可改变的所以需要在定义的时候就进行初始化,因为如果现在不进行初始化,等到后来也就没有办法进行初始化了。
在C语言中如果使用变量(包含const)声明数组的大小在编译的时候可以通过,但是根本就没有办法运行。但是在C++中是可以运行的。定义一个数组必须指定数组元素的个数。C与C++中对const的含义是不同的,在C++中对const的含义做了扩充。
- 编译器扩展:编译器,太智能了。c不允许变量赋值给数组,编译器允许。这就是个例子。
.c与.cpp文件的区别
- .c文件只能够书写C语言代码。.cpp既可以书写C语言程序也可书写C++程序。
- gcc认为.c的为C程序,.cpp的为C++程序;
- g++认为.c的为C++程序,.cpp的为C++程序;
- VC++的编译器cl认为.c的为C程序,.cpp的为C++程序;
C程序与C++程序中同样的函数在编译后的obj文件中的symbol是不同的,所以以C方式编译的obj文件与以C++方式编译的obj文件无法成功链接。
const volatile int a=10;
- const 表示在本段程序中不能对a做修改,任何修改都是非法的。
- volatile 表示另一个程序段则完全有可能修改,因此编译器最好不要做太激进的优化。
- const 的含义是“请作为常量使用”,而并非“放心吧,那肯定是常量”
- volatile 的含义是“请不要做没谱的优化,这个值可能变掉的”,而并非“你可以修改那个值。
- 因此const 与 volatile 本来就不矛盾。
注:
- const修饰的变量不允许在这里修改不代表不允许别处修改。
- 对于非指针非引用的变量,const volatile同时修饰的意义确实不大。
- 两者同时修饰一个对象的典型情况,是用于驱动访问外部设备的只读寄存器。
大端与小端
定义
- 大端:数据的高字节存储在内存空间的低地址上,数据的低字节存储在内存空间的高地址上。
- 小端:数据的高字节存储在内存空间的高地址上,数据的低字节存储在内存空间的低地址上。
如何判断一个系统是大端还是小端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
bool isSystemLittleDian()
{
union test
{
int i;
char a;
}u;
u.i=1;
return u.a==1;
}
int main()
{
if(isSystemLittleDian())
printf("该系统是小端");
else
printf("该系统是大端");
return 0;
}
链接网址
符号
- 编译器的确会将注释剔除,但是不是简单的剔除,而是使用空格代替原来的注释。
- 注释不能够出现在C语言的任何地方。
C语言轻松一刻
- 汇编程序的注释,是以;开头的。贝多芬于1827年去世,而1827的十六进制正是723.注释:R.I.P.V.B.)的意思是:(Rest In Peace,Ludwing Van Neethoven,安息吧,路德维希.凡.贝多芬)
- 大师们把代码写成这样是经典,你把代码写成这样是垃圾。
预处理
#define
- 试图使用宏开始或结束一段注释是不行的。
- 想要很好的使用宏那就不要吝啬括号。
- 空格在宏定义的时候是有效的,在使用宏函数的时候会被编译器忽略掉。
- 整数常量后面接UL表示类型,606024365UL 等价于 ((unsignedlong)606024365)
- 宏的声明周期从
开始,到 ```#undef``` 结束 1
2
3
4
5
6
7
8
9
10
11
12
13
146. 条件编译对于程序的移植和调试是很有用的。
7. #include的路径也有点需要进行说明:include支持相对路径,格式如蚁迹寻踪(.代表当前目录..代表上层目录)
8. 如何通过定义宏来控制源代码的版本?
### [内存对齐](https://baike.baidu.com/item/%E5%86%85%E5%AD%98%E5%AF%B9%E9%BD%90/9537460?fr=aladdin)
### 指正加减运算
```c++
int main()
{
int a[5]={1,2,3,4,5};
int *ptr=(int*)(&a+1);
printf("%d,%d\n",*(a+1),*(ptr-1));
return 0;
}
1 |
|
会产生泄漏的内存就是堆上的内存(这里不讨论资源或者是句柄的泄漏)也就是说由
系列函数或 ```new``` 操作符分配的内存。如果用完之后没有及时 ```free``` 或 ```delete```,这块内存就无法释放,知道整个程序终止。 1
2
3
4
5
6
7
8
94. ```free(p)``` 之后必须进行 ```p=NULL;``` 否则 ```p``` 就变成了一条野狗。
```c++
void whenEnd()
{
free(p);
p=NULL;
}函数就是一些语句的集合。
- C语言中,函数的static局部变量是函数的记忆存储器。建议精良少用static局部变量。
- 有的时候函数不需要返回值,但是为了增加灵活性如支持链式表达,可以附加返回值。如:strcpy函数
1 | int len=strlen(strcpy(strDest,strSource)); |
ASSERT
- 函数声明
1 | void assert( int expression ); |
- assert的作用是现计算表达式 expression ,如果其值为假(即为0),那么它先向stderr打印一条出错信息,然后通过调用 abort 来终止程序运行。
- 在调试结束后,可以通过在包含#include <assert.h>的#ifdef NDEBUG语句之前插入 #define NDEBUG 来禁用assert调用即可。
- 比较好的在程序中使用assert的地方:
- 空指针检查。例如,针对一个函数的参数进行空指针检查。你可以这样使用:assert (pointer != NULL);,产生的错误会像这样:Assertion ‘pointer != ((void *)0)’ failed。这样,当出现空指针时,你的程序就会退出,并很好的给出错误信息。
- 检查函数参数的值。例如,如果一个函数只能在它的一个参数foo为正值的时候被调用,你可以在函数开始时这样写:assert (foo > 0);,这将帮助你检测函数的错误使用,这也给源代码阅读者很清晰的印象,那就是在这里对函数的参数值有限制。
使用原则
每个assert只检验一个条件,因为同时检验多个条件时,如果断言失败,无法直观的判断是哪个条件失败。
1
2
3
4
5//不好
assert(nOffset>=0 && nOffset+nSize<=m_nInfomationSize);
//好
assert(nOffset >= 0);
assert(nOffset+nSize <= m_nInfomationSize);
排序
- 10W个数据进行排序,冒泡46.44S,快排0.8S。
- 1000W个数据进行快排,
- 别看free和delete的名字恶狠狠,其实它们人很好的,它们只是切断了指针和内存之间的联系,并没有把指针干掉。(但是同时让指针成为了野指针)。
指针有一些“似是而非”的特征:
- 指针消亡了,并不表示它所指向的内存会被自动释放。
- 内存释放了,并不表示指针会消失或者变成NULL指针。
- 我就认为程序终止了运行,一切指针都会消亡动态内存会被操作系统回收。既然如此,在程序临终前,就可以不必释放内存、不必将指针设置成NULL了。终于可以偷懒而不会发生错误了。这种想法是错误的,如果别人把那段程序取出来用到其它的地方怎么办?
malloc、free与new、delete
- 使用new为int等内部类型分配一个空间。
1 | int *p=new int[length]; |
使用new为对象数组分配空间,此时只能够使用无参数的构造函数进行分配,不能够使用有参数的构造函数进行分配。
1
Obj *object=new Obj[100];
使用delete释放new分配的对象数组时,不要丢掉[].
41
delete [] object;
malloc、free 与 new、 delete的区别见课本P52讲的太好了。
函数重载
- 如果两个函数的函数名相同不同的仅仅是返回值类型,那么这个函数有的时候能够重载有的时候不能够重载,例如下面的两个函数
1
2void Function(void);
int Function(void);如果使用:
int a=Function();
来进行调用则能够实现重载。如果使用:
1 | Function(); |
来进行调用,则无法实现重载。
- 如何在C++中调用已经编译好的C程序。
- 需要使用
1 | extern “C” |
重载、覆盖于隐藏
- 隐藏
- 如果派生类和基类的函数名相同,但是函数的参数不同。那么不管基类函数有没有virtual,那么基类函数将被隐藏。
- 如果派生了和基类的函数名相同,但是函数的参数也相同。那么如果基类函数没有viritual,那么基类函数将被隐藏。
- 重载
- 相同的范围(在一个相同的类中)
- 函数名字相同
- 参数不同
- virtual关键字可有可无
- 覆盖
- 参数值的缺省只能够出现在函数的声明中,不能够出现在函数的定义中。
1 | //正确 |
- 如果函数有多个参数那么缺省只能够从后向前缺省。
- 不合理的使用缺省值将导致重载函数的二义性。
1 |
|
运算符重载
- 如果运算符被重载为全局函数,那么只有一个参数的运算符叫做一元运算符,有两个参数的运算符叫做二元运算符。
- 如果运算符被重载为类的成员函数,那么一元运算符没有参数,二元运算符只有一个右侧参数,因为对象自己成了左侧参数。
- 不能够重载的运算符
- 不能改变C++内部数据类型(如int,float等)的运算符
- 不能重载 “*” 运算符
- 不能重载目前C++运算符集合中没有的符号。
- 对于已经存在的运算符进行重载时,不能改变优先级规则,否则将引起混乱。