本人学习期间总结的所有C语言笔记:


1.运算符
   一元运算符优先级大于二元运算符
   ,运算符优先级最低
   []大于*号运算符(用于指针数组时)
   ! > 算术运算符 > 关系运算符 > && > || > = > ,

2.字符型
   char没有明确的定义为signed或unsigned,如果需要注意,要自己加上
   '\''    '\"'    '\\'    '%%'    '\?'
   警报'\a'        回退'\b'        换页'\f'        换行'\n'        回车'\r'        水平tab'\t'    垂直tab'\v'

   getchar();从stdin缓冲区中读取
   getch();从键盘缓冲区中读取,在win下不回显,在Unix下回显 在<curses.h>中

   字符测试函数:【包含在<ctype.h>中】
   1) int isalnum(int c)      //测试c是否是字母或数字,是的话返回为真,否则为假
   2) int isdigit(int c)      //测试c是否是数字
   3) int isalpha(int c)      //测试c是否是字母
   4) int islower(int c)      //测试c是否是小写字母
   5) int isupper(int c)      //测试c是否是大写字母
   6) int tolower(int c)      //转换为小写字母
   7) int toupper(int c)      //转换为大写字母
   8) int iscntrl(int c)      //测试c是否是控制字符
   9) int isspace(int c)      //测试c是否是空格
   10)int isgraph(int c)      //测试c是否是除了空格外的可打印的字符
   11)int isprint(int c)      //测试c是否是可打印字符
   12)int ispunct(int c)      //测试c是否是除了空格,数字,字母外的可打印字符,即是否为符号

3.字符串
   字符串就是一个字符数组,结尾以'\0'作为标志
   一个字符占用一个字节(汉字占用两个字节),从左到右依次存放,在字符串的结尾自动加入一个字节的结束标志'\0'
   'a'与"a"的不同:'a'代表一个字符占用一个字节;"a"代表一个字符串占用两个字节.
   字符串初始化:char str[10]="abc123!@#";//不能写成char str[10];str="dfh";(初始化声明的时候的=不代表赋值操作,只是初始化)
   gets()
   puts()

   字符串转换函数:
   1)int atoi(const char *str)        //将字符串类型的数据转换为整型,参数为字符串首地址
   2)double atof(const char *str)        //将字符串类型的数据转换为双精度浮点数
   字符串库函数:
   1) strlen()
   2) strcpy()    strncpy()【复制指定字符个数的字符串】char *strncpy(char *s1, const char *s2, size_t n);
   3) strcmp()    int strncmp(const char *s1, const char *s2, size_t n);【比较前面n个字符】
   4) strcat()    可以用在这种情况下:char ping[8]="ping ",ip[32]="www.baidu.com"; strcat(ping,ip); system(ping);
   5) strtok()    字符串分割函数以s2为标准分割s1,char *strtok(char *s1, const char *s2);
   6) bzero(void *s, size_t n)    字符串清理,参数1是要清空的字符串,参数2是要清空的位数
   7) memset(void *s, int c, size_t n)    字符串填充,参数2为填充进去的内容,参数3为要填充的字节数
   8) strchr(const char *s, int c)        在s中查找c第一次出现的地址
   9) strrchr(const char *s, int c)    在s中查找c最后一次出现的地址
   10)strstr(const char *s1, const char *s2)    在s1中查找s2第一次出现的地址

4.变量(Mac下)
   int    4个字节        -2e15~2e15-1
   float    4个字节        有效位数6~7位
   double    8个字节        有效位数15~16位
   char    1个字节        ASCII码字符,或-128~127
   定义一个变量时,系统就自动的分配了空间,未赋值前,是随即放入的一个垃圾值

5.无符号、×××10进制、8进制、16进制
     正常:u   d   o   x
   短: hu  hd  ho  hx
   长: lu  ld  lo  lx

