CODE_C1

要写纯粹的,脱离低级趣味的 函数!

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

  1. CONST定义的是变量,是不可改变的变量。因为是不可改变的所以需要在定义的时候就进行初始化,因为如果现在不进行初始化,等到后来也就没有办法进行初始化了。

  2. 在C语言中如果使用变量(包含const)声明数组的大小在编译的时候可以通过,但是根本就没有办法运行。但是在C++中是可以运行的。定义一个数组必须指定数组元素的个数。C与C++中对const的含义是不同的,在C++中对const的含义做了扩充。

  3. 编译器扩展:编译器,太智能了。c不允许变量赋值给数组,编译器允许。这就是个例子。

.c与.cpp文件的区别

  1. .c文件只能够书写C语言代码。.cpp既可以书写C语言程序也可书写C++程序。
  2. gcc认为.c的为C程序,.cpp的为C++程序;
  3. g++认为.c的为C++程序,.cpp的为C++程序;
  4. VC++的编译器cl认为.c的为C程序,.cpp的为C++程序;
  5. C程序与C++程序中同样的函数在编译后的obj文件中的symbol是不同的,所以以C方式编译的obj文件与以C++方式编译的obj文件无法成功链接。

    const volatile int a=10;

  6. 需要使用codeblock进行调试,查看寄存器的值

  7. const 表示在本段程序中不能对a做修改,任何修改都是非法的。
  8. volatile 表示另一个程序段则完全有可能修改,因此编译器最好不要做太激进的优化。
  9. const 的含义是“请作为常量使用”,而并非“放心吧,那肯定是常量”
  10. volatile 的含义是“请不要做没谱的优化,这个值可能变掉的”,而并非“你可以修改那个值。
  11. 因此const 与 volatile 本来就不矛盾。

    注:

  12. const修饰的变量不允许在这里修改不代表不允许别处修改。
  13. 对于非指针非引用的变量,const volatile同时修饰的意义确实不大。
  14. 两者同时修饰一个对象的典型情况,是用于驱动访问外部设备的只读寄存器。

大端与小端

定义

  1. 大端:数据的高字节存储在内存空间的低地址上,数据的低字节存储在内存空间的高地址上。
  2. 小端:数据的高字节存储在内存空间的高地址上,数据的低字节存储在内存空间的低地址上。

    如何判断一个系统是大端还是小端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    #include <stdio.h>  
    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

  1. 试图使用宏开始或结束一段注释是不行的。
  2. 想要很好的使用宏那就不要吝啬括号。
  3. 空格在宏定义的时候是有效的,在使用宏函数的时候会被编译器忽略掉。
  4. 整数常量后面接UL表示类型,606024365UL 等价于 ((unsignedlong)606024365)
  5. 宏的声明周期从
    开始,到 ```#undef``` 结束
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    6. 条件编译对于程序的移植和调试是很有用的。
    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;
    }
即 ```&a+5*size(int);``` 一个类型为T的指针的移动,是以 ```sizeof(T)``` 为单位进行移动的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

### 指针与数组
1. C语言中,当一维数组作为函数参数的时候,编译器总会把它解析成一个指向首元素首地址的指针。
2. 函数本身是没有类型的,只有函数的返回值才有类型。
3. 实际传递第数组大小与函数形参指定的数组大小没有关系。
4. main函数内的变量不是全局变量而是局部变量,只不过器声明周期和全局变量一样长而已。全局变量一定是定义在函数外部的。
### C语言中的内存区域:
* 静态区:保存自动全局变量和static变量。
* 栈:保存局部变量。
* 堆:由malloc系列函数或new操作符分配的内存。
1. 只有字符串常量才有结束标志符。下面这种写法就没有结束标志符:


```c++
char a[7]={'a','b','c','d','e','f'};
  1. 会产生泄漏的内存就是堆上的内存(这里不讨论资源或者是句柄的泄漏)也就是说由

    系列函数或 ```new``` 操作符分配的内存。如果用完之后没有及时 ```free``` 或 ```delete```,这块内存就无法释放,知道整个程序终止。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    4. ```free(p)``` 之后必须进行 ```p=NULL;``` 否则 ```p``` 就变成了一条野狗。


    ```c++
    void whenEnd()
    {
    free(p);
    p=NULL;
    }

  2. 函数就是一些语句的集合。

  3. C语言中,函数的static局部变量是函数的记忆存储器。建议精良少用static局部变量。
  4. 有的时候函数不需要返回值,但是为了增加灵活性如支持链式表达,可以附加返回值。如:strcpy函数
1
int len=strlen(strcpy(strDest,strSource));

ASSERT

  1. 函数声明
