• C语言学习笔记(加C高级)


       1 程序编译的过程:
       2     预处理   生成.i文件
       3     编译     将预处理后的文件转换成汇编语言,生成.s文件
       4     汇编        将汇编语言变为目标代码(机器代码)生成.o 文件
       5     链接        连接目标代码,生成可执行程序
       6 
       7     预处理是C语言程序从源代码变成可执行程序的第一步,主要是C语言编译器对各种预处理命令进行处理,包括头文件的包含、宏定义的扩展、
       8 条件编译的选择等。打印出预处理之后的结果:gcc -E hello.c 或者 cpp hello.c这样我们就可以看到源代码中的各种预处理命令是如何
       9 被解释的,从而方便理解和查错。
      10 
      11     编译之前,C语言编译器会进行词法分析、语法分析(-fsyntax-only),接着会把源代码翻译成中间语言,即汇编语言。如果想看到这个中间
      12 结果,可以用-S选项。
      13 
      14 
      15 1、值常量:整型值常量、浮点型值常量(例如:0.5f)、字符型值常量('a''
    ''
    ')、字符串值常量("hello")、符号常量、常型常量
      16 2、变量声明:存储类型、数据类型、变量名
      17 3、合法的标识符,只能以字母和下划线开头,后面可以是数字、下划线、字母,不能是关键字(例如:charshortintlong  18 4、数据的存储形式: 1、在内存中的形式:小端字节序,地址低位存数据低位,地址高位存数据高位
      19                  2、在网络中形式,大端字节序,地址高位存低位,地址低位存数据高位
      20 
      21 5、三种常见的布尔分析
      22     1、整型数的布尔判断(直接判断)
      23     2、浮点数的判断     (用范围比较方式)        
      24     3、指针的判断
      25                                                                                                                 
      26 6、a++和++a的区别:
      27     a++先返回a的值,再加a = a + 1; (表达式结束才会自增)
      28     ++a先a=a + 1,再返回原来a + 1的值(表达式完成前自增)
      29 
      30 运算符优先级:
      31     优先级        运算符         名称或含义             使用形式
      32                   []        数组下标                数组名[整型表达式]
      33                   ()        圆括号                (表达式)/函数名(形参表)
      34       1              .            成员选择(对象)        对象.成员名
      35                     ->        成员选择(指针)        对象指针->成员名
      36 -------------------------------------------------------------                    
      37                   -            负号运算符            算数类型表达式
      38                 (type)        强制类型转换            (纯量数据类型)纯量表达式
      39                   ++        自增运算符            ++纯量类型可修改左值表达式
      40                     --        自减运算符            --纯量类型可修改左值表达式
      41       2              *            取值运算符            *指针类型表达式
      42                     &            取地址运算符            &表达式
      43                     !            逻辑非运算符            纯量类型表达式
      44                     ~            按位取反                ~整型表达式
      45                   sizeof      长度运算符            sizeof表达式
      46 -------------------------------------------------------------
      47 具体祥看:https://blog.csdn.net/u014100559/article/details/90764534?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522159358726719724848309940%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=159358726719724848309940&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~baidu_landing_v2~default-5-90764534.first_rank_ecpm_v3_pc_rank_v3&utm_term=c%E8%AF%AD%E8%A8%80%E4%BC%98%E5%85%88%E7%BA%A7%E6%8E%92%E5%BA%8F
      48 
      49 
      50 
      51                         所占字节数                    数据表示范围
      52 char                        1                         -128~127
      53 unsigned char                 1                           0~255
      54 short                        2                       -2^15~2^15-1
      55 int                            4                        -21亿~21亿
      56 long                        8(32位机上为4位)
      57 long long                    8
      58 unsigned short                2                          0~65535
      59 unsigned int                                          0~42亿
      60 
      61 
      62 防止数据溢出(上溢:溢出到范围的底部,下溢:溢出到数据的底部)
      63 整数除整数得整数,整数除浮点数(其中有一个为浮点数,则结果为浮点数)
      64 
      65 sizeof:计算某类型或某表达式所占的字节数
      66 
      67 非格式化字符
      68     输入:getchar() 可使用其使程序暂停(一次只提取一个字符)
      69     输出:putchat() 
      70 
      71 
      72 /**************************************************************************/
      73 #include <stdio.h>
      74 #define MAX(n1 , n2)((n1) > (n2) ? (n1) : (n2))
      75 
      76 int max_fun(int num1 , int num2)
      77 {
      78     return num1 > num2 ? num1 : num2;
      79 }
      80 
      81 int main()
      82 {
      83     int num1 , num2 , num3
      84     printf("input three number
    ");
      85     scanf("%d,%d,%d",&num1 , &num2 , &num3);
      86     int max;
      87     max = max_fun(max_fun(num1 , num2) , num3);
      88     printf("max = %d
    ",max);
      89 }
      90 /**************************************************************************/
      91 
      92 while()//先判断再执行,有可能一次都不会执行
      93 
      94 do...while()//先执行再判断,至少会执行一次
      95 
      96 for(初始化表达式 ; 判断表达式 ; 迭代表达式)
      97 {
      98     循环体
      99 }
     100 //for循环执行顺序: 初始化表达式->判断表达式->循环体->迭代表达式->判断表达式
     101 
     102 coutinue()语句
     103     功能:结束本次循环,跳过循环体中尚未执行的语句,进行下一次循环体的判断(一般结合if使用)
     104 
     105 
     106 数组:有一个首地址,后面有连续内存。
     107      大批量存储指定类型的数据,有统一访问的入口(数组名 + 下标),方便对批量数据的访问。
     108 特点:1、类型统一  2、大小固定
     109 (数组名代表了数组的首地址)(数组的维数必须是常量)   
     110 
     111 数组初始化
     112     1、静态初始化(在声明时初始化,用{}初始化)
     113     2、多态初始化(声明之后的赋值)
     114 
     115 中括号[]的本质:*(数组名 + 偏移量)
     116 例:P[1]就是*(p + 1) , 1[p]就是 *(1 + p)     
     117 
     118 bzero(目标,0,位数)    //将目标清为0
     119 memset(首地址,0,字节数)//将目标设置为0
     120 memcpy(数组1,数组2,sizeof(数组2))
     121 
     122 void* : void*指针可以接收任意类型指针,提高函数通用性。
     123 const void*是常量指针,只能改变指向,不能改变值。
     124 
     125 
     126 /*********************************************************************************/
     127 二维数组:
     128 #include <stdio.h>
     129 
     130 int main(int argc, char const *argv[])
     131 {
     132     int arr[3][2];
     133     printf("arr = %p , arr[0] = %p , &arr[0][0] = %p
    " , arr , arr[0] ,&arr[0][0]);            //值相等,但意义不一样&arr[0][0],arr[0]的类型都为int*
     134     printf("&arr[0][0] = %p,arr[0][1] = %p,&arr[1][0] = %p
    ",arr[0][0],arr[0][1],&arr[1][0]); //二维数组与一维数组的存储形式都一样,都是连续内存
     135     printf("arr[0] = %p,arr[1] = %p,arr[2] = %p
    ",arr[0],arr[1],arr[2]);                        //二维数组的第一维表示的是每行的首地址
     136     printf("arr[0][1] = %p,arr[1][0] = %d
    ",arr[0][1],arr[1][0]);                                //声明时如果未初始化,产生的也是随机值。
     137     printf("sizeof(arr) = %ld
    ",sizeof(arr[0]));                                                //总字节数
     138     printf("sizeof(arr[0]) = %ld
    ",sizeof(arr[0]));                                            //sizeof(每行首地址) = 一行所占字节数
     139     printf("sizeof(arr)/sizeof(arr[0]) = %ld
    ",sizeof(arr)/sizeof(arr[0]));                    //行数
     140     printf("sizeof(arr[0])/sizeof(arr[0][0]) = %ld
    ",sizeof(arr[0])/sizeof(arr[0][0]));        //列数
     141     printf("sizeof(arr)/sizeof(arr[0][0]) = %ld
    ",sizeof(arr)/sizeof(arr[0][0]));                //总的成员个数
     142     return 0;
     143 }
     144 /*********************************************************************************/
     145 
     146 字符串与数组
     147 字符串的本质:是一种以''结尾的字符数组
     148 
     149 char ch[6] = {"china"}
     150 参数是char*,表示可读可写,所以一般用字符数组char[],如果传的是指针,则不能是
     151 指向野指针、空指针、指向常量数据区的指针,可以是指向函数栈,堆内存的指针
     152 
     153 字符串的两种表达形式:
     154     字符数组:char buf[] = "hello";
     155     字符指针:const char *str = "hello";
     156 两种表达形式看起来一样,但是实际上存在很大差异,字符指针只有一个"hello",存在于常量数据区
     157 字符数组有两"hello",一个存在于常量数据区,另外一个存在于函数栈中
     158 
     159 
     160 字符串的比较命令:
     161     int strcmp(const char *s1 , const char *s2);//区分大小,全部比较s1和s2
     162     int strncmp(const char *s1 , const char *s2 , size_t n);//区分大小写部分比较s1 s2
     163     int strcasecmp(const char *s1 , const char *s2);//忽略大小写全部比较s1,s2
     164     int strncasecmp(const char *s1 , const char *s2 , size_t n);//忽略大小写部分比较s1,s2
     165     返回值:返回值大于0 ,表示S1>S2,返回值等于0,表示S1==S2,返回值小于0,表示S1<S2
     166 
     167 字符串比较示例:
     168 #include <stdio.h>
     169 #include <string.h>//导入字符串操作的头文件
     170 
     171 int main()
     172 {
     173     const char *str1 = "hello world";
     174     const char *str2 = "helloworld";
     175     char buf1[] = "hello world";
     176     char buf2[] = "hello WORLD";
     177     int sub_1 = strcmp(str1,str2);
     178     int sub_2 = strncmp(buf1,buf2,11);
     179     int sub_3 = strcasecmp(buf1,buf2);
     180     int sub_4 = strncasecmp(buf1,buf2,9);
     181 }
     182 
     183 
     184 字符串函数与 int float 类型的转换
     185 int sscanf(const char *float,...(变量地址));//将指定格式的字符串转换成int , float型
     186 int sprintf(char *buffer,const char *format,...)//将一个格式化的字符串输出到一个目的字符串中,而printf是将一个格式化的字符串输出到屏幕
     187 
     188 
     189 定义函数正确的顺序是先定义main(),在main()之前声明其他函数,在main之后实现函数
     190 函数的四要素:头文件、功能、参数、返回值
     191 头文件:把要调用的函数声明提取过来,相当于直接声明函数
     192 功能:每个函数都要实现指定的功能才有意义
     193 参数的作用:提高函数的灵活性(输入型,输出型,输入输出型)
     194 返回值:用于保存函数运行结果
     195 
     196 函数栈:由系统自动分配,自动回收(全自动),存放形参,函数中的一般局部变量,返回值。
     197 堆内存:由程序malloc(),calloc(),realloc()去申请用free()释放,如果不释放则会内存泄漏。
     198 
     199 参数的传递方式:
     200         地址传递:(C++里的浅拷贝)
     201             作用效果:函数内的改变会影响函数外
     202             本质:通过形参指针改变实参的值
     203             判断条件:根据形参判断,如果形参是指针或者数组则一般是地址传递(特殊情况:如果只是改变形参指针的指向,而没有通过形参改变实参的值)
     204         值传递:(C++里的深拷贝)
     205             作用效果:函数内的改变不影响函数外
     206             本质:只改变形参的值,没改变实参的值
     207             判断条件:根据形参判断,如果形参是基本结构或着结构体等一般类型(非指针,非数组)
     208 
     209 /***********************************************************************************************************/
     210 变量的存储类型(存储位置不同):
     211     1、register(寄存器变量):存在寄存器中(寄存器变量可以提高运算速度,程序中不会去声明,由编译器自动生成)
     212     2、auto(自动变量):在函数栈中 
     213     3static(静态变量):在全局数据区,不能被外部文件引用访问 
     214     4extern(外部变量):在全局数据区,引用外部啊文件的变量
     215 
     216 静态变量:在函数中声明的静态变量
     217 
     218 静态局部变量与一般局部变量的区别:
     219     相同之处:作用域相同,都是作用于函数内
     220     不同之处:1、生命周期不同
     221             2、初始化次数不同
     222             3、初始化值不同
     223             4、存储位置不同
     224 静态全部变量与一般全局变量的区别:
     225     相同之处:1、生命周期相同(整个应用程序)
     226              2、存储位置相同(全局数据区)
     227              3、初始化次数相同
     228              4、初始化值相同
     229              5、在同一个文件中作用域相同
     230     不同之处:1、作用域不同,一般全局变量可在本文件使用,也可以被外部文件使用,静态全局变量只能用于本文件(保护,限制数据只能在本文件使用)
     231 
     232 一般函数与静态函数:
     233     在同一个文件中两种函数没有区别
     234     一般函数可作用于本文件也可以作用于外部文件,但是静态函数只能作用于本文件,不能作用于外部文件
     235     (如果需要在外部文件中调用静态函数,可以间接调用,先在一般函数中嗲用静态函数,再在外部文件调用一般函数)
     236 
     237 
     238 /****************************************************************************************************************/
     239 指针(任何指针在64位机下都是8个字节)
     240 
     241 指针使用:* &
     242 *在指针中的两个作用:
     243         1、作类型说明符:例如int *p 在声明指针时做类型说明,说明P是指针其类型为int
     244         2、作取值符:除声明之外,*都是取值符,取出地址的值
     245 &的作用:在指针中 & 只要一个作用,即为取地址
     246 
     247 * 取值会减少指针的级数,* 一级指针取值,*二级指针是取的一级指针的地址,**p取出的二级指针的值
     248 
     249 指针在使用时注意:
     250     1、指针类型必须一致
     251         错误示例: int i = 'a';
     252                   char ch1 = 'a';
     253                   int *p = &ch1;//error
     254                   float *pf = &ch1//error
     255                   char *p = (char *) &i;//指针类型可以强转
     256     2、在强制转换时不仅要看指针的类型,还要看原参数的类型
     257                   例如:char ch = 3.14;
     258                          int *p = (int*)&ch;
     259                   因为ch为char 型,一次只取一个字节,而int型一次读取4个字节,如果这样进行强转,则指针*p的值便会成为一串错误的数据
     260 
     261 空指针:声明指针时如果没有初始化地址,则赋值为NULL。
     262     例如:int *p = NULL;
     263 主要是为了后面的安全检查,如不赋值为空指针,那么这个指针将会成为野指针,野指针无法进行安全检查
     264 
     265 void *指针:可以接收任意类型的地址,也可以给任何指针赋值
     266 void *指针经常用来作函数参数,提高函数的通用性
     267 不能声明void型变量,因为编译器不知道给void类型分配多少字节
     268 
     269 以下操作是OK的,因为指针在64位机上都是8个字节,以下的操作实际上只是改变了指针指向
     270 int *p4 = &num2;
     271 void *pv = p4;        
     272 char *pc = pv;
     273 
     274 但是 int temp = *pv 这样的写法是错误的,因为 pv 的指针类型是 void 型的,不能对 void* 进行取值
     275 
     276 C语言中运算符只有以下三种是右结合性
     277 1、单目运算符
     278 2、赋值运算符
     279 3、三目运算符
     280 指针的算术运算只能由加(+)、减(-)、后自增(++)、后自减(--),不能是乘(*)、除(/)、取余(% 281 
     282 指针函数:返回类型为指针的函数(例如 strcpy() 、 memcpy())
     283     (注:指针函数返回参数千万不要返回一个局部变量的指针,因为局部变量在调用结束后会被自动销毁)
     284 函数指针:指向和函数的指针(int (*_) (int,int))
     285 
     286 /*******************************************************************************************/
     287 函数指针示例:
     288 #include <stdio.h>
     289 
     290 int max(int num1,int num2)
     291 {
     292     printf("max:excate
    ");
     293     return num1 > num2 ? num1 : num2;
     294 }
     295 
     296 float add(float num1 , float num2)
     297 {
     298     printf("add : excate
    ");
     299     return num1 + num2;
     300 }
     301 
     302 int main()
     303 {
     304     printf("main = %p , max = %p , add = %p
    ",main,max,add);
     305     //int *p = max;//由于max是函数名,虽然也是个地址,但是不能用一般的指针指向它,需要用到函数指针
     306     int (*pfun)(int , int );//声明的函数指针
     307     pfun = max;//对函数指针进行赋值,此时使用pfun(int,int),与max(int,int)效果一样
     308     printf("pfun = %p , max = %p
    ",pfun,max);
     309     int res = pfun(1,2);
     310     printf("pfun_res = %d
    ",res);
     311 
     312     float (*pfun2)(float,float) = add;
     313     printf("pfun2 = %p , add = %p
    " , pfun2 , add);
     314     float r = pfun2(1.2,3.4);
     315     printf("r = %f
    ",r);
     316 }
     317 /*******************************************************************************************/
     318 函数指针的使用:主要用于作参数,提高函数的通用性(回调函数)
     319 
     320 
     321 预处理指令:
     322     #include //静态包含
     323     #define     //宏替换
     324     #if      //条件编译
     325     #ifdef      //条件编译
     326     #ifndef     //条件编译
     327 
     328 1、预处理指令只占用编译时间,不占用调用时间
     329 2、一行只能写一个预处理指令,不是C语言语句,不需要加分号(;)
     330 
     331 宏替换:
     332     1、不带参宏  2、带参宏
     333     不带参宏示例:
     334         #define PI 3.14
     335 宏替换在预处理时会将宏名替换成宏体内容,不会进行安全检查,且如为带参宏需要注意其优先级,最好是给每一个参数都加上个小括号"()"
     336 
     337 系统的预定义宏:
     338     __FILE__    类型为 const char * , 所在文件的文件名
     339     __func__    类型为 const char * , 所在函数的函数名
     340     __LINE__    类型为 int          , 所在的行数
     341     __DATE__    类型为 const char * , 系统当前日期
     342     __TIME__    类型为 const char * , 系统当前时间
     343 
     344 /*********************************************************************************************/
     345 
     346 结构体:
     347     struct studnet                        struct 结构体类型名
     348     {                                    {
     349                                             成员类型1 成员变量1
     350                                             成员类型2 成员变量2    
     351     };                                    };
     352 
     353 声明结构体变量的格式:struct 结构体类型名 变量名
     354 1、结构体内部变量内存是连续的
     355 2、结构体成员如果没有初始化则会是随机数
     356 3、结构体类型不占内存,结构体变量才占内存,其大小为所有成员大小之和,而且与结构体成员个数和顺序有关。
     357 
     358 struct temp1        //8字节                                    
     359 {
     360     char ch1;
     361     char ch2;
     362     int  i;
     363 };
     364 
     365 struct temp2        //12字节
     366 {
     367     char ch1;
     368     int  i;
     369     char ch2;
     370 };
     371 
     372 结构体中的成员可以是不同的数据类型,成员按照定义时的顺序依次存储在连续的内存空间。
     373 和数组不一样的是,结构体的大小不是所有成员大小简单的相加,需要考虑到系统在存储结
     374 构体变量时的地址对齐问题。
     375 
     376 偏移量指的是结构体变量中成员的地址和结构体变量地址的差。结构体的大小是由最后一个
     377 成员的偏移量加上最后一个成员的大小。显然 (temp2),结构体变量中第一个成员的地址
     378 就是结构体变量的首地址。因此,第一个成员ch1的偏移量为0。第二个成员i的偏移量是第一个
     379 成员的偏移量加上第一个成员的大小(0+1),其值为1;第三个成员ch2的偏移量是第二个成员
     380 的偏移量加上第二个成员的大小(1+4),其值为5。
     381 
     382 根据内存对齐的方法,用最后一个成员的偏移量加上最后一个成员的大小,得出结构体大小为12
     383 
     384 结构体中的成员可以是本结构体类型的指针,不能是本结构体的变量,因为编译器不知道给它分配多少内存
     385 (可以是其他结构体的变量)
     386 
     387 结构体的定义:
     388     匿名定义:
     389         struct
     390         {
     391             char name[16];
     392             int age;
     393         }stu1 = {"zhangsan",22};//必须在声明结构体类型时声明变量
     394 
     395 
     396         struct student
     397         {
     398             char name[16];
     399             int age;
     400             struct
     401             {
     402                 int year;
     403                 int month;
     404                 int day;
     405             }bdate;
     406         };
     407 
     408 /***********************************************************************************/
     409 共用体:几个不同的变量共享一段内存的结构,成为"共用体"类型的结构
     410 (同一时刻只能做一种功能)
     411 
     412 定义共用体类型变量的一般形式为:
     413     union 共用体名
     414     {
     415         成员表列
     416     }变量表列;
     417 
     418 例如:
     419 union Data
     420 {
     421     int i;
     422     char ch;
     423     float f;
     424 }a,b,c;
     425 
     426 地址空间表示图:
     427 |地址 |1000|1001|1002|1003|
     428 |_____|____|____|____|____|
     429 |int  |____|____|____|____|        
     430 |char |____|____ ____ ____
     431 |float|____|____|____|____|
     432 
     433 以上3个变量在内存中占的字节数不同,但是都是从同一地址开始,也就是使用了覆盖技术,后面的数据覆盖了前面的数据
     434 
     435 共用体类型数据的特点:
     436     同一内存段可以用来存放不同类型的成员,但在每一瞬间只能存放其中一个成员,而不是同时存放几个
     437 
     438 /***********************************************************************************/
     439 #include <stdio.h>
     440 
     441 union stu
     442 {
     443     int i;
     444     char c;
     445     float f;
     446 }a;
     447 
     448 int main()
     449 {
     450     a.i = 0x61;//0x61为97
     451     printf("%d
    ",a.i);//97
     452     printf("%c
    ",a.c);//a
     453     printf("%f
    ",a.f);//0.000000
     454 }
     455 /***********************************************************************************/
     456 97转换成16进制为0x00000061,因为电脑的存储发方式是小端字节序格式,因此地址在内存中的存储方式如下所示
     457 
     458 |地址 |1000|1001|1002|1003|
     459 |_____|____|____|____|____|
     460 |int  |0x61|0x00|0x00|0x00|        
     461 |char |0x61|____ ____ ____
     462 |float|0x61|0x00|0x00|0x00|
     463 
     464 共用体的用途:
     465     在数据处理中,需要对同一空间安排不同的用途,使用共用体比较方便
     466         例如:有若干人员的数据,其中有学生和教师。学生的数据中包括:姓名、号码、性别、职业、班级。
     467         教师的数据包括:姓名、号码、性别、职业、职务。要求用同一表格来处理
     468 可以看出:学生和老师的数据的项目大多数是相同的,只有一个不同,学生的班级,教师的职位。
     469 struct{
     470     int num;          //成员 编号
     471     char name[10];    //成员 姓名
     472     char sex;         //成员 性别
     473     union{            //声明无名共用体类型
     474         int class;    //成员 班级
     475         char position[10];//成员 职务
     476     }category;
     477 }person[2];
     478 /***********************************************************************************/
     479 /***********************************************************************************/
     480 枚举:
     481     枚举是一种特殊类型,它的值包含在一个命名的常量集合中,这些常量称为枚举符。在实际应用中,有的
     482     变量只有几种可能取值,如人的性别只有两种可能取值,星期只有七种可能取值,在C语言种对这样取值
     483     比较特殊的变量可以定义为枚举类型。所谓枚举是指将变量的值一一列举出来,变量只限于列举出来的值
     484     的范围内取值。
     485     
     486     定义一个变量时枚举类型,可以先定义一个枚举类型名,然后再说明这个变量是该枚举类型。
     487 
     488     例如:
     489         enum weekday{sun,mon,tue,wed,thu,fri,sat};
     490     定义了一个枚举类型名enum weekday,然后定义变量为该枚举类型。
     491         enum weekday day;
     492     当然,也可以直接定义枚举类型变量,如:
     493         enum weekday{sun,mon,tue,wed,thu,fri,sat} day;
     494 需要说明的是:
     495     1、枚举元素不是变量,而是常数,因此枚举元素又称枚举常量,所以不能对枚举元素进行赋值。
     496     2、枚举元素作为常量,它们是有值的,C语言在编译时按定义的顺序使他们的值为 1,2,3...
     497 /***********************************************************************************/
     498 
     499 
     500 /***********************************************************************************/
     501 用数组存放数据时,需要事先定义数组的大小或者尽量放大数组的大小,显然浪费内存且不易更改,
     502 而链表则无这种缺点。链表是一种可动态地进行存储分配的结构
     503 链表有一个头指针(head),存放的地址指向一个元素,链表中的每个元素称为节点,每个节点由两部分组成
     504 即数据部分和指针部分。
     505  
     506 | a | b |  | a | b |  | a | b |  
     507 
     508 a为值部分,b为指针部分
     509 
     510 /***********************************************************************************/
     511 创建一个简单的链表
     512 #include <stdio.h>
     513 
     514 struct student 
     515 {
     516     int num;
     517     float score;
     518     struct student *next;
     519 };
     520 
     521 int main()
     522 {
     523     struct student a,b,c;    //三个链表节点
     524     struct student *head;
     525     struct student *p;
     526 
     527     a.num = 12345;
     528     a.score = 80;
     529     b.num = 56789;
     530     b.score = 90;
     531     c.num = 90123;
     532     c.score = 99;
     533 
     534     head = &a;
     535     a.next = &b;
     536     b.next = &c;
     537     c.next = NULL;
     538 
     539     p = head;
     540 
     541     do{
     542         printf("%d
    ",p->num);
     543         p = p->next;
     544     }while(p != NULL);
     545     return 0;
     546 }
     547 /***********************************************************************************/
     548 
     549 这个链表所有节点都是在程序中定义,不是临时开辟,页没有用完就释放,这种链表称为静态链表
     550 
     551 
     552 动态链表:
     553 所谓动态链表,是指在程序执行过程中从无到有地建立起一个链表,即一个一个地开辟结点和输入各
     554 结点数据,并建立起前后相链的关系。
     555 
     556 参考程序:
     557 #include <stdio.h>
     558 #include <stdlib.h>
     559 
     560 struct Student
     561 {
     562     int No;
     563     struct Student *next;
     564 };
     565 
     566 int main()
     567 {
     568     struct Student *p1 , *p2;
     569     struct Student *head , *p;
     570     int n = 0;//结点个数
     571 
     572     head = NULL;
     573     P1 = (struct Student *)malloc(sizeof(struct Student));
     574     printf("请输入第1个学号
    ");
     575     scanf("%d",&p1->No);
     576     p2 = p1;
     577     while(p1->No != 0)
     578     {
     579         n++;
     580         if(n == 1)
     581         {
     582             head = p1;
     583         }
     584         else
     585         {
     586             p2->next = p1;
     587         }
     588         p2 = p1;
     589         printf("请输入学号,输入0终止:
    ");
     590         p1 = (struct Student *)malloc(sizeof(struct Student));
     591         scanf("%d",&p1->No);
     592     }
     593     p2->next = NULL;
     594     p = head;
     595     printf("
    学号为:
    ");
     596     while(p != NULL)
     597     {
     598         printf("%d
    ",p->No);
     599         p = p->next;
     600     }
     601     return 0;
     602 }
     603 /***********************************************************************************/
     604 静态链表和动态链表的区别:
     605     1、静态链表是用类似于数组方法实现的,是顺序的存储结构,物理地址上是连续的,而且需要
     606 预先分配地址空间大小。所以静态链表的初始化长度一般是固定的,在做插入和删除操作时不需要移
     607 动元素,仅需要改指针。
     608     2、动态链表是用内存申请函数(malloc())动态申请内存的,所以在链表的长度上没有限制。
     609     因为是动态申请内存的,所以每个节点的物理地址不连续,要通过指针来顺序访问。    
     610 
     611 单向循环链表:
     612     将单链表中尾节点的指针由空改为指向头结点(或第一个元素节点),就使得整个单链表形成了一个环
     613     这种头尾相接的单链表称为单向循环链表。
     614 
     615          __________________________________________________    
     616         |                                                   |            
     617         v                                                   |    
     618     |head|next| -> | A |next| -> | B |next| -> | C |next| --
     619 
     620                          __________________________________
     621                         |                                   |            
     622                         v                                   |    
     623     |head|next| -> | A |next| -> | B |next| -> | C |next| --
     624 
     625 
     626 双向链表:
     627     在单向链表的结点中增加一个指向其前驱的pre指针,该链表中第一个结点的前驱结点为NULL
     628     最后一个结点的后继结点为NULL;
     629                                     ______________         _______________        
     630                                    |               |        |               |
     631                                    v              |     v               |
     632     |before|head|next| -> |before| A |next| -> |before| B |next| -> |before| C |next|-> NULL 
     633                   NULL<-------|
     634 
     635 双向循环链表:
     636     双向循环链表直接体现为"双向和循环",一般的单链表只有结点数据data和next指向地址,而在
     637     此需要增加前面部分的 pre 指向地址,同时还需要循环。
     638 
     639 
     640 
     641 /***********************************************************************************/
     642 树与二叉树:
     643 树:
     644     树形结构是一种典型的非线性数据结构,体现在数据元素之间有明显的层次关系
     645 定义:树是由n个节点构成的有限集合,n大于等于零,节点数为零的树称为空树,节
     646 点数大于零的树称为非空树
     647 
     648 一颗非空树满足以下条件:
     649     1、有且仅有一个被称为根(R)的特殊节点,其余所有节点都可由节点经过一定的分支得到,而根节点 R 没有前驱节点
     650     2、当n > 1时,除根节点R外的其他节点被分成 m 个互不相交的子集,T1 ,T2 ,....Tm,其中每个自己Ti本身又是一棵树,被称为R节点的子树
     651 
     652 树的定义时递归定义,即每个子树的定义也是按照上面的过程完成的。
     653 
     654 相关名词:
     655     1、节点
     656     2、节点的度
     657     3、树的度
     658     4、叶子节点
     659     5、分支节点
     660     6、节点的层次
     661     7、树的深度
     662 
     663  双亲:即上层的那个节点(直接前驱)
     664  孩子:即下层节点子树的根
     665 
     666 二叉树:
     667     二叉树与树完全不同,树的根不允许为空,而二叉树的根允许为空,且二叉树每个节点最多允许2个节点
     668 
     669     二叉树性质:1、在二叉树的第 i 层上至多有 2^i-1 个节点
     670                2、深度为 k 的二叉树最多有 2^k-1 个节点
     671 
     672     满二叉树:一棵树的深度为 K ,且有2^k-1 个节点的二叉树。
     673 
     674     完全二叉树:最大深度与最小深度相差不超过 1 
     675 
     676 二叉树的遍历:
     677     前序遍历、中序遍历、后序遍历
     678     示例:
     679                     A
     680                    /             
     681                   B   C
     682                  /     
     683                 D   E
     684 前序遍历:先根再左再右    ABDEC
     685 中序遍历:先左再根再右    DBEAC
     686 后序遍历:先左再右再根    DEBCA      
     687 
     688 
     689 
     690 /***********************************************************************************/
     691 文件操作:
     692     1、打开文件
     693     2、文件读/ 694     3、关闭文件
     695 
     696 标准I/O(不区分操作系统,在任何系统中都可以使用):
     697 
     698     文件常见的操作:
     699         fopen/fclose
     700         fgets/fputs
     701         fgetc/fputc
     702         fwrite/fread
     703         fseek
     704 FILE* fopen(const char *path ,const char *mode);
     705 FILE* freopen(const char *restrict pathname,const char*restrict type,FILE *restrict fp);//用于重定向
     706 FILE *fdopen(int filedes,const char *type);
     707 
     708 fseek,用于将指针移到文件的某一个位置
     709 语法:
     710     status = fseek(fileID,offset,origin)
     711 
     712     fileID是fopen打开时产生的整数标识,大于 0 时,表示文件成功打开,在文件中,offset是对于origin位置
     713     开始移动的整数,origin有三种状态,bof,cof,eof,其分别表示文件的开始位置,当前位置,和末尾位置,如果操作
     714     成功,则状态返回 0,否则返回-1
     715 
     716 
     717 fopen() 打开由 path 指定的一个文件,mode参数如下:
     718 ---------------------------------------------------------------------------------------------------------
     719 |    r或者rb    |    打开只读文件,该文件必须存在                                                            |
     720 |      r+或r+b    |     打开可读写的文件,该文件必须存在。                                                       |
     721 |      w或wb        |   打开只写文件,若文件存在则文件长度清为0,即会擦除文件以前内容,若文件不存在则会建立该文件     |
     722 | w+或w+b或wb+  |   打开可读写文件,若文件存在则文件长度清为0,即会擦除文件以前内容,若文件不存在则会建立该文件   |
     723 |      a或ab      |   以追加的方式打开只写文件,若文件不存在,则会建立文件,如果文件存在,写入的数据                  |        
     724 |                    会被加到文件尾,即文件原先内容会被保留                                                     |
     725 |    a+或a+b或ab+|    以追加的方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,从头读取,但是写入  |
     726 |                    的数据会被加到文件尾,即文件原先的内容会被保留                                             |
     727 ---------------------------------------------------------------------------------------------------------|
     728 
     729 /***********************************************************************************/
     730 fopen示例:
     731 #include <stdio.h>
     732 
     733 int main()
     734 {
     735     FILE* fp;
     736     int i;
     737     char name[8];
     738     fp = fopen("path","r");
     739     fscanf(fp,"%s",name);
     740     fclose(fp);
     741     for(i = 0 ; i < 8 ; i++)
     742     {
     743         printf("%c",name[i]);
     744     }
     745     printf("
    ");
     746     return 0;
     747 }
     748 /***********************************************************************************/
     749 读写的函数:
     750     一次读写一个字节:
     751         fgetc/getchar/get
     752         fputc/putchar/putc
     753     
     754     一次去写一行:
     755         fgets/gets        
     756         fputs/puts
     757     gets不使用,因为gets获取一行数据是指导EOF而结束的,如果变量大小小于字符大小,则会造成内存溢出
     758     puts函数主要用于向标准输出设备写入字符串并换行,即会自动写一个'
    '
     759     fputs函数用来向指定文件写入一个字符串(不换行)
     760 
     761 
     762     fwrite/fread
     763     函数原型:
     764         size_t fread(void *buffer,size_t num_bytes,size_t count,FILE *fp);
     765         size_t fwrite(const void *buffer,size_t num_bytes,size_t count,FILE *fp);
     766 
     767       对fread()而言,buffer是接收读入数据的内存区的指针。
     768       对fwrite()而言,buffer是写入到那个文件的信息的指针。
     769       count的值确定读/写多少项,每项长度等于num_bytes。
     770       fp是指向事先打开的流的指针。
     771       正常情况下,fread()返回读入的项数,fwrite()返回写出的项数。
     772       只要文件按二进制打开,fread()和fwrite()就可以读/写各类信息。以下程序先向文件写double,int和long型数据,然后再读回。
     773 
     774 -----------------------------------------------------------------------------
     775                     打开一个标准I/O流的六种不同方式
     776 
     777         限制                    r       w       a      r+       w+
     778     文件必须已存在            √                        √
     779     擦除文件以前的内容                √                        √
     780     流可以读                    √                        √        √
     781     流可以写                            √        √        √        √
     782     流只可在尾端处写                            √
     783 ------------------------------------------------------------------------------
     784 
     785 FILE指针:每个被使用的文件都在内核中开辟一个区域,用来存放文件的相关信息,这些信息是保存在一个结构体类型的
     786 变量中,该结构体类型是由系统定义的,取名为FILE
     787 
     788 标准I/O库的所有操作都是围绕流(stream)来进行的,在标准I/O中,流用 FILE* 来描述
     789     
     790     流(stream):
     791         定义:所有的I/O操作仅是简单的从程序移进或者移出,这种字节流,就称为流
     792         分类:文本流/二进制流
     793 
     794         文本流:
     795             定义:在流中处理数据是以字符出现,在文本流中,'
    ' 被转换成回车符CR和换行符LF的ASCII码0DH和0AH
     796                 而当输出时,0DH和0AH被转换成'
    '
     797             数字2001在文本流中的表示方法为'2','0','0','1'
     798             ASCII: 50 48 48 49
     799 
     800         二进制流:
     801             定义:流中处理的是二进制序列,若流中有字符,则用一个字节的二进制ASCII码表示;若是数字,则用对应的二进制数表示。对'
    '不进行变换
     802                 数字2001在二进制流中的表示方法为 00000111 11010001
     803 
     804 /***********************************************************************************/
     805 文件I/O(系统I/O):
     806     文件I/O相关操作函数
     807         open()  creat()  close()  read()  write()  lseek()  
     808  
     809 int open(const char *pathname , int flags);
     810 int open(const char *pathname , int flags , mode_t mode);
     811 int creat(const char *pathname , mode_t mode);
     812 
     813 pathname 是表示欲打开的文件路径字符串
     814 flags 是表示打开方式:
     815     O_RDONLY 以只读方式打开文件
     816     O_WRONLY 以只写方式打开文件
     817     O_RDWR 以可读写方式打开文件. 上述三种旗标是互斥的, 也就是不可同时使用, 但可与下列的旗标利用OR(|)运算符组合.
     818     O_CREAT 若欲打开的文件不存在则自动建立该文件.
     819     O_EXCL 如果O_CREAT 也被设置, 此指令会去检查文件是否存在. 文件若不存在则建立该文件, 否则将导致打开文件错误. 此外, 若O_CREAT 与O_EXCL 同时设置, 并且欲打开的文件为符号连接, 则会打开文件失败.
     820     O_NOCTTY 如果欲打开的文件为终端机设备时, 则不会将该终端机当成进程控制终端机.
     821     O_TRUNC 若文件存在并且以可写的方式打开时, 此旗标会令文件长度清为0, 而原来存于该文件的资料也会消失.
     822     O_APPEND 当读写文件时会从文件尾开始移动, 也就是所写入的数据会以附加的方式加入到文件后面.
     823     O_NONBLOCK 以不可阻断的方式打开文件, 也就是无论有无数据读取或等待, 都会立即返回进程之中.
     824     O_NDELAY 同O_NONBLOCK.
     825     O_SYNC 以同步的方式打开文件.
     826     O_NOFOLLOW 如果参数pathname 所指的文件为一符号连接, 则会令打开文件失败.
     827     O_DIRECTORY 如果参数pathname 所指的文件并非为一目录, 则会令打开文件失败。注
     828 
     829 mode 表示打开权限
     830     S_IRWXU00700 权限, 代表该文件所有者具有可读、可写及可执行的权限.
     831     S_IRUSR 或S_IREAD, 00400 权限, 代表该文件所有者具有可读取的权限.
     832     S_IWUSR 或S_IWRITE, 00200 权限, 代表该文件所有者具有可写入的权限.
     833     S_IXUSR 或S_IEXEC, 00100 权限, 代表该文件所有者具有可执行的权限.
     834     S_IRWXG 00070 权限, 代表该文件用户组具有可读、可写及可执行的权限.
     835     S_IRGRP 00040 权限, 代表该文件用户组具有可读的权限.
     836     S_IWGRP 00020 权限, 代表该文件用户组具有可写入的权限.
     837     S_IXGRP 00010 权限, 代表该文件用户组具有可执行的权限.
     838     S_IRWXO 00007 权限, 代表其他用户具有可读、可写及可执行的权限.
     839     S_IROTH 00004 权限, 代表其他用户具有可读的权限
     840     S_IWOTH 00002 权限, 代表其他用户具有可写入的权限.
     841     S_IXOTH 00001 权限, 代表其他用户具有可执行的权限.
     842 
     843 
     844 这些函数都会返回一个文件描述符fp来表示当前打开的文件,文件描述符是一个非负整数,打开现存文件或者新建文件时,内核会返回一个文件描述符,用于指代被打开的文件,
     845 所有执行I/O操作的系统调用都通过文件描述符。进程刚刚启动的时候,0是标准输入,1是标准输出,2是标准错误。如果此时去打开一个新的文件,它的文件描述符会是3。
     846 
     847 
     848 权限的修改和阅读:
     849     权限分类:
     850             r      w      x
     851             4      2      1
     852 
     853     示例:
     854         -rwxrwxrwx   0777最高权限,采用8进制方法进行表示
     855         -rw-r--r--     0644
     856 
     857 标准I/O和文件I/O的区别:
     858     标准I/O是ANSI C建立的一个标准I/O模型,是一个标准函数包和stdio.h头文件中的定义,具有一定的可移植性,标准I/O库处理很多细节
     859     例如缓存分配,以优化长度执行I/O等。标准I/O提供了三种类型的缓存。
     860         1、全缓存:当填满标准I/O缓存后才进行实际的I/O操作
     861         2、行缓存:当输入或输出中遇到新行符时,标准I/O库执行I/O操作
     862         3、不带缓存:stderr就是了
     863 
     864     文件I/O被称为不带缓存的I/O,不带缓存指的是每个read, write , 都调用内核中的一个系统调用,也就是一般所说的低级I/O
     865 
     866     文件I/O使用文件描述符来表示打开的文件,所以可以访问不同类型的文件,如普通文件、管道文件等。而标准I/O使用的是FILE(流)
     867     来表示打开的文件,通常只用来访问普通文件。
     868 
     869 文件I/O使用示例:
     870 /***********************************************************************************/
     871 #include <unistd.h>
     872 #include <stdio.h>
     873 #include <sys/types.h>
     874 #include <sys/stat.h>
     875 #include <fcntl.h>
     876 
     877 int main(void)
     878 {
     879     int f;
     880     int size;
     881     char buf[100];
     882     f = open(path , O_RDONLY);   //path为文件路径
     883     size = read(f , buf , sizeof(buf));
     884     printf("size = %d , buf = %s
    ",size , buf);
     885     close(f);
     886     return 0;
     887 }
     888 /***********************************************************************************/
     889 
     890 
     891 /***********************************************************************************/
     892                                     文件和目录
     893 以下三个函数可以获取文件/目录的属性信息:
     894 #include <unistd.h>
     895 #include <sys/types.h>
     896 #include <sys/stat.h>
     897 
     898 int stat(const char *path, struct stat *buf);
     899 int fstat(int fd, struct stat *buf);
     900 int lstat(const char *path, struct stat *buf);
     901 
     902 三个函数的返回:若成功则为0,若出错则为-1,并且设置errno.
     903 给定一个path的情况下:
     904 stat函数返回一个与此命名文件有关的信息结构
     905 fstat函数获得已在描述符filedes上打开的文件的有关信息
     906 lstat函数类似于stat,但是当命名的文件是一个符号连接时,lstat返回该符号连接的有关信息,而不是由该符号连接引用的文件的信息。
     907 
     908 
     909 掌握目录操作相关焊数
     910 chdir()
     911 opendir()
     912 readdir()
     913 
     914 /***********************************************************************************/
     915 
     916 
     917 /***********************************************************************************/
     918                                 动态库和静态库
     919 什么是库:
     920     库是共享程序代码的方式,一般分为静态库和动态库
     921 静态库:
     922     链接时完整的拷贝至可执行文件中,被多次使用就有多份冗余拷贝
     923 动态库:
     924     链接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序共用,节省空间。
     925 
     926 使用静态库和动态库的好处:
     927     1、使用静态库的好处
     928         模块化,分工合作
     929         避免少量的改动经常导致大量的重复编译链接
     930         也可以重用(注意不是共享内存)
     931     2、使用动态库的好处:
     932         使用动态库可以将最终可执行的文件体积缩小
     933         使用动态库多个应用程序共享内存中的同一份库文件,节省资源
     934         使用动态库可以不重新编译链接可执行程序的前提下,更新动态库文件达到更新应用程序的目的
     935 /***********************************************************************************/
     936 
     937 /***********************************************************************************/
     938                                     进程/线程
     939 创建   执行     消亡
     940 进程是一个独立的可调度的任务
     941 进程是一个程序的一次执行的过程
     942 
     943 进程的定义:"进程"是操作系统的最基本,最重要的概念之一。但迄今为止对这一概念还没有一个确定的统一描述。
     944     下面给出几种对进程的定义描述。进程是程序的一次执行,进程是可以并行执行的计算,进程是一个程序与其
     945     使用的数据在处理机上顺序执行时发生的活动。进程是程序在一个数据集合上的运行过程。它是系统进行资源分
     946     配和调度的一个独立单位。
     947 
     948 进程的特征:
     949     多态性:是程序的一次执行;
     950     并发性:进程是可以并发执行;
     951     独立性:是系统进行资源分配和调度的一个独立单位
     952     异步性:进程间的相互制约,使进程执行具有间隙、
     953     结构性:进程是具有结构的
     954 
     955 进程和程序的区别:
     956     1、程序是永存的,进程是暂时的,是程序在数据集上的一次执行,有创建有撤销,存在是暂时的
     957     2、程序是静态的观念,进程是动态的观念
     958     3、进程具有并发性,而程序没有
     959     4、进程是竞争计算机资源的基本单位,程序不是
     960     5、进程和程序不是一一对应的,一个程序可以对应多个进程可执行同一程序;一个进程可以执行一个或多个程序
     961 
     962 进程创建:fork()     会复制进程(父 ,子)  vfork() 
     963 #include <sys/type.h>
     964 #include <unistd.h>
     965 #include <stdio.h>
     966 #include <stdlib.h>
     967 
     968 int main(){
     969 
     970     pid_t pid; 
     971     pid = fork();   //当fork调用成功后,子进程开始从fork后开始执行
     972 
     973     //创建进程失败
     974     if(-1 == pid){
     975         perror("fork failed");
     976         return -1;
     977 
     978     //父进程
     979     }else if(pid > 0){
     980             //pid用于接收当前进程id, ppid用于接收父进程id
     981             printf("parent : pid = %d , ppid = %d
    ", getpid(), getppid());
     982             sleep(1);//系统延时
     983 
     984     //子进程
     985     }else if(pid == 0){
     986             printf("child: pid = %d, ppid = %d
    ", getpid(), getppid());
     987             sleep(1);
     988     }
     989     return 0;
     990 }
     991 
     992 返回值 pid : 0:子进程
     993              子进程pid(大于 0 的整数):父进程
     994              -1:出错
     995 
     996 ( pid_t 的底层代码就是一个 int 997 
     998 
     999 fork() 和 vfork() 的区别:
    1000     fork()和 Vfork()都可以创建一个进程,但 vfork() 是由 fork()封装得来的
    1001 其实 fork 创建的子进程相对独立,当用 fork 后子进程与父进程同时各自进行自己的程序互不影响,但只
    1002 有一个终端接受他们两个的输出,所以并不是子进程与父进程随即调用,而只是在同时执行父进程和子进程,
    1003 只是随机输出,子进程与父进程仍各自有序。
    1004 
    10051)使用fork创建一个进程时,子进程只是完全复制父进程的资源。这样得到的子进程独立于父进程,具有
    1006 良好的并发性。而使用vfork创建一个子进程时,操作系统并不将父进程的地址空间完全复制到子进程,用vfork
    1007 创建的子进程共享父进程的地址空间,也就是说子进程完全运行在父进程的地址空间上。子进程对该地址空间中
    1008 任何数据的修改同样为父进程所见。
    1009 
    10102)使用fork创建一个子进程哪个进程先运行取决于系统的调度算法。而vfork创建一个进程时,vfork保证子进程
    1011 先运行,当他调用exec或exit之后,父进程才可能被调读运行。如果在调用exec或exit之前子进程要依赖父进程的
    1012 某个行为,就会导致死锁。
    1013 
    1014 因为使用fork创建一个进程时,子进程需要将父进程几乎每种资源都复制,所以fork是一个开销很大的系统调用,
    1015 这些开销并不是所有情况都需要的。比如fork一个进程后,立即调用exec执行另一个应用程序,那么fork过程中子
    1016 进程对父进程地址空间的复制将是一个多余的过程。vfork不会拷贝父进程的地址空间,这大大减小了系统的开销。
    1017 
    1018 当用vfork创建进程时,若以return 0 结束则释放局部变量,以exit(0)结束则不会释放。
    1019 
    1020 (p185)fork一个子进程,该子进程中的var和globvar记录的是父进程中var和globvar中的值,而fork之后父进程对变量的改变则不对子进程产生影响。
    1021 
    1022 vfork一个子进程,先执行子进程,子进程沿用父进程中的变量,当以exit(0)结束后,父进程可仍沿用子进程中的变量。
    1023 当以return 0 结束则释放局部变量,父进程再引用时则会为系统给的随机值。
    1024 
    1025 
    1026 在任何位置执行 exit() 都会结束进程(会刷新缓存),_exit() 不会刷新缓存
    1027 
    1028 atexit() 注册退出函数
    1029 int atexit(void(* function)(void));
    1030 把function注册到系统中,退出的时候会调用这个函数
    1031 
    1032 excel() 执行其他的程序代码数据段全部被新的程序的代码段和数据段取代,只要执行成功,就不会返回了
    1033         用来执行参数path字符串所代表的文件路径(绝对路径),第二个参数代表执行文件时传递的agrv,最后一个参数必须是空指针
    1034         excel("/bin/cat","/etc/passwoed",NULL);
    1035                命令路径        参数路径
    1036 
    1037 wait() 和 waitpid()的区别:
    1038     调用 wait() 函数使程序进行阻塞,直到任一个子进程结束或者是该进程接收到了一个信号为止。如果该进程没用子进程或者其子进程已经结束,wait() 函数会立即返回。
    1039     调用 waitpid() ,参数 pid 指定想等待的子进程ID,值 -1 表示第一个终止的子进程,参数 options 指定附加页,最常用的是 WNOHANG ,通知内核在没有已终止子进程的时候不要阻塞
    1040 当正常返回的时候,waitpid返回收到的子进程的进程ID;
    1041 如果设置了选项 WNOHANG ,而调用中waitpid 发现没有已退出的子进程可收集,则返回0
    1042 如果调用中出错,则返回 -1 ,这时 errno 会被设置成相应的值以指示错误所在 
    1043 /***********************************************************************************/
    1044 
    1045 /***********************************************************************************/
    1046 
    1047                                                     网络编程
    1048 DNS:常用的有114.114.114.114 /  8.8.8.8 用来解析数据
    1049 子网掩码:用于判断IP是不是同一个网段
    1050 
    1051 例如:有两个IP,192.168.0.100  ,  192.168.0.8 两个IP需要通信,则两个IP与子网掩码作按位与操作
    1052 192.168.0.100 & 0xff.0xff.0xff.0 = 192.168.0.0
    1053 192.168.0.8   & 0xff.0xff.0xff.0 = 192.168.0.0
    1054 两次的值相等,则说明两个IP在同一网段
    1055 
    1056 网络字节序都是大端字节序
    1057 TCP:
    1058 TCP服务端流程
    1059     1、创建套接字socket
    1060     2、绑定端口 bind
    1061     3、监听是否有客户端连接 Listen
    1062     4、接受    accept
    1063     5、读/写 send/recv
    1064     6、关闭  close
    1065 
    1066 TCP客户端:
    1067     1、创建套接字  socket
    1068     2、主动连接别人 connect
    1069     3、读/写 send/recv   
    1070     4、关闭  close
    1071 
    1072 UDP:
    1073 UDP服务器流程:
    1074     1、创建一个套接字 socket
    1075     2、设置socket属性 setsockept
    1076     3、绑点IP地址,端口等信息到socket上,用函数bind
    1077     4、循环接收数据,用函数数recvfrom
    1078     5、关闭网络连接
    1079 
    1080 UDP客户端流程:
    1081     1、创建一个套接字 socket
    1082     2、设置socket属性,用函数setsockeopt
    1083     3、绑点IP地址,端口等信息到socket上,用函数bind
    1084     4、设置对方的IP地址和端口等属性; 
    1085     5、发送数据,用函数数sendto
    1086     6、关闭网络连接
    1087 
    1088 
    1089             OSI模型                  TCP/IP协议
    1090             应用层                        应用层
    1091             表示层
    1092             会话层        
    1093             传输层                        传输层
    1094             网络层                        网络层
    1095             数据链络层                    数据链络层
    1096             物理层                        物理层
    1097 
    1098 
    1099 TCP和UDP都是OSI模型中传输层的协议,TCP提供可靠的通信传输,而UDP则常被用于让广播和细节控制交给应用的通信传输
    1100 
    1101 UDP补充:
    1102    UDP不提供复杂的控制机制,利用IP提供面向无连接的通信服务。并且它是将应用程序发来的
    1103    数据在收到的那一刻,立刻按照原样发送到网络上的一种机制。即使是出现网络拥堵的情况下,
    1104    UDP也无法进行流量控制等避免网络拥塞的行为。此外,传输途中如果出现了丢包,UDO也不负
    1105    责重发。甚至当出现包的到达顺序乱掉时也没有纠正的功能。如果需要这些细节控制,那么不得
    1106    不交给由采用UDO的应用程序去处理。换句话说,UDP将部分控制转移到应用程序去处理,自己
    1107    却只提供作为传输层协议的最基本功能。UDP有点类似于用户说什么听什么的机制,但是需要用
    1108    户充分考虑好上层协议类型并制作相应的应用程序。
    1109 
    1110 TCP补充:
    1111   TCP充分实现了数据传输时各种控制功能,可以进行丢包的重发控制,还可以对次序乱掉的分包
    1112   进行顺序控制。而这些在UDP中都没有。此外,TCP作为一种面向有连接的协议,只有在确认通信
    1113   对端存在时才会发送数据,从而可以控制通信流量的浪费。TCP通过检验和、序列号、确认应答、
    1114   重发控制、连接管理以及窗口控制等机制实现可靠性传输。
    1115 
    1116 
    1117 TCP与UDP区别总结:
    1118 1、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接
    1119 2、TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保   证可靠交付
    1120 3、TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的
    1121   UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
    1122 4、每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
    1123 5、TCP首部开销20字节;UDP的首部开销小,只有8个字节
    1124 6、TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
    1125 
    1126 
    1127 
    1128 
    1129 
    1130 
    1131 排序算法:
    1132     1、选择排序
    1133     2、冒泡排序
    1134     3、快速排序
    1135     4、希尔排序
    1136     5、桶排序
    1137     6、计数排序
    1138     7、插入排序
    1139     8、归并排序
    1140     9、堆排序
    1141     10、基数排序
    1142 
    1143 1、选择排序:
    1144 /**********************************************************************************************************************/
    1145 #include <stdio.h>
    1146 #define ARRSIZE 5
    1147 
    1148 void ChoiceSort(int *arr)
    1149 {
    1150     int i,j,min,temp;
    1151     for(i = 0 ; i < ARRSIZE-1 ; i++)
    1152     {
    1153         min = i;
    1154         for(j = i+1 ; j < ARRSIZE ; j++)
    1155         {
    1156             if(arr[j] < arr[min])      //正序排序
    1157                 min = j;
    1158         }
    1159         if(min != i)
    1160         {
    1161             temp = arr[min];
    1162             arr[min] = arr[i];
    1163             arr[i] = temp;
    1164         }
    1165     }
    1166     for(i = 0 ; i < ARRSIZE ; i++)
    1167         printf("%d	",arr[i]);
    1168 }
    1169 
    1170 int main(void)
    1171 {
    1172     int arr[ARRSIZE] = {0};
    1173     int i;
    1174     for(i = 0 ; i < ARRSIZE ; i++)
    1175         scanf("%d",&arr[i]);
    1176     ChoiceSort(arr);
    1177     return 0;
    1178 }
    1179 
    1180 /**********************************************************************************************************************/
    1181 
    1182 /**********************************************************************************************************************/
    1183 2、冒泡排序:
    1184     #include <stdio.h>
    1185 #define ARRSIZE 5
    1186 
    1187 void BubbleSort(int *arr)
    1188 {
    1189     int i,j,temp,flag;
    1190     for(i = 1 ; i < ARRSIZE ; i++)
    1191     {
    1192         flag = 0;
    1193         for(j = 0 ; j < ARRSIZE-i ; j++)
    1194         {
    1195             if(arr[j] > arr[j+1])
    1196             {
    1197                 temp = arr[j];
    1198                 arr[j] = arr[j+1];
    1199                 arr[j+1] = temp;
    1200                 flag = 1;
    1201             }
    1202         }
    1203         if(!flag)
    1204             break;
    1205     }
    1206     for(i = 0 ; i < ARRSIZE ; i++)
    1207         printf("%d	",arr[i]);
    1208     printf("
    ");
    1209 }
    1210 
    1211 int main(void)
    1212 {
    1213     int i;
    1214     int arr[ARRSIZE] = {0};
    1215     for(i = 0 ; i < ARRSIZE ; i++)
    1216         scanf("%d",&arr[i]);
    1217     BubbleSort(arr);
    1218     return 0;
    1219 }
    1220 
    1221 /**********************************************************************************************************************/
    1222 
    1223 /**********************************************************************************************************************/
    1224 3、快速排序:
    1225 #include <stdio.h>
    1226 
    1227 void QuickSort(int *arr,int num)
    1228 {
    1229     int i = 0;
    1230     int j = num - 1;
    1231     int x = arr[0];
    1232     if(num > 1)
    1233     {
    1234         while( i != j)
    1235         {
    1236             for(; i < j ; j--)
    1237             {
    1238                 if(arr[j] < x)
    1239                 {
    1240 
    1241                     printf("j-- a[i] = %d,arr[j] = %d
    ",arr[i],arr[j]);
    1242                     arr[i] = arr[j];
    1243                     break;
    1244                 }
    1245             }
    1246             for(; i < j ; i++)
    1247             {
    1248                 if(arr[i] > x)
    1249                 {
    1250                     printf("i++ a[j] = %d,arr[i] = %d
    ",arr[j],arr[i]);
    1251                     arr[j] = arr[i];
    1252                     break;
    1253                 }
    1254             }
    1255             arr[i] = x;
    1256         }
    1257         QuickSort(arr,i);
    1258         QuickSort(arr + i + 1 , num - i - 1); 
    1259     }
    1260 }
    1261 
    1262 int main(void)
    1263 {
    1264     int i;
    1265     int arr[7] = {49,38,65,97,76,13,27};
    1266     printf("old:");
    1267     for(i = 0 ; i < 7 ; i++)
    1268     {
    1269         printf("%d	",arr[i]);
    1270     }
    1271     printf("
    ");
    1272     QuickSort(arr,7);
    1273 #if 1
    1274     printf("new:");
    1275     for(i = 0 ; i < 7 ; i++)
    1276     {
    1277         printf("%d	",arr[i]);
    1278     }
    1279 #endif
    1280     printf("
    ");
    1281     return 0;
    1282 }
    1283 
    1284 /**********************************************************************************************************************/
    1285 
    1286 /**********************************************************************************************************************/
    1287 4、希尔排序:
    1288 
    1289 /**********************************************************************************************************************/
    1290 
    1291 /**********************************************************************************************************************/
    1292 
    1293 5、桶排序:
    1294 /**********************************************************************************************************************/
    1295 
    1296 /**********************************************************************************************************************/
    1297 6、计数排序:
    1298 /**********************************************************************************************************************/
    1299 
    1300 /**********************************************************************************************************************/
    1301 7、插入排序:
    1302 /**********************************************************************************************************************/
    1303 
    1304 /**********************************************************************************************************************/
    1305 
    1306 8、归并排序:
    1307 /**********************************************************************************************************************/
    1308 
    1309 /**********************************************************************************************************************/
    1310 
    1311 9、堆排序:
    1312 /**********************************************************************************************************************/
    1313 
    1314 /**********************************************************************************************************************/
    1315 
    1316 10、基数排序:
    1317 /**********************************************************************************************************************/
    1318 
    1319 /**********************************************************************************************************************/
    1320 
    1321 strlen的C语言实现:
    1322 #include <stdio.h>
    1323 
    1324 int my_strlen(char *dat)
    1325 {
    1326     int n = 0;
    1327     while(*dat != '')
    1328     {
    1329         n++;
    1330         dat++;
    1331     }
    1332     return n;
    1333 }
    1334 
    1335 int main(void)
    1336 {
    1337     char arr[] = "123456";
    1338     int num = 0;
    1339     num = my_strlen(arr);
    1340     printf("num = %d
    ",num);
    1341     return 0;
    1342 }    
    1343 /**********************************************************************************************************************/
    1344 
    1345 /**********************************************************************************************************************/
    1346 strcpy的C语言实现:
    1347 #include <stdio.h>
    1348 
    1349 char* strcpy_0(char *dest , const char *src)
    1350 {
    1351     char *address = dest;
    1352     while((*dest++ = *src++) != '');
    1353     return address;
    1354 }
    1355 
    1356 int main(void)
    1357 {
    1358     char arr0[7] = "123456";
    1359     char arr1[7] = "123345";
    1360     int i;
    1361     char *ch;
    1362     ch = strcpy_0(arr0 , arr1);
    1363     for(i = 0 ; i < 6 ; i++)
    1364         printf("%c",arr0[i]);
    1365     printf("
    ");
    1366 }
    1367 /**********************************************************************************************************************/
    1368 
    1369 /**********************************************************************************************************************/
    1370 strcat的C语言实现:
    1371 #include <stdio.h>
    1372 
    1373 char* strcat_0(char *dest ,const char *src)
    1374 {
    1375     char *address = dest;
    1376     while(*dest != '')
    1377         dest++;
    1378     while(*dest++ = *src++);
    1379     return address;
    1380 }
    1381 
    1382 int main(void)
    1383 {
    1384     char arr0[10] = "123";
    1385     char arr1[] = "78";
    1386     char *ch = NULL;
    1387     ch = strcat_0(arr0,arr1);
    1388     printf("%s
    ",ch);
    1389     printf("%d
    ",sizeof(arr0));
    1390 
    1391     return 0;
    1392 }
    1393 /**********************************************************************************************************************/
    1394 
    1395 /**********************************************************************************************************************/
    1396 strcmp的C语言实现:
    1397 #include <stdio.h>
    1398 
    1399 int strcmp_0(char *str1 , char *str2)
    1400 {
    1401     while(*str1 == *str2)
    1402     {
    1403         if(*str1 == '')
    1404             return 0;
    1405         str1++;
    1406         str2++;
    1407     }
    1408     return *str1 - *str2;
    1409 }
    1410 
    1411 int main(void)
    1412 {
    1413     int num = strcmp_0("123432432","123");
    1414     printf("%d
    ",num);
    1415 
    1416     return 0;
    1417 }
    1418 /**********************************************************************************************************************/
    1419 
    1420 /**********************************************************************************************************************/
  • 相关阅读:
    JS中关于clientWidth offsetWidth scrollWidth 等的含义
    javascript中数组concat()join()split()
    我的大数据学习路线(持续更新)
    java多线程-学习笔记
    java多线程-线程交互&互斥&同步
    java多线程-关键人物程咬金
    java多线程-军队战争
    java多线程-两个演员线程
    pytorch-Flatten操作
    龙良曲pytorch学习笔记_迁移学习
  • 原文地址:https://www.cnblogs.com/jiayezi/p/13281305.html
Copyright © 2020-2023  润新知