6.浮点数
   默认情况下,浮点数是以双精度存储的,要明确使用的是单精度,要在数字后面加上字母f(如:57.0f),如果必须以long double格式存储,要在数     字的末尾加上字母l(如:57.0l)。    
   当读取double类型的数值时,在e、f、g前放置字母l,但是l只能在scanf中使用,不能在printf中使用,在printf中e、f、g就能表示double型
   当读或写long double类型的值时,在e、f、g前放置字母L;

7.scanf函数和printf函数
   scanf函数在读入数字时,会自动跳过空白字符,但在读入字符时,不会跳过空白字符
   读写一个单独的字符时,可以用getshar和putchar代替:[ch=getchar();    putchar(ch)]

8.sizeof(类型名)
   sizeof返回的值是无符号的整数
   可以用sizeof(a)/sizeof(a[0])计算数组长度

9.类型定义(typedef)
   typedef int Bool;    //(后面变量名的首字母最好大写)
   Bool flag;    //类似与使用了int flag
   C语言库自身使用typedef定义了一些不同的类型名,这些类型名通常以_t结尾,比如clock_t;

10.宏定义    
   【定义宏】#define BOOL int
       数组和指针不能用宏定义,宏定义中的替换列表为空也是合法的,也可以包含对另一个宏的调用,但宏不能调用它本身.
       一个数字常量,如果不是0和1,就最好定义成宏.
   【带参数的宏】 #define  标示符(x1,x1,…,xn)  替换列表    //参数在替换列表中都要放在圆括号中
       #运算符将一个宏的参数转换为字符串字面量;##运算符可以将两个记号(如标示符)粘在一起,成为一个记号.
       #define CONCAT(x,y)  x##y    //CONCAT(a,b)会得到ab,但CONCAT(a,CONCAT(b,c))不会得到abc,而是aCONCAT(b,c)
   【在宏中放入语句】放在do{ … }while(0)中,这时while后可以不加分号,在调用宏的时候再加分号;如果定义的时候就有分号,在调用的时候就不能再加分号了.
   【取消宏】#undef 标示符

   【预定义宏】_DATE_宏(mm dd yyyy)和_TIME_宏(hh:mm:ss)指明程序编译的时间,是字符串字面量.
         _LINE_宏: 被编译的文件的行数,是整型常量    _FILE_宏: 被编译的文件的名字,是字符串字面量
         _STDC_宏: 如果编译器接受标准C,值为1,是整型常量

11.数组
   定义数组时,数组名不能和变量名相同,也不能和系统关键字一样
   数组的地址就是数组元素的首地址,使用数组名a,代表的就是数组a[]的地址
   多维数组:不常用,因为指针数组更加的灵活好用
   无论是一维数组还是多维数组,通过单词const作为数组声明的开始可以把数组变为“常量”;
   (如:const int months[]={31,28,31,30,31,30,31,31,30,31,30,31};)
12.函数
   在调用函数前,都需要先声明函数,函数声明类似与函数定义的第一行,函数的声明必须与函数的定义一样。
   【函数声明】 返回类型  函数名(形式参数);    【编译器只预处理函数,但是不分配内存】

   【函数定义】 返回类型  函数名(形式参数)    【开辟内存空间,分配内存给函数,函数入口地址为函数名所在地址】
          {
           声明
           语句
          }    

   【函数调用】 函数名(实参);    

   函数无法返回数组;如果忽略返回类型,会假定为int型;
   如果返回为空类型,必须写明void,如果没有形参,也要写明为void.如:void function(void)

   数组做函数参数的时候,传递的是数组名,也就是地址
   实参是通过值来传递的,在调用函数时,计算出实参的值然后传递(赋值)给对应的形参

   函数分类:
   1)从用户角度分有:
   系统函数:库函数,不需要用户定义,包含头文件就可以直接使用的函数,printf();scanf();system();
   自定义函数:用户根据自己的需要,声明函数的返回值类型,函数名,形参列表;但是必须符合函数声明的规则。可以是有参、无参、有返回值、无返回             值、还可以是函数的重写。
   2)从返回值的角度:
   有返回值:return,(函数有且只能返回一个返回值),可以是基本数据类型和指针等。
   无返回值:void,在C89标准中可以忽略返回值类型,UNIX中默认为int,Window VC中为void;在C99标准中不可以忽略返回值类型。
   3)从参数的角度:
   带参数:需要告诉编译器参数的类型和数量
   int main(int argc,char *argv[]);
   int main(int argc,char **argv);
   不带参数:参数列表为空,void,可以省略,即int main()或int main(void);就是不需要参数来参与运算。

