zoukankan      html  css  js  c++  java
  • 第一本C语言笔记(下)

    11. 数组

    (1)数组初始化时,如果初始化数字个数超过存储区个数,就忽略多余数字。如果初始化数字个数少于存储区个数,则后面的存储区自动被初始化为0。

    (2)数组名称可以代表数组里第一个存储区的地址。可以对数组名称进行sizeof计算,结果是数组所包含的总字节数。

    (3)变长数组:C99规范允许声明数组时使用变量表示数组里包含的存储区个数。但是变长数组不能初始化。

    (4)一维、二维少数族名称不可被赋值。对二维数组名进行sizeof计算可以得到数组里面所有存储区的总大小。可以在二维数组名称后加一个下标,这个写法中的下标作为组下标使用。它表示组下标对应组中第一个存储区的地址。

    int arr[3][4];     //arr[1] 表示二维数组中 arr[1][0]的地址, 这个写法有时候可以代表一组中的所有存储区(看做一个一维数组)
    

    (5)产生7个1~36之间的整数

    #include <stdio.h>
    void Product7Numbers(int arr[7])
    {
            int count = 0, i = 0;
            srand(time(0));
        	//改循环中,负责产生随机数
            do{
            arr[count] = rand()%36 + 1;
            //该循环中,用于检查新产生的随机数是否和之前产生的所有随机数重复
            for(i=0; i<=count-1; ++i)
            {
                    if(arr[count] == arr[i])
                            break;
            }
            //如果新产生的随机数和之前的所有随机数都不重复,则count加1,表示成功产生一个随机数
            if(i == count)
            {
                    count++;
            }
            }while(count<7);
    }
    void PrintNumbers(int arr[7])
    {
            int i = 0;
            for(i=0; i<7; i++)
            {
                    printf("arr[%d]: %d
    ",i, arr[i]);
            }
    }
    int main()
    {
            int arr[7] = {0};
            PrintNumbers(arr);
            Product7Numbers(arr);
            PrintNumbers(arr);
            return 0;
    }
    
    [root@localhost 0701]# vim RandomNum.c
    [root@localhost 0701]# gcc RandomNum.c
    [root@localhost 0701]# ./a.out
    arr[0]: 0
    arr[1]: 0
    arr[2]: 0
    arr[3]: 0
    arr[4]: 0
    arr[5]: 0
    arr[6]: 0
    arr[0]: 17
    arr[1]: 1
    arr[2]: 2
    arr[3]: 8
    arr[4]: 24
    arr[5]: 6
    arr[6]: 31
    
    12. 函数

    (1)多函数程序执行的模式如下:

    a. 整个程序的执行时间被话费成多个端,每个段分给一个函数使用;
    b. 任何两个时间段不能相互重叠,并且所用时间段必须连续;
    c. 如果函数A把自己的时间分给函数B使用,则函数B结束后必须把后面的时间还给函数A
    

    (2)变量不可以跨函数使用,不用函数内部的变量可以重名。

    (3)如果函数多次执行,则每次变量对应的存储区可能不同,volative 声明变量的存储区可以在多个程序中使用。

    (4)用于实现数据传递的存储区必须由调用函数提供。

    (5)调用函数只有一次机会获得返回值,得到后必须立刻使用或者转存到某个存储区里。

    (6)函数名前什么都不写,表示函数有一个整数类型的返回值。(C99中不支持)

    (7)只要能当数字使用的内容都可以作为实际参数。

    (8)如果小括号里什么都不写,表示函数形参的个数和类型是任意的。

    (9)数组是可以作为形参使用的。

    (10)使用数组形参可以实现双向数据传递,这种参数叫做输入输出参数。

    (11)数组形参声明中可以省略存储区个数。

    12. 静态变量

    (1)静态变量初始化只在程序开始的时候执行一次。

    (2)可以跨函数使用静态变量的存储区。

    (3)静态全局变量的作用域只包含声明它的文件内部的所有语句,其生命周期为程序执行期间。

    13. 指针

    (1)指针变量可以用来记录地址数据,也可以根据记录的地址数据找到来源的存储区。

    (2)指针和存储区的捆绑关系在程序执行过程中可能不断变化。

    (3)可以把指针看做变量的某种身份,可以使用指针实现针对身份的编程。

    #include <stdio.h>
    void Sort3Numbers(int* p_a, int* p_b, int* p_c)
    {
            if(NULL == p_a |NULL == p_b | NULL == p_c)
            {
                    return;
            }
            int t = 0;
            if(*p_c < *p_b)
            {
                    t = *p_c; *p_c = *p_b; *p_b = t;
            }
            if(*p_b < *p_a)
            {
                    t = *p_b; *p_b = *p_a; *p_a = t;
            }
            printf("%d %d %d
    ", *p_a, *p_b, *p_c);
    }
    int main()
    {
            int a=3, b=20, c=1;
            Sort3Numbers(&a, &b, &c);
            return 0;
    }
    
    [root@localhost 0701]# vim Sort3Numbers.c
    [root@localhost 0701]# gcc Sort3Numbers.c
    [root@localhost 0701]# ./a.out
    1 3 20
    
    #include <stdio.h>
    
    void Sort3Numbers1(int** pp_a, int** pp_b, int** pp_c)
    {
            if(NULL == pp_a |NULL == pp_b | NULL == pp_c)
            {
                    return;
            }
            int* p_t = NULL;
            if(**pp_c < **pp_b)
            {
                    p_t = *pp_c; *pp_c = *pp_b; *pp_b = p_t;
            }
            if(**pp_b < **pp_a)
            {
                    p_t = *pp_b; *pp_b = *pp_a; *pp_a = p_t;
            }
            printf("%d %d %d
    ", **pp_a, **pp_b, **pp_c);
    }
    int main()
    {
            int a=3, b=20, c=1;
            int *p_a = &a;
            int *p_b = &b;
            int *p_c = &c;
            Sort3Numbers1(&p_a, &p_b, &p_c);
            printf("the mininum is %d
    ", *p_a);
            return 0;
    }
    
    [root@localhost 0701]# vim Sort3Numbers1.c
    [root@localhost 0701]# gcc Sort3Numbers1.c
    [root@localhost 0701]# ./a.out
    1 3 20
    the mininum is 1
    
    13. 函数声明

    (1)C语言中,函数形参的个数可以不固定,这种参数叫做变长参数(例如 printf 和 scanf )。

    (2)函数的隐式声明:如果编译器首先编译函数调用的语句,就会猜测函数的格式,猜测结果里认为函数有一个整数类型存储区记录返回值,有任意多个不确定类型的形参。

    (3)隐式声明中形式参数的类型只能是整数类型或者双精度浮点类型。

    (4)如果隐式声明的格式和函数真实的格式不一致则编译时会出错。

    14. 递归函数

    (1)C语言中一个函数可以调用自己,这种函数叫递归函数。如果一个问题可以拆分成 n 个小问题,其中至少有一个小问题和原来的问题本质是一样的,这种问题就可以采用递归函数解决。

    (2)递归函数编写步骤

    a. 编写语句描述问题的分解方式(假设递归函数已经完成)
    b. 在递归函数的开头编写分支处理无法分解的情况(这个分支必须保证函数可以结束)
    

    (3)采用递归函数解决问题的思路叫递归,采用循环解决同样问题的思路叫递推。

    (4)没有初始化的全局变量自耦东被初始化为 0,没有初始化的静态变量被自动初始化为 0。

    (5)递归算1+2+3+...+n = ?

    #include <stdio.h>
    int RecursionAdd(int num)
    {
            if(num == 1)
            {
                    return 1;
            }
            return RecursionAdd(num-1) + num;
    }
    int main(int argc, char** argv)
    {
            int num = atoi(*(argv+1));
            printf("Sun is %d
    ", RecursionAdd(num));
            return 0;
    }
    
    [root@localhost 0701]# vim RecursionAdd.c
    [root@localhost 0701]# gcc RecursionAdd.c
    [root@localhost 0701]# ./a.out 2
    Sun is 3
    
    15. 指针与字符串

    (1)地址数据只能参与如下计算过程:

    a. 地址 +/- 整数    实际上加减的是 n 个捆绑存储区的大小
    b. 地址 - 地址      结果是一个整数,这个整数表示两个地址之间包含的捆绑存储区个数
    

    (2)计算机里对数组下标的处理:

    arr[num]    ------>      *(arr+num)
    

    (3)所有跨函数使用存储区都必须通过指针实现。

    (4)函数可以把一个存储区里的地址作为返回值使用,这个时候它需要提供一个指针类型的存储区记录这个返回值。

    (5)不可以把非静态局部变量的地址作为返回值使用。

    (6)前置 const 和后置 const

    const int* p_num;    //表示 p_num 所指的存储区中的数字不能修改 
    int const *p_num1;	 //表示 p_num1 这个指针的值不能修改	
    

    (7)无类型指针通常作为函数的形式 参数使用,可以通过它把任意类型的存储区传递给被调用函数。

    (8)字符串 —— C语言中所有的文字信息必须记录在一组连续的字符类型存储区里,所有文件信息必须以 ''(ASCII是整数0)字符作为结尾。

    (9)所有的字符串都可以采用一个字符类型指针表示。(一般使用字符串字面值或字符数组表示)

    (10)编译器在编译时会自动在字符串字面值的末尾增加一个 '' 字符。编译器会把字符串字面值替换成第一个字符所在存储区的地址。(程序中内容一样的字符串字面值只有 1 个,多个并列的字符串会被合并成一个)。

    (11)只有包含 '' 的字符数组才能当做字符串使用。

    (12)可以使用字符串字面值对字符数组进行初始化,这个时候 '' 字符也会被初始化到字符数组里。

    (13)可以使用 %s 作占位符把字符串内容显示在屏幕上。

    (14)将一个字符颠倒

    #include <stdio.h>
    #include <string.h>
    char* ReversalCharString(char* p_char, int size)
    {
            char* p_start = p_char, *p_end = p_char + size - 1;
            char temp = 0;
            while(p_start < p_end)
            {
                    temp = *p_start;
                    *p_start = *p_end;
                    *p_end = temp;
                    p_start++;
                    p_end--;
            }
            return p_char;
    }
    int main(int argc, char** argv)
    {
            char* p_test = *(argv+1);
            ReversalCharString(p_test, strlen(p_test));
            printf("%s
    ", p_test);
            return 0;
    }
    
    [root@localhost 0701]# vim ReversalCharString.c
    [root@localhost 0701]# gcc ReversalCharString.c
    [root@localhost 0701]# ./a.out  ILoveYou
    uoYevoLI
    

    (15)指针数组里每个存储区都是一个指针类型的存储区,字符指针数组包含多个字符指针,每个字符指针可以代表一个字符串。所以可以采用字符指针数组带保镖多个相关字符串。例如 int main(int argc, char* argv[])。

    16. 字符串相关处理

    (1)处理字符串的函数

    strlen		统计有效字符个数(''之前的字符),和sizeof不同。
    strcat		合并两个字符串(把参数2中的内容追加到参数1的数组中),如果参数1所指的存储区不够大,则会造成严重的错误
    strncat		功能和 strcat 一样,它比 strcat 多了一个整数类型参数表示数组里空余存储区的个数
    strcmp		比较两个字符串的大小(长度),根据 ASCII 码比较,ASCII 码大则这个字符大。
    			返回值: 1  —— 前一个字符串大 
    				   -1  —— 后一个字符串大
    				   	0  —— 相等
    strncmp		只比较前 n 个字符
    strcpy		把一个字符串复制到字符串数组里(替换原有内容)
    memset		把字符数组里多个存储区填充成同一个字符,无返回值。
    strstr		在大字符串里查找小字符串的位置,返回值为找到字符串的指针,找不到的时候返回值是NULL。
    

    (2)字符串输入输出函数(#include<stdio.h>)

    sprintf			把数字按照格式放在字符数组里形成字符串
    例如: sprintf(str, "%d %c %g
    ", num, ch, fnum);	//把num、ch、fnum 按照 "%d %c %g
    "的geshi8输出到字符数组 str 中。
    sscanf			从字符串里获得数字并记录在缓存区里。
    例如: sscanf(str, "%d %c %g", &num, %ch, &fnum); 	//把str字符串中的数字按 "%d %c %g"格式分别保存到 num、ch、fnum 中。
    

    (3)字符串转换成数字

    atoi		将字符串中不带小数点的数字字符 转换成 整数类型
    atof		将字符串中带小数点的数字字符	 转换成 双精度浮点型
    
    17. 宏与程序编译

    (1)编译器会在预编译阶段把程序中所有宏名称替换成它所代表的数字。

    (2)可以在编译命令中使用 -D 选项指定宏名称所代表的的数字。

    gcc -DPI = 3.14f   01macro.c    //只有在编译时才知道的数字就应该用宏表示
    

    (3)宏不可以使用自己的存储区和函数进行数据传递。宏也没有形式参数和返回值。

    (4)能到数字使用的宏必须写成表达式

    #define  ABS(n)   n >= 0 ? n | 0 - n   //宏的参数可以直接代表函数的存储区
    

    (5)宏没有形式参数,所以不能保证优先计算参数内部的操作符。宏里面所有能当数字使用的参数都应该写在小括号里。

    (6)条件编译可以让编译器在编译时从 n 组语句中选择一组编译而或略其他组。

    #ifdef / #ifndef   ...    #else    ...    #endif
    #if   ...  #elif(任意多次)  ...  #else ...   #endif
    

    (7)多文件编译步骤

    a. 把所有函数分散在多个不同的源文件里(主函数通常单独占一个文件夹)。 
    b. 为每个源文件编写配对的头文件(主函数所在的源文件不需要头文件)。 
    	- 所有不分配内存的内容都可以写在头文件里
    	- 头文件里至少应该包含配对源文件里所有函数的声明
    c. 在每个源文件里使用 #include 预处理指令包含所有必要的头文件
    	- 配对头文件是必需头文件
    	- 如果源文件里使用了头文件里声明的函数,则这个头文件也是必要头文件
    

    (8)头文件中采用的宏名称应该根据文件路径变化得到,例如 _ADD_H_。

    (9)如果希望在一个源文件里使用另一个源文件里声明的全局变量,就需要使用 extern 关键字再次声明这个全局变量。

    ​ - 使用 extern 关键字声明变量的语句不会分配内存,所以通常放在头文件里。

    (10)不可以在一个源文件里使用另一个源文件中的静态全局变量

    18. 结构体

    (1)结构体声明中包含的变量声明语句不会分配内存。

    (2)结构体变量会分配存储区,它可以用来记录数字。

    (3)结构体中,在捆绑过的结构体指针后使用 -> 再加上某个子存储区名称,这个写法可以表示那个子存储区。

    p_Person->age 		等价于 	(*p_person).age
    

    (4)我们的计算机中,所有的指针都占 4 个字节。

    19. 枚举(enum)和联合(union)

    (1)计算机把从 0 开始的一组连续的整数分配给枚举类型中的所有名称。

    (2)声明枚举时,可以指定某个名称代表的整数,后面的整数也会发生变化。

    (3)联合存储区可以当做多种不同类型存储区使用,联合的每个子存储区代表联合存储区一种可写的类型。

    (4)联合的所有子存储区开始地址一样,他们所占的位置互相重叠。

    (5)联合存储区的大小是最大子存储区的大小

    20. 函数指针

    (1)二级指针通常作为函数的形式参数使用,这个时候可以让被调用函数使用调用函数的一级指针存储区。

    (2)C 语言里,函数也有地址,函数名称可以代表函数的地址。

    (3)函数指针可以用来记录函数的地址,函数指针也需要先声明,然后才能使用

    ​ - 函数指针声明可以根据函数声明变化得到

    ​ - 例如: int add(int, int) --> int (*p_func)(int, int)

    (4)函数指针也分类型,不同类型的函数指针适合于不同类型的函数捆绑。

    (5)函数指针一旦和函数捆绑,就可以用来调用那个函数。

    (6)函数指针可以用作函数的形式参数,会作为实际参数使用的函数叫做回调函数。

    21. 动态内存分配

    (1)malloc 函数可以动态分配一组连续的字节

    ​ 它的返回值表示分配好的第一个字节的地址

    ​ 如果分配失败则返回值为 NULL

    ​ malloc 函数用一个无类型指针存储区记录返回值,需要首先强制类型转换成有类型指针然后才可以使用

    ​ 动态分配内存使用完成后必须释放 free(p_num);

    ​ 一个函数动态分配内存可以给任何其他函数使用(即延长了动态内存的生命周期)。

    22. 文件操作

    (1)所有 文件都采用二进制方式记录数字

    ​ 如果文件里的所有二进制内容都对应字符,则这种文件叫文本文件

    ​ 除了文本文件以外的所有文件叫做二进制文件

    ​ 文本文件可以当做二进制文件使用

    (2)文件操作的基本步骤

    a. 打开文件 fopen("a.txt", "w");
    b. 操作文件    fread/fwrite   (以二进制方式操作)
    c. 关闭文件 fclose(p_file);
    

    (3) fopen 函数

    fopen 函数有两个参数,第一个为文件名字符串,第二个为打开文件方式标识
    打开文件的方式:
    "r"		只能查看文件内容,只能从文件头开始查看。如果文件不存在,打开会失败
    "r+"	比"r"多了修改功能
    "w"		只能修改文件内容不能查看,只能从文件头开始修改,如果文件不存在就创建文件,如果已存在就删除文件内容
    "w+"	比"w"多了查看功能
    "a"		只能修改文件内容不能查看,在文件原有内容后面追加新内容,如果我呢间不存在就创建文件,如果文件存在不许修改文件原有内容
    "a+"	比"a"多了查看功能
    "b"		也是一种文件打开方式,它可以和上面任何一种打开方式混用,这个打开方式表示程序中只能采用二进制方式操作文件
    

    (4)fopen 函数的返回值是一个地址,这个地址应该记录在文件指针里。

    ​ 程序中只能使用文件指针代表打开的文件

    ​ fopen 函数如果打开文件失败则返回值为 NULL

    (5)一旦完成对文件的操作之后就必须使用 fclose 函数关闭文件。

    ​ fclose 函数需要文件指针作为参数

    ​ 关闭文件后文件指针成为野指针,必须恢复成空指针

    (6)文件操作分两种:

    ​ a. 把内存中一组连续存储区的内容拷贝到文件里(写文件)

    ​ b. 把文件中一组连续字节的内容拷贝到内存里(读文件)

    (7)fread 函数采用二进制方式读文件内容,fwrite 函数采用二进制方式写文件内容。这两个函数都需要四个参数

    第一个参数: 内存中第一个存储区的地址
    第二个参数: 内存中单个存储区的大小
    第三个参数: 希望操作的存储区个数
    第四个参数: 文件指针
    

    这两个函数的返回值都表示实际操作的存储区个数。

    (8)fprintf 函数可以把数据按照格式记录在文本文件里。这个函数的参数就是在 printf 函数参数的前面加一个文件指针。

    (9)fscanf 函数可以按照格式从文本文件里落去数字并记录在存储区里。该函数的参数就是在 scanf 函数参数的前面增加一个文件指针。

    (10)ftell 函数可以得到位置指针的数值。

    (11)计算机里为每个打开的文件保留一个整数,这个整数表示下一次读写操作的开始位置。这个位置一定在两个相邻的字节之间。这个正式表示文件头开始到这个位置之间包含的字节个数。这个整数叫做文件的位置指针

    (12)每当从文件中获得 N 个字节或者向文件里写入 N 个字节后,位置指针向后移动 N 个位置。

    (13)rewind 函数可以把位置指针设置成文件开头。

    (14)fseek 函数可以把位置指针设置成文件里的任何位置。如果目标位置在基准位置后则用非负数表示,否则用负数表示。

    (15)示例:以结构体为单位读写文件

    [root@rockman 0706]# cat filewrite.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    typedef struct{
            int id;
            float salary;
            char name[10];
    }Person;
    void filewrite(char filepath[16])
    {
            FILE* pfile = fopen(filepath, "ab");
            int i=0, cnt=0;
            Person ps;
            if(pfile)
            {
                    for(i=0; i<2; i++)
                    {
                            printf("Please input your ID:");
                            scanf("%d", &ps.id);
                            printf("Please input your name:");
                            scanf("%s", &ps.name);
                            printf("Please input your salary:");
                            scanf("%f", &ps.salary);
                            cnt = fwrite(&ps, sizeof(ps), 1, pfile);
                            if(cnt)
                            {
                                    printf("the record was writen successfully!
    ");
                            }
                            else{
                                    printf("fwrite error!
    ");
                            }
                    }
            }
            fclose(pfile);
            pfile = NULL;
    }
    int main()
    {
            filewrite("person.bin");
            return 0;
    }
    
    [root@rockman 0706]# ./write.out
    Please input your ID:1
    Please input your name:rock
    Please input your salary:1111
    the record was writen successfully!
    Please input your ID:2
    Please input your name:mary
    Please input your salary:2222
    the record was writen successfully!
    
    [root@rockman 0706]# cat fileread.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    typedef struct{
            int id;
            float salary;
            char name[10];
    }Person;
    void fileread(char filepath[16])
    {
            FILE* pfile = fopen(filepath, "rb");
            int cnt=0;
            Person ps;
            if(pfile)
            {
                    while(1){
                            cnt = fread(&ps, sizeof(ps), 1, pfile);
                            //printf("cnt:%d
    ", cnt);
                            if(cnt)
                            {
                                    printf("ID:%d, name:%s, salary:%f
    ",ps.id, ps.name, ps.salary);
                            }
                            else{
                                    break;
                            }
                    }
            }
            fclose(pfile);
            pfile = NULL;
    }
    int main()
    {
            fileread("person.bin");
            return 0;
    }
    
    [root@rockman 0706]# ./read.out
    ID:1, name:rock, salary:1111.000000
    ID:2, name:mary, salary:2222.000000
    

    (16)编程实现文件拷贝功能,类似 cp 命令

    [root@rockman 0706]# cat mycp.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    void mycp(char* psrc, char* pdest)
    {
            char buf[100] = {0};
            int size = 0;
            FILE* pfsrc = NULL, *pfdest = NULL;
            pfsrc = fopen(psrc, "rb");
            if(!pfsrc)
            {
                    printf("resouse file open error!
    ");
                    fclose(pfsrc);
                    pfsrc = NULL;
                    return;
            }
            pfdest = fopen(pdest, "wb");
            if(!pfdest)
            {
                    printf("destination file open error!
    ");
                    fclose(pfdest);
                    pfdest = NULL;
                    return;
            }
            while(1)
            {
                    size = fread(buf, sizeof(char), 100, pfsrc);
                    if(!size)
                    {
                            break;
                    }
                    fwrite(buf, sizeof(char), size, pfdest);
            }
            fclose(pfdest);
            pfdest = NULL;
            fclose(pfsrc);
            pfsrc = NULL;
            return;
    }
    int main(int argc, char** argv)
    {
            mycp(*(argv+1), *(argv+2));
            return 0;
    }
    
    [root@rockman 0706]# gcc mycp.c -o cp.out
    [root@rockman 0706]# ./cp.out person.bin person_copy.bin
    [root@rockman 0706]# ls -l per*
    -rw-r--r--. 1 root root 40 Jul  6 10:45 person.bin
    -rw-r--r--. 1 root root 40 Jul  6 11:08 person_copy.bin
    
    道虽迩,不行不至;事虽小,不为不成。
  • 相关阅读:
    STS 创建 Maven 项目填坑
    JeeSite | 访问控制权限
    《Spring + MyBatis 企业应用实战》书评
    MyBatis-Generator 用法介绍
    Java描述数据结构之链表的增删改查
    Java中的Object类的几个方法
    设计模式之策略模式(Strategy Pattern)
    设计模式之模板方法(Template Method)
    JeeSite | 数据分页与翻页
    day 19
  • 原文地址:https://www.cnblogs.com/rock-cc/p/9269808.html
Copyright © 2011-2022 走看看