1
void assert( int expression );
  1. assert的作用是现计算表达式 expression ,如果其值为假(即为0),那么它先向stderr打印一条出错信息,然后通过调用 abort 来终止程序运行。
  2. 在调试结束后,可以通过在包含#include <assert.h>的#ifdef NDEBUG语句之前插入 #define NDEBUG 来禁用assert调用即可。
  3. 比较好的在程序中使用assert的地方:
    • 空指针检查。例如,针对一个函数的参数进行空指针检查。你可以这样使用:assert (pointer != NULL);,产生的错误会像这样:Assertion ‘pointer != ((void *)0)’ failed。这样,当出现空指针时,你的程序就会退出,并很好的给出错误信息。
    • 检查函数参数的值。例如,如果一个函数只能在它的一个参数foo为正值的时候被调用,你可以在函数开始时这样写:assert (foo > 0);,这将帮助你检测函数的错误使用,这也给源代码阅读者很清晰的印象,那就是在这里对函数的参数值有限制。
  4. 使用原则

    • 每个assert只检验一个条件,因为同时检验多个条件时,如果断言失败,无法直观的判断是哪个条件失败。

      1
      2
      3
      4
      5
      //不好
      assert(nOffset>=0 && nOffset+nSize<=m_nInfomationSize);
      //好
      assert(nOffset >= 0);
      assert(nOffset+nSize <= m_nInfomationSize);

排序

  1. 10W个数据进行排序,冒泡46.44S,快排0.8S。
  2. 1000W个数据进行快排,

  1. 别看free和delete的名字恶狠狠,其实它们人很好的,它们只是切断了指针和内存之间的联系,并没有把指针干掉。(但是同时让指针成为了野指针)。

    指针有一些“似是而非”的特征:

  2. 指针消亡了,并不表示它所指向的内存会被自动释放。
  3. 内存释放了,并不表示指针会消失或者变成NULL指针。
  4. 我就认为程序终止了运行,一切指针都会消亡动态内存会被操作系统回收。既然如此,在程序临终前,就可以不必释放内存、不必将指针设置成NULL了。终于可以偷懒而不会发生错误了。这种想法是错误的,如果别人把那段程序取出来用到其它的地方怎么办?

    malloc、free与new、delete

  5. 使用new为int等内部类型分配一个空间。
1
int *p=new int[length];
  1. 使用new为对象数组分配空间,此时只能够使用无参数的构造函数进行分配,不能够使用有参数的构造函数进行分配。

    1
    Obj *object=new Obj[100];
  2. 使用delete释放new分配的对象数组时,不要丢掉[].
    4

    1
    delete [] object;
  3. malloc、free 与 new、 delete的区别见课本P52讲的太好了。


函数重载

  1. 如果两个函数的函数名相同不同的仅仅是返回值类型,那么这个函数有的时候能够重载有的时候不能够重载,例如下面的两个函数
  2. 1
    2
    void Function(void);
    int Function(void);

    如果使用:

    int a=Function();
    来进行调用则能够实现重载。如果使用:

1
Function();

来进行调用,则无法实现重载。

  1. 如何在C++中调用已经编译好的C程序。
    • 需要使用
1
2
3
4
extern “C”
{
void foo(int x,int y);
}

重载、覆盖于隐藏

  1. 隐藏
    • 如果派生类和基类的函数名相同,但是函数的参数不同。那么不管基类函数有没有virtual,那么基类函数将被隐藏。
    • 如果派生了和基类的函数名相同,但是函数的参数也相同。那么如果基类函数没有viritual,那么基类函数将被隐藏。
  2. 重载
    • 相同的范围(在一个相同的类中)
    • 函数名字相同
    • 参数不同
    • virtual关键字可有可无
  3. 覆盖
    • 不同的范围(基类和派生类)
    • 函数名字相同
    • 参数相同
    • 基类必须具有virtual关键字

      参数值的缺省

  4. 参数值的缺省只能够出现在函数的声明中,不能够出现在函数的定义中。
1
2
3
4
5
6
//正确
void Func(int a,int b=1);
//错误
void Func(int a,int b=1)
{
}
  1. 如果函数有多个参数那么缺省只能够从后向前缺省。
  2. 不合理的使用缺省值将导致重载函数的二义性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
using namespace std;

void Output(int a);
//缺省参数值,将导致函数重载
void Output(int a,float b=1.1);

int main()
{
Output(1);
Output(1,1.4);
return 0;
}

void Output(int a)
{
cout<<"Output_Int "<<a<<endl;
}
void Output(int a,float b)
{
cout<<"Output_Float "<<a<<" , "<<b<<endl;
}

运算符重载

  1. 如果运算符被重载为全局函数,那么只有一个参数的运算符叫做一元运算符,有两个参数的运算符叫做二元运算符。
  2. 如果运算符被重载为类的成员函数,那么一元运算符没有参数,二元运算符只有一个右侧参数,因为对象自己成了左侧参数。
  3. 不能够重载的运算符
    • 不能改变C++内部数据类型(如int,float等)的运算符
    • 不能重载 “*” 运算符
    • 不能重载目前C++运算符集合中没有的符号。
    • 对于已经存在的运算符进行重载时,不能改变优先级规则,否则将引起混乱。