13.返回语句
   return:【return 表达式】如果return中表达式的类型和函数返回的类型不一致,系统会把表达式的类型转换成返回类型
   exit:任何函数中都可以使用,【exit(表达式)】;包含在#include <stdlib.h>头文件中。
   main函数的返回值是状态码,0表示正常结束,非0为异常结束。    

14.递归
   递归是函数嵌套调用的一种特殊形式,就是当一个函数调用另外一个函数的过程时,另一个函数恰好是它本身。
   递归产生的条件:直接或间接的调用它本身,有一个出口(即结束递归的条件),要有一个明显的公理或公式,有规律可循。
   1)直接递归调用
   2)间接递归调用

   注意:
   1)递归调用在发生时,消耗了大量的系统资源,反复读写栈内存,CPU资源,会降低系统性能,不可控或深层递归都会造成系统不稳定
   2)我们在开发过程中使用的都是浅层递归,如Windows搜索,使用了多线程,浅层递归(一般不超过3层)
   3)递归调用也被用于分治算法
   4)能不用就不用递归,如:.(){ .|. };.  (.本身是一个函数,()为.函数的参数列表,;前为.函数的定义,最后一个.是调用这个函数)  

15.迭代
   迭代法又叫做递推,凡是能用递归来实现的迭代或递推都能实现。
   八皇后问题,水仙花束问题,兔子繁殖问题,约瑟夫出圈问题,快速排序,汉诺塔

16.变量作用域
   全局变量:
   局部变量:

17.指针
   简单的说,指针就是地址。
   内存中的最小存储单元是字节,一个字节有8位,每一位都有自己的地址,指针就是他们的首地址。
   基本数据类型的指针,就是他们在内存中的首地址。
   构造类型的指针,数组,他的位置就是,数组名称所在内存的首地址,即a[0]的地址或者是数组名。
   函数类型的指针,就是函数的入口地址,即函数名所在内存的首地址。

   指针变量:和其他基本数据类型的变量类似,特殊的是他是能保存地址的变量。
   【定义指针变量】类型标示符 *变量名(int *p);  

   指针变量初始化:
       int *p=NULL;
       int *q=&a;                                        
       int *s=100;
   造成野指针的三种方式:
       1.定义指针的时候没有初始化
       2.指针指向一个局部变量,当局部变量执行完毕释放后,仍然指向该局部变量的时候(用完置NULL即可)
       3.当指针指向一块动态的内存空间时malloc(new)使用完毕被free(delete)释放后,仍然指向该空间,(用完置NULL即可)

18.指针和常量的关系
   1)常量指针:
       指针指向了一个常量的指针变量,常量的内容不可以修改,但是地址可以修改。
       int const *p;    const在*号后,是p的值不能修改;const在*号前,是*p的内容不能修改
   2)指针常量
       是个常量,是指指针变量P的值不能被修改,p是个常量,而指针指向的对象的值是可以改变的。
       int *const p;

19.指针和数组的关系
   1)数组指针
       指向一维数组的指针,即数组的首元素
       int a[10];int *p=NULL;p=a;
       是一个指向二维数组一行的指针变量,即二维数组的首行的地址
       int (*p)[表达式];    
   2)指针数组
       数组元素是指针变量
       int *p[表达式];

20.指针和函数的关系
   1)函数指针
       就是指向函数名的指针变量
       int (*p)(形参列表);    
   2)指针函数
       它是一个返回指针类型的函数,即函数的返回类型是指针
       int *p(形参列表);
       如:int *adr(int x)
          {
           return &x;
          }
       这种情况,返回值还可以继续作为函数的参数来使用,如回调函数,比如Unix信号和信号量,signal(),malloc()

21.指向指针的指针
   int a=0;  int *p=&a;  int **q=&p;    //*p==a,  **q==a  

22.结构体(struct)
   能够保存不同数据类型的一种构造数据类型
   【关键字】struct
   【定义结构体】    struct 结构体名{
              成员变量1;
              成员变量2;
              …
              成员变量n;

           };    //分号表示结构体定义完毕
   【结构体变量初始化】(结构体变量就是包含结构体所有属性的一个变量)
           struct struct_name 变量名={ , , };    //struct struct_name 整体是变量的类型
       或    struct struct_name{
              成员变量1;
              成员变量2;
           }object_name={ , , };
   【引用结构体成员变量】
           .   或  ->(用于指针的时候)

   【结构体和指针】
       1)指向结构体的指针
           struct struct_name *p;    //p是指针变量名
           p=&object_name;        //&取地址符不能忘

   【结构体的嵌套】
       一个结构体中嵌套了另一个结构体
       struct struct_name{
           成员变量1;
           成员变量2;
           struct struct_name_a{
               成员变量a;
               成员变量b;
           }object_name_a;
       }object_name;
       【引用】object_name.object_name_a.b;

   【结构体变量(可以用指针)作为函数参数使用】
       void struct_function(struct struct_name object_name_temp);
       void struct_function(struct struct_name *p);

   【结构体变量作为函数的返回值】
       struct struct_name function_name()
       {
           return object_name;
       }

   【结构体数组】
       数组中的每个元素都是一个结构体变量
       struct struct_name{
           成员变量1;
           成员变量2;
       }数组名[表达式];        //表达式可以为空,为空的时候是不定长度的数组

23.联合体(union)
   用户自定义的数据类型,和结构体不同的是结构体变量独立使用各自成员的内存空间,联合体的所有成员共享存储空间,即使用联合体省内存.
   定义联合体和结构体一样,只需要把struct改成union即可.

24.枚举(enum)
   枚举的成员中只能用一种类型,成员内不指定序号的时候都以0开始,C语言把枚举变量和常量作为整数来处理
   enum color{red, black, blue}a,b;    //中间以逗号隔开

25.链表
   链表是线性表的一种,是线性表的链式存储;是一种使用动态内存分配的,能保存不同数据类型的一种数据结构.构成链表的最小单位称为节点.
   1)单链表
     有一个头结点(非空)链表,结点之间有前驱和后继,尾结点无后继结点,指向空.节点有两部分组成数据域和指针域.头结点用于判断链表是否为空.
   2)双链表
   3)循环链表

26.位操作
   1)位与&
       用于置0
   2)位或|
       用于置1
   3)位异或^
       用于加密和解密:
       0000 1100    0000 0110    (用第一次异或得到的值,再进行异或)
        ^    0000 1010    ^    0000 1010    (用同一个值作为异或的运算)
       --------------  --------------
       0000 0110    0000 1100    (重新得到了之前的值)

       交换a,b的值:  a=a^b; b=a^b; a=a^b;
   4)位取反~
       都是用补码来取反的
       连带符号位一起取反,取反之后符号位为1的话(补码),减去1(反码),除去符号位其它位取反(原码)
   5)左移<<    
       往左移,低位补0,左移一位相当于乘以2,左移n位相当于乘以2的n次方
   6)右移>>
       往右移,高位补符号位,右移一位相当于除以2,右移n位相当于除以2的n次方
       Mac下左移右移符号位不变
27.内存管理
     系统级内存管理(Unix系统提供的内存管理)
   1)物理内存管理
   2)虚拟内存管理
   3)页面文件getpage
   4)内存映射mmap
   5)共享内存
   brk();//在分配内存时效率很高,只有3个值:大于0(申请该大小的内存空间),等于0(完全释放内存),小于0(在当前的内存空间上释放该大小的空间)
   sbrk();    
   OC中用的内存管理,计数,当计数为0的时候就自动释放(OC brk)
     用户级内存管理
   1)malloc
   2)calloc
   3)realloc
     C语言执行过程中,在内存中的情况:
   1)代码段(静态区域)
     代码段由程序中的机器码组成,C语言中源代码编译后就形成了机器码,执行的时候,CPU的程序计数器指向了代码段的每一条代码,并由CPU依次执行
   2)数据段(静态区域)
     只读数据段,是程序使用一些不会被修改的数据,使用这些数的方式类似与查表式的操作,放置在只读存储器中;
     已初始化读写数据段,是在程序中声明,有初值的变量,需占用寄存器空间,在程序执行时,位于可读写的内存区域内,供程序运行时读写
   3)BSS段(未初始化读写数据段)(静态区域)
     是在程序中声明,没有初始化的变量,这些变量在程序运行前不占存储器空间
   4)堆空间(动态区域)
     只在程序运行时出现,一般由程序员分配和释放,在具有操作系统的情况下,若程序员忘记释放,在程序结束后,系统会自动回收内存
   5)栈内存(动态区域)
     只在程序运行时出现,在函数内部使用的变量,函数参数及返回值,函数调用(递归)都将使用栈空间.栈空间是由编译器自动分配和释放的内存.

     C语言提供的两种内存分配的方式:
   1).静态内存分配(栈内存)
      由编译器自动为我们分配的内存空间,一般栈内存的大小为10M~30M
      内存的对齐:是指整型占4个字节,大于4个字节的数据类型,也按照4个字节来计算,而且是4的整数倍
      内存的补齐:只在结构体中有,如果所占字节数不足4个数据类型,比如char,需将不足的字节数补空,补够4个字节
   2).动态分配内存(malloc向堆申请内存)
      malloc函数(分配后不初始化)
       void *malloc(size_t size);    //返回值是万能指针,在Mac下占8个字节,在Win下占4个字节
       用于动态的向堆内存申请内存空间,使用完后,要使用free()函数释放该内存
       一旦指针p指向动态分配的内存块,就可以忽略p是指针的事实,并且把它用作数组的名字,p[i].
      free函数
      calloc函数(分配之后初始化为0)(用于存储构造类型或复合类型数据)
       void *calloc(size_t nmemb, size_t size);   //有nmemb个元素,每个元素占size个字节,nmemb为1表示为任何的数据类型
      realloc函数
       void *realloc(void *ptr, size_t size);      //ptr指向通过malloc或calloc或realloc分配的内存块
28.预处理
   预处理器的行为是由指令控制的,这些指令是由#开头的一些命令.指令总是在第一个换行符处结束,若想在下一行继,在当前行末尾用\字符.
   【宏定义】 #define指令定义一个宏, #undef指令删除一个宏定义.
   【文件包含】#include导致一个指定文件的内容被包含到程序中.
     1)#include <文件名>    //用于C语言自身库的头文件
     2)#include "文件名"    //用于所有其他头文件,也包含任何自己编写的文件
     3)#import <文件名>  "文件名"    //被包含的头文件只被编译一次,不会重复
   【条件编译】#if, #ifdef, #ifndef, #elif, #else和#endif指令将一段文本块包含到程序中或排除在程序之外.
     1)#if 常量表达式    //当预处理器遇到#if时,如果表达式的值为0,在#if和#endif之间的行会在预处理过程中从程序中删除,
       语句              否则,在它们之间的行会被保留在程序中,并继续被编译器处理,这时的#if和#endif对程序无任何影响.
       #endif          对于没有定义过的标示符,#if会把它当做是值为0的宏对待
     2)defined    仅用于预处理器,当defined应用于标示符时,若标示符是个定义过的宏返回1,否则返回0.常用在#if指令中
       #if defined(BULL)
       ...
       #endif
     3)#ifdef 标示符        //等价于   #if defined(标示符)

       #ifndef
     4)#elif  表达式1
       #else
   【特殊】#error, #line, #pragma更为特殊,较少用到.
     1)#error 消息
     2)#line n ("文件名")    //line指令用于改变给程序行编号的方式,n在1和32767之间,之后的行编号为n+1,文件名可以没有
     3)#pragma 记号        //对非常大的程序或需要使用指定编译器的特殊功能的程序很有用

29.链表
      由一连串的结构(结点)组成的,其中每个结点都包含指向下一个链中结点的指针,最后的一个结点包含一个空指针.
   for (p = first; p!=NULL; p = p->next)

30.文件
   C程序中的流的访问是通过文件指针实现的,类型为FILE *fp;文件结束符EOF(由宏定义的);
  1)打开文件
   FILE *fopen(const char *filename, const char *mode);    //filename可以包含路径信息,没打开返回空指针
   打开的模式(mode):
      "r":只读            "w":写,文件不需要存在        "a":追加,文件不需要存在
      "r+":读和写,从文件头开始        "w+":读和写,文件存在就截取        "a+":读和写,文件存在就追加    
      如果打开的是二进制文件,需要在模式字符串中包含字母b, 即"rb"
  2)关闭文件
   int fclose(FILE *stream);    //参数必须为文件指针,指针来自fopen或freopen,成功关闭文件返回0,否则返回EOF
  3)为流附加文件
   FILE *freopen(const char *filename, const char *mode, FILE *stream);    //stream有stdin,stdout,stderr
  4)格式化输入/输出
   fscanf(fp,"%d", &i)    //从fp中读入    fscanf(stdin, "%d", &i)等价于scanf("%d", &i)
   fprintf(fp, "%d", i)    //从fp中读出    fprintf(stdout, "%d", i)等价于printf("%d", i)
  5)字符读写函数
   fgetc(fp)    getc(fp)
   fputc(ch, fp)     putc(ch, fp)
  6)行的输入/输出
   fgets(char *s, int n, FILE *stream)    //n为限制要读入字符的数量, 为字符串的时候要额外的加1, 表示为'\0'        
   gets(char *s)    //逐个读取字符
   fputs(const char *s, FILE *stream)    //系统不会自己写入换行符, 可以人为的加入"\r\n"
   puts(const char *s)    //系统会自动写入换行符
  7)块的输入/输出(主要用于操作二进制文件)
   fread()
   fwrite()
  8)fflush(fp)    //为fp清洗缓冲区        fflush(NULL)    //清洗全部缓冲区
    remove("filename")    //删除文件,参数是文件名,而不是文件指针
    rename("filename", "newfilename")    //重命名文件名, 参数也是文件名,不是文件指针, 文件此时要是关闭状态
    fcopy  f1.c  f2.c    //把文件f1.c复制给文件f2.c
    feof(fp)
    ferror(fp)
    fseek    //文件位置  文件开始: SEEK_SET    文件当前位置: SEEK_CUR      文件结尾: SEEK_END

31.gcc编译的过程
  预处理(Pre-processing):-E    
   编译器将C源代码中包含的头文件如stdio.h编译进来(.i)
  编译(Compliling):-S
   编译器首先检查代码的规范性, 是否有语法错误等, 以确定代码实际要做的工作, 无误后, 将代码翻译成汇编语言(.s)
  汇编(Assembling):-c
   把编译阶段生成的.s文件转成二进制目标代码(.o)
  链接(Link):
   将输出文件.o链接成最终的可执行文件

32.函数库
  静态库(.a): 是指在编译链接时, 把库文件中的代码全部加入到可执行文件中, 因此生成的文件比较大, 但在运行时就不再需要库文件了.
  动态库(.so): 在编译链接时, 并没有把库文件的代码加入到可执行文件中, 而是在程序执行时由运行时链接文件加载库, 节省了系统开销.
  gcc编译时, 默认使用动态库.