精华内容
下载资源
问答
  • C语言重点知识归纳
    2019-06-01 23:59:25

    三种循环结构:
    a)for() ; while(); do- while()三种。
    b)for循环当中必须是两个分号,千万不要忘记。
    c)写程序的时候一定要注意,循环一定要有结束的条件,否则成了死循环。
    d) do-while()循环的最后一个while();的分号一定不能够丢。(当心上机改错),do-while循环是至少执行一次循环。
    2) break 和 continue的差别

    break是打破的意思,所以看见break就退出整个一层循环
    cotinue是继续的意思,但是要结束本次循环,就是循环体内的剩下的语句不再执行,跳到循环开始,然后判断循环条件,进行新一轮的循环

    嵌套语句
    就是有循环里面还有循环,这种比较复杂,要一层一层耐心的计算,一般记住两层是处理二维数组的。
    while(c=getchar)

    未完待续(没网了。。。)

    更多相关内容
  • C语言——知识点汇总

    千次阅读 2021-10-08 15:59:02
    C语言的基本数据类型包括字符型、整数型、浮点型。 2.常量 常量或常数,表示固定不变的数据,是具体的数据。 1)字符常量,如'6','a','F',不能是中文,例如'女',因为一个汉字和全角的符号占两个字节(GBK编码)...

    1.基本数据类型

    C语言的基本数据类型包括**字符型、整数型、浮点型**。
    

    2.常量

    常量或常数,表示固定不变的数据,是具体的数据。
    
    1)字符常量,如'6''a''F',不能是中文,例如'女',因为一个汉字和全角的符号占两个字节(GBK编码)。
    
    2)整型常量,如6,27,-299。
    
    3)浮点型常量,如5.43,-2.3,5.67,6.0。
    
    4)字符串常量,如"625""女""www.freecplus.net""西施"

    3.变量

     字符串变量
     
    		在C语言中,**没有“字符串”这个数据类型**,而是用字符数组来存放字符串,并提供了丰富的库函数来操作字符串。
    
     char name[21];   // 定义一个可以存放20字符的字符串。
     
     注意几个细节:
     
    	1)如果要定义一个存放20个英文的字符串,数组的长度应该是20+1,原因以后再讨论。
    	2)中文的汉字和标点符号需要两个字符宽度来存放(GBK编码)。
    	例如name[21]可以存放20个英文字符,或10个中文字符。
    	3)字符串不是C语言的基本数据类型,不能用“=”赋值,不能用“>”和“<”比较大小,不能用“+”拼接,不能用==!=判断两个字符串是否相同,要用函数,具体方法我以后再介绍,现在了解就可以。
    

    - 变量的命名

    变量名属于标识符,需要符合标识符的命名规范,具体如下:
    
    1)变量名的第一个字符必须是字母或下划线,不能是数字和其它字符。
    
    2) 变量名中的字母是区分大小写的。比如a和A是不同的变量名,num和Num也是不同的变量名。
    
    3)变量名绝对不可以是C语言的关键字。
    
    4)关于变量的命名,为了便于理解,尽可能用英文单词或多个英文单词的简写,太短不能表达含义,太长了书写麻烦,如果英语不好,那就用中文拼英的第一个字母,例如身份证号码,cardid、userid都可以,sfzhm也行,不要怕被笑话,英语不好的程序员很多。
    

    - 变量的初始化

    变量在定义后,操作系统为它分配了一块内存,但并不会把这块内存打扫干静,也就是说内存中可能有垃圾数据,建议在使用之间对其初始化(打扫干静)。

    变量初始化是一个良好的编程习惯,否则有时候程序可能会产生意想不到的结果。
    1、整数型、字符型、浮点型变量初始化
    对整数型、字符型、浮点型变量来说,初始化就是给它们赋0值。

    可以在定义的时候立即初始化。

      int   ii=0;   // 定义整数型变量并初始化
      char  cc=0;  // 定义字符型变量并初始化
      double money=0;   // 定义浮点型变量并初始化
    
    

    也可以先定义,然后再初始化。

      int   ii;  // 定义整数型变量
      char  cc;  // 定义字符型变量
      double money;   // 定义浮点型变量
      ii=0;  // 初始化ii为0
      cc=0;  // 初始化cc为0
      money=0;  // 初始化money为0
    

    2、字符串变量的初始化
    对字符串变量来说,初始化就是把内容清空,本质上也是赋0值。

      char name[21];  // 定义一个可以存放20字符的字符串
      memset(name,0,sizeof(name));   // 清空字符串name中的内容
    

    - 变量的赋值

      strcpy(name1,name);   // 把name的值赋给name1
    

    注意了,字符串变量的赋值与其它类型不同,不能用=号,要用strcpy函数

    - const约束

     const  double  pi = 3.1415926;
    

    用const定义的变量的值是不允许改变的,不允许给它重新赋值,即使是赋相同的值也不可以。所以说它定义的是只读变量。这也就意味着必须在定义的时候就给它赋初值,如果程序中试图改变它的值,编译的时候就会报错。

    4.数据输入

    在C语言中,有三个函数可以从键盘获得用户输入。

    getchar:输入单个字符,保存到字符变量中。

    gets:输入一行数据,保存到字符串变量中。

    scanf:格式化输入函数,一次可以输入多个数据,保存到多个变量中。

    - 详解scanf,其他忽略:
    scanf函数是格式化输入函数,用于接受从键盘输入的数据,用户输入数据完成后,按回车键(Enter)结束输入。
    输入整数的格式用%d表示
    输入字符串的格式用%s表示
    输入字符的格式用%c表示
    输入浮点数的格式用%lf表示
    输入字符串:

     char name[21];
      memset(name,0,sizeof(name));
      printf("请输入您姓名:"); 
      scanf("%s",name);     // 注意了,字符串变量名前可以不加符号&,不要问原因,以后再介绍。
    

    5.数据输出

    在C语言中,有三个函数可以把数据输出到屏幕。

    putchar:输出单个字符。

    puts:输出字符串。

    printf:格式化输出函数,可输出常量、变量等。

    - 详解printf输出,其他忽略:
    printf函数是格式化输出函数, 用于向屏幕输出数据。
    调用一次printf函数可以输出多个常量或变量。

    int age=18;
      char xb='x';
      double weight=62.5;
      char name[21];
      memset(name,0,sizeof(name));
      strcpy(name, "西施");
      printf("我的姓名是:%s,姓别:%c,年龄:%d岁,体重%lf公斤。\n",name,xb,age,weight);
    

    注意,printf函数第一个参数(格式化字符串)的格式与后面的参数列表(常量或变量的列表)要一一对应,一个萝卜一个坑的填进去,不能多,不能少,顺序也不能错,否则会产生意外的结果。

    6.if语句

    如果if后只有一行语句,可以省略大括号。多行不能省略。

    C语言用“&&”表示“并且”,用“||”表示“或者”,官方用语就是逻辑运算符。

    7.循环

    • for
     for (语句1;表达式;语句2)
      {
        语句块
      }
    

    在for循环中,语句1、表达式和语句2都可以为空(此时为死循环),for (;;)等同于while (1)。

    8.数组

    数组(array)是一组数据类型相同的变量,可以存放一组数据,它定义的语法是:

     数据类型 数组名[数组长度];
     
     例如:
      double money[20];
      
    定义数组的时候,数组的长度必须是整数,可以是常量,也可以是变量。
    
    数据的下标也必须是整数,可以是常量,也可以是变量。
    
    使用数组元素和使用同类型的变量一样。
    
    scanf("%lf", &money[4]);   // 把一个值读入数组的第5个元素
    

    数组是有多个变量组成,占用内存总空间的大小为多个变量占用的内存空间之和,用sizeof(数组名)就可以得到整个数组占用内存的大小,如下:

     int ii[10];    // 定义一个整型数组变量
     printf("sizeof(ii)=%d\n",sizeof(ii));     // 输出结果:sizeof(ii)=40
    

    - 数组的初始化

    采用memset函数对数组进行初始化,如下:
    
      int no[10];
      memset(no,0,sizeof(no));
    

    数组下标越界相当于访问了其它程序的内存,可能会导致程序异常中断(Core dump)。由操作系统的内核中断。
    段错误,就是程序非法操作内存,引起程序的崩溃。

    - 二维数组定义的语法是:

    数据类型 数组名[第一维的长度][第二维的长度];
    
    二维数组的初始化也是用memset,例如:
    
      memset(girl,0,sizeof(girl));
    

    9.字符串

    字符串就是一个以空字符’\0’结束的字符数组,是一个特殊的字符数组,这是约定,是规则。

    空字符’\0’也可以直接写成0。

    //字符数组中写的\0等于对某个字符赋值0;
    char str1[11]="123\0 567";
    char str[4]=0;
    

    在这里插入图片描述
    因为字符串需要用0结束,所以在定义字符串的时候,要预留多一个字节来存放0。

    - 如果字符串不用0结束,会有什么样的结果,我们用代码来演示一下

    #include <stdio.h>
    #include <string.h>
     
    int main()
    {
      char name[3];
      memset(name,0,sizeof(name));
     
      name[0]='a';
      name[1]='b';
      name[2]='c';
     
      printf("name=%s=\n",name);
    }
    

    上面代码输出abc之后,有乱码,并且每次执行程序输出的结果不可预知。
    输出时会在name后的内存中一直找到‘\0’才会结束输出,这也是后面,乱码的来源。
    如果‘\0’在字符串中间,后面的内容将被丢弃。

    - 字符串长度问题
    1.对于strlen来说,计量标准为字符串数组中的非终结符字符个数;

    2.对于sizeof来说,计量标准为字符串中的所有位数,包含非终结符号;

    对于相同的字符串char[]="12345";
    
    strlen的结果为5sizeof的结果为6

    所以得出一个结论:定义n字节的char字符串,只能存放n-1个字符,最后一位一定是\0
    sizeof字符数组的大小和字符数组的实际大小一定是一样的。

    char str[3]="12\0";
    printf("str=%s",str);
    printf("str len=%d",strlen(str));
    printf("str3 sizeof=%ld\n",sizeof(str3));
    //输出结果:12.
    

    - 越界问题
    数组越界:
    二维数组的两个变量之间的内存是连续的,第一个元素之后没有多余的空间,如果给两个变量strcpy大于数组长度的数据,那么输出肯定会有问题。
    普通变量越界:
    如果给普通字符串变量strcpy数据不一定会报错,因为该变量后面的地址可能是空的,如果不是空的也可能会报错,看运气。

    - 字符串比较大小
    两个字符串比较的方法是比较字符的ASCII码的大小,从两个字符串的第一个字符开始,如果分不出大小,就比较第二个字符,如果全部的字符都分不出大小,就返回0,表示两个字符串相等。

    10.变量的作用域

    1)在所有函数外部定义的是全局变量。

    2)在头文件中定义的是全局变量。

    3)在函数或语句块内部定义的是局部变量。

    4)函数的参数是该函数的局部变量。

    全局变量在主程序退出时由系统收回内存空间。

    局部变量和全局变量的名称可以相同,在某函数或语句块内部,如果局部变量名与全局变量名相同,就会屏蔽全局变量而使用局部变量。

    11.指针

    指针也是一种内存变量,是内存变量就要占用内存空间,
    在C语言中,任何类型的指针占用8字节的内存(32位操作系统4字节)

    int x=11;
    
    //p的值就跟x的值是一样的,代表的是变量的值。
    //例如:x的值就是11;p的值就是&x的值,
    //&p的值是指针变量p的内存地址。
    int * p=&x;
    
    

    - 空指针

    就是说指针没有指向任何内存变量,指针的值是空,所以不能操作内存,否则可能会引起程序的崩溃。

    12.数组的地址

    数组占用的内存空间是连续的,数组名是数组元素的首地址,也是数组的地址。

    13.整数

    下面是64位linux和32位占字节数:
    在这里插入图片描述
    注意:

    1)计算机用最高位1位来表达符号(0-正数,1-负数),unsigned修饰过的正整数不需要符号位,在表达正整数的时候比signed修饰的正整数取值大一倍。

    - 二进制
    二进制由 0 和 1 两个数字组成,书写时**必须以0b或0B(不区分大小写)**开头,例如:

     // 以下是合法的二进制
      int a = 0b101;      // 换算成十进制为 5
      int b = -0b110010;  // 换算成十进制为 -50
      int c = 0B100001;   // 换算成十进制为 33
      
      // 以下是非法的二进制
      int m = 101010;  // 无前缀 0B,相当于十进制
      int n = 0B410;    // 4不是有效的二进制数字
    

    - 八进制

    八进制由 0~7 八个数字组成,书写时必须以0开头(注意是数字 0,不是字母 o),例如:

    // 以下是合法的八进制数
      int a = 015;      // 换算成十进制为 13
      int b = -0101;    // 换算成十进制为 -65
      int c = 0177777;  // 换算成十进制为 65535
     
      // 以下是非法的八进制
      int m = 256;  // 无前缀 0,相当于十进制
      int n = 03A2;  // A不是有效的八进制数字
    

    - 十六进制
    十六进制由数字 0~9、字母 A~F 或 a~f(不区分大小写)组成,书写时必须以0x或0X(不区分大小写)开头,例如:

     // 以下是合法的十六进制
      int a = 0X2A;   // 换算成十进制为 42
      int b = -0XA0;  // 换算成十进制为 -160
      int c = 0xffff;   // 换算成十进制为 65535
     
      // 以下是非法的十六进制
      int m = 5A;    // 没有前缀 0X,是一个无效数字
      int n = 0X3H;  // H不是有效的十六进制数字
    

    注意:在C语言中,不要在十进制数前加0,会被计算机误认为是八进制数。

    - 整数的输出

    输出格式含义
    %hd、%d、%ld以十进制、有符号的形式输出short、int、long 类型的整数。
    %hu、%u、%lu以十进制、无符号的形式输出short、int、long 类型的整数。
    %ho、%o、%lo以八进制、不带前缀、无符号的形式输出 short、int、long 类型的整数
    %#ho、%#o、%#lo以八进制、带前缀、无符号的形式输出 short、int、long 类型的整数
    %hx、%x、%lx %hX、%X、%lX以十六进制、不带前缀、无符号的形式输出 short、int、long 类型的整数。如果 x 小写,那么输出的十六进制数字也小写;如果 X 大写,那么输出的十六进制数字也大写。
    %#hx、%#x、%#lx %#hX、%#X、%#lX以十六进制、带前缀、无符号的形式输出 short、int、long 类型的整数。如果 x 小写,那么输出的十六进制数字和前缀都小写;如果 X 大写,那么输出的十六进制数字和前缀都大写。

    - 常用的库函数
    使用库函数一定要加头文件stdlib.h,否则会报错。

    //C语言提供了几个常用的库函数,声明如下:
    int  atoi(const char *nptr);   // 把字符串nptr转换为int整数
    long atol(const char *nptr);     // 把字符串nptr转换为long整数
    int  abs(const int j);            // 求int整数的绝对值
    long labs(const long int j);     // 求long整数的绝对值
    

    14、随机数

    1、生成随机数
    在C语言中,我们使用 <stdlib.h> 头文件中的 srandrand 函数来生成随机数。

    void srand(unsigned int seed);   // 随机数生成器的初始化函数
    int  rand();                        // 获一个取随机数
    

    srand函数初始化随机数发生器(俗称种子),在实际开发中,我们可以用时间作为参数,只要每次播种的时间不同,那么生成的种子就不同,最终的随机数也就不同,通常我们采用 <time.h> 头文件中的 time 函数即可得到一个精确到秒的时间作为种子;但同一秒下生成的随机数还会是相同的。
    示例:

    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
     
    int main()
    {
      int ii;
      srand(time(0));  // 播下随机种子
      for (ii=0;ii<5;ii++)  // 生成5个随机数
      {
        printf("%d ", rand());  // 获取随机数
      }
      printf("\n");
    }
    

    2、生成一定范围随机数
    对于产生一定范围的随机数,就需要使用一定的技巧,常用的方法是取模运算(取余数),再加上一个加法运算:

    //任何数取余的余数一定是小于被除数(50)的,因为除不下时才会剩下余数。
    int a = rand() % 50;   // 产生0~49的随机数
    
    如果要规定上下限:
      int a = rand() % 51 + 100;   // 产生100~150的随机数
    

    15、数据类型的别名

    使用 typedef 关键字来给数据类型定义一个别名,示例如下:

    typedef unsigned int size_t;
    size_t ii; 等同于 unsigned int ii;
    

    16、字符

    - ASCII码表
    省略。

    - 转义字符

    C语言中,字符需要转义的情况有两种:

    1)对于 ASCII 编码,0~31(十进制)范围内的字符为控制字符,它们都是看不见的,不能在显示器上显示,也无法从键盘输入;

    2)某些字符在C语言中有特别的用途,如单引号、双引号、反斜杠。

    C语言又定义了一种简单的书写方式,即转义字符的形式来表示。

    转义字符完整的列表如下:
    在这里插入图片描述

    - 字符就是整数
    字符和整数没有本质的区别。可以给 char 变量一个字符,也可以给它一个整数;反过来,可以给 int 变量一个整数,也可以给它一个字符。

    char 变量在内存中存储的是字符对应的 ASCII 码值。如果以 %c 输出,会根据 ASCII 码表转换成对应的字符,如果以 %d 输出,那么还是整数。

    int 变量在内存中存储的是整数本身,如果以 %c 输出时,也会根据 ASCII 码表转换成对应的字符。

    也就是说,ASCII 码表将整数和字符关联起来了。

    char类型占内存一个字节,signed char取值范围是-128-127,unsigned char取值范围是0-255。

    如果整数大于255,那么整数还是字符吗?

    描述再准确一些,在char的取值范围内(0-255),字符和整数没有本质区别。

    字符肯定是整数,0-255范围内的整数是字符,大于255的整数不是字符。

    - 常用的库函数
    以下是常用的字符函数,必须掌握。

    int isalpha(int ch);  // 若ch是字母('A'-'Z','a'-'z')返回非0值,否则返回0。
    int isalnum(int ch);  // 若ch是字母('A'-'Z','a'-'z')或数字('0'-'9'),返回非0值,否则返回0。
    int isdigit(int ch);  // 若ch是数字('0'-'9')返回非0值,否则返回0。
    int islower(int ch);  // 若ch是小写字母('a'-'z')返回非0值,否则返回0。
    int isupper(int ch);  // 若ch是大写字母('A'-'Z')返回非0值,否则返回0。
    int tolower(int ch);  // 若ch是大写字母('A'-'Z')返回相应的小写字母('a'-'z')。
    int toupper(int ch);  // 若ch是小写字母('a'-'z')返回相应的大写字母('A'-'Z')
    

    以下是不常用的字符函数,极少使用,了解即可。

    int isascii(int ch);  // 若ch是字符(ASCII码中的0-127)返回非0值,否则返回0。
    int iscntrl(int ch);  // 若ch是作废字符(0x7F)或普通控制字符(0x00-0x1F),返回非0值,否则返回0。
    int isprint(int ch);  // 若ch是可打印字符(含空格)(0x20-0x7E)返回非0值,否则返回0。
    int ispunct(int ch);  // 若ch是标点字符(0x00-0x1F)返回非0值,否则返回0。
    int isspace(int ch);  // 若ch是空格(' '),水平制表符('/t'),回车符('/r'),走纸换行('/f'),垂直制表符('/v'),换行符('/n'),返回非0值,否则返回0。
    int isxdigit(int ch); // 若ch是16进制数('0'-'9','A'-'F','a'-'f')返回非0值,否则返回0。
    

    大写转小写,ASCII码加32就可以了。
    输出百分号:printf("%%\n");

    17.浮点数

    - 占用内存的情况
    在这里插入图片描述
    - 浮点数的精度
    float数的两个特征:

    1)float数据类型表达的是一个近似的数,不是准确的,小数点后的n位有误差,浮点数的位数越大,误差越大,到8位的时候,误差了1,基本上不能用了。

    2)用“==”可以比较两个整数或字符是否相等,但是,看起来相等的两个浮点数,就是不会相等。

    double数的两个特征:

    1)double数据类型表达的也是一个近似的数,不是准确的,小数点后的n位有误差,浮点数的位数越大,误差越大,到18位的时候,误差了1,基本上不能用了。

    2)用“==”可以比较两个double数值是否相等。

    总结:
    float只能表达6-7位的有效数字,不能用“==”判断两个数字是否相等。

    double能表达15-16位有效的数字,可以用“==”判断两个数字是否相等。

    long double占用的内存是double的两倍,但表达数据的精度和double相同。

    在实际开发中,建议弃用float,只采用double就可以,long double暂时没有必要,但不知道以后的操作系统和编译器对long double是否有改进。

    - 浮点数的输出

    float采用%f占位符。
    
    double采用%lf占位符。测试结果证明,double不可以用%f输入,但可以用%f输出,但是不建议采用%f,因为不同的编译器可能会有差别。
    
    long double采用%Lf占位符,注意,L是大写。
    
    浮点数输出缺省显示小数点后六位。
    
    浮点数采用%lf输出,完整的输出格式是%m.nlf,指定输出数据整数部分和小数部分共占m位,其中有n位是小数。如果数值长度小于m,则左端补空格,若数值长度大于m,则按实际位数输出。
    
      double ff=70001.538;
      printf("ff=%lf=\n",ff);       // 输出结果是ff=70001.538000=
      printf("ff=%.4lf=\n",ff);     // 输出结果是ff=70001.5380=
      printf("ff=%11.4lf=\n",ff);   // 输出结果是ff= 70001.5380=
      printf("ff=%8.4lf=\n",ff);    // 输出结果是ff=70001.5380=
    

    - 常用的库函数
    只介绍double

    double atof(const char *nptr);       // 把字符串nptr转换为double
    double fabs(double x);                // 求双精度实数x的绝对值
    double pow(double x, double y);      // 求 x 的 y 次幂(次方)
    double round(double x);               // double将小数第一位四舍五入到整数位
    double ceil(double x);                // double向上取整数
    double floor(double x);               // double向下取整数
    double fmod(double x,double y);      // 求x/y整除后的双精度余数
    // 把双精度val分解成整数部分和小数部分,整数部分存放在ip所指的变量中,返回小数部分。
    double modf(double val,double *ip);
    

    - 应用经验

    浮点数有一些坑,例如两个浮点数不相等和精度的问题,在实际开发中,我们经常用整数代替浮点数,因为整数是精确的,效率也更高。
    
    例如人的身高一米七五,以米为单位,用浮点数表示是1.75米,如果以厘米为单位,用整数表示是175long整数的取值是-9223372036854775808~9223372036854775807,有效数字是19位,而double的有效数字才15-16位,所以,整数可以表达的小数更大的数,更实用,麻烦也更少。
    
    货币:1.75元,如果采用0.01元为单位就是175,采用0.001元为单位就是1750,如果您说要更多小数怎么办?您这是钻牛角尖。
    
    码农之道:高水平的程序员不容易掉坑里,注意,是不容易,不是不会,最好的方法是不要靠近坑。
    

    - 科学计数法
    科学记数法是一种记数的方法。把一个数表示成a与10n相乘的形式(1≤|a|<10,n为整数),这种记数法叫做科学记数法。

    51400000000=5.14×1010,计算机表达10的幂是一般是用E或e,也就是51400000000=5.14E105.14e100.00001=1×10-5,即绝对值小于1的数也可以用科学记数法表示为a乘10-n的形式。即1E-51e-5

    18.结构体

    使用结构体(struct)来存放一组不同类型的数据。

    - 内存占用
    结构体会自动内存对齐。

    C语言提供了结构体成员内存对齐的方法,在定义结构体之前,增加以下代码可以使结构体成员变量之间的内存没有空隙。
    #pragma pack(1)
    

    - 结构体的变量名

    和数组不一样,结构体变量名不是结构体变量的地址。

      struct st_girl stgirl;
      printf("%d\n",stgirl);   // 没有意义。
      printf("%p\n",stgirl);   // 没有意义,结构体变量名不是结构体变量的地址。
      printf("%p\n",&stgirl);  // 这才是结构体的地址。
    

    - 结构体初始化
    采用memset函数初始化结构体,全部成员变量的值清零:

     memset(&queen,0,sizeof(struct st_girl));
    或
      m**加粗样式**emset(&queen,0,sizeof(queen));
    

    - 成员的访问
    采用圆点.运算符可以访问(使用)结构的成员。

    - 结构体数组

    结构体可以被定义成数组变量,本质上与其它类型的数组变量没有区别。
    C++标准库的vector容器是一个动态的结构体数组,比结构体数组更方便。

    - 结构体指针
    结构体是一种自定义的数据类型,结构体变量是内存变量,有内存地址,也就有结构体指针。

     struct st_girl queen;
      struct st_girl *pst=&queen;
      
    通过结构体指针可以使用结构体成员,一般形式为:
      (*pointer).memberName
    或者:
      pointer->memberName
    

    - 结构体的复制
    函数声明:

    void *memcpy(void *dest, const void *src, size_t n);
    参数说明:
    src 源内存变量的起始地址。
    dest 目的内存变量的起始地址。
    n 需要复制内容的字节数。
    函数返回指向dest的地址,函数的返回值意义不大,程序员一般不关心这个返回值。
    

    示例:

    #include <stdio.h>
    #include <string.h>
     
    struct st_girl
    {
      char name[50];     // 姓名
      int  age;          // 年龄
    };
     
    void main()
    {
      struct st_girl girl1,girl2;
     
      strcpy(girl1.name,"西施");  // 对girl1的成员赋值
      girl1.age=18;
     
      // 把girl1的内容复制到girl2中
      memcpy(&girl2,&girl1,sizeof(struct st_girl));
     
      printf("girl1.name=%s,girl1.age=%d\n",girl1.name,girl1.age);
      printf("girl2.name=%s,girl2.age=%d\n",girl2.name,girl2.age);
    }
    

    - 清零函数
    memset和bzero函数。

    bzero函数不是标准库函数,所以可移植性不是很好,建议使用memset函数代替。

    bzero函数是内存空间清零。
    
    包含在<string.h>头文件中。
    
    函数的声明如下:
    void bzero(void *s, size_t n);
    s为内存空间的地址,一般是数组名或结构体的地址。
    n为要清零的字节数。
    
    如果要对数组或结构体清零,用memset和bzero都可以,没什么差别,看程序员的习惯。
    

    19.格式化输出

    格式化输出的函数有printf、sprintf和snprintf等,功能略有不同,使用方法大同小异。
    printf为例:
    函数声明:

    int printf(const char *format, ...);
    

    格式说明符的形式如下(方括号 [] 中的项为可选项):

      %[flags][width][.prec]type
    
    1、类型符(type)
    它用以表示输出数据的类型,以下是常用类型的汇总,不常用的就不列了。
    %hd、%d、%ld 以十进制、有符号的形式输出 shortintlong 类型的整数。
    %hu、%u、%lu 以十进制、无符号的形式输出 shortintlong 类型的整数
    %c 输出字符。
    %lf 以普通方式输出doublefloat弃用,long doube无用)。
    %e 以科学计数法输出double%s 输出字符串。
    %p 输出内存的地址。
    
    %02u 输出无符号整型两位,不足两位用0吧补齐
    
    2、宽度(width)
    它用于控制输出内容的宽度。
    printf("=%12s=\n","abc");     // 输出=         abc=
    printf("=%12d=\n",123);       // 输出=         123=
    printf("=%12lf=\n",123.5);    // 输出=  123.500000=
    
    3、对齐标志(flags)
    flags它用于控制输出内容的对齐方式。
    不填或+:输出的内容右对齐,这是缺省的方式,上一小节就是右对齐的示例。
    -:输出的内容左对齐。
      printf("=%-12s=\n","abc");    // 输出=abc         =
      printf("=%-12d=\n",123);     // 输出=123         =
      printf("=%-12f=\n",123.5);    // 输出=123.500000  =
    如果输出的内容是整数或浮点数,并且对齐的方式是右对齐,可以加0填充,例如:
      printf("=%012s=\n","abc");  // 输出=         abc=
      printf("=%012d=\n",123);   // 输出=000000000123=
      printf("=%012f=\n",123.5);  // 输出=00123.500000=
    
    4、精度(prec)
    如果输出的内容是浮点数,它用于控制输出内容的精度,也就是说小数点后面保留多少位,后面的数四舍五入。
      printf("=%12.2lf=\n",123.5);   // 输出=      123.50=
      printf("=%.2lf=\n",123.5);     // 输出=123.50=
      printf("=%12.2e=\n",123500000000.0);  // 输出=    1.24e+11=
      printf("=%.2e=\n",123500000000.0);    // 输出=1.24e+11=
    

    20.main函数

    1. main函数的参数
      main函数有三个参数,argc、argv和envp,它的标准写法如下:
    int main(int argc,char *argv[],char *envp[])
    
    int argc,存放了命令行参数的个数。
    char *argv[],是个字符串的数组,每个元素都是一个字符指针,指向一个字符串,即命令行中的每一个参数。
    char *envp[],也是一个字符串的数组,这个数组的每一个元素是指向一个环境变量的字符指针。
    envp先放一下,先讲argc和argv。
    
    注意几个事项:
    1)argc的值是参数个数加1,因为程序名称是程序的第一个参数,即argv[0],在上面的示例中,argv[0]./book101。
    2)main函数的参数,不管是书写的整数还是浮点数,全部被认为是字符串。
    3)参数的命名argc和argv是程序员的约定,您也可以用argd或args,但是不建议这么做。
    

    示例:

    #include <stdio.h>
     
    int main(int argc,char *argv[])
    {
      int ii=0;
     
      // 显示参数的个数
      printf("argc is %d\n",argc);
     
      // 列出全部的参数
      for (ii=0;ii<argc;ii++)
      {
        printf("argv[%d] is %s\n",ii,argv[ii]);
      }
    }
    

    运行结果:在这里插入图片描述

    1. envp参数
      envp存放了当前程序运行环境的参数。
      示例:
    #include <stdio.h>
     
    int main(int argc,char *argv[],char *envp[])
    {
      int ii = 0;
     
      while (envp[ii] != 0)  // 数组最后一个元素是0
      {
        printf("%s\n",envp[ii]); ii++;
      }
    }
    

    运行结果:
    在这里插入图片描述

    注意:示例运行的结果与linux系统的env命令相同
    在实际开发中,envp参数的应用场景不多,各位了解一下就行了。

    21.动态内存管理

    编写程序的时候不能确定内存的大小,希望程序在运行的过程中根据数据量的大小动态的分配内存。动态内存管理,就是指在程序运行过程中动态的申请和释放内存空间

    C语言允许程序动态管理内存,需要时随时开辟,不需要时随时释放。内存的动态管理是通过调用库函数来实现的,主要有malloc和free函数。
    使用动态分配内存技术的时候,分配出来的内存必须及时释放并让指针指向空或正确的地方free(pi); pi=0;

    1. malloc 库函数
    命名空间:<stdlib.h>
    
    void *malloc(unsigned int size);
    malloc的作用是向系统申请一块大小为size的连续内存空间,如果申请失败,函数返回0,如果申请成功,返回成功分配内存块的起始地址。
    例如:
      malloc(100)// 申请 100 个字节的临时分配域,返回值为其第一个字节的地址
    
    1. free 函数
    函数的原型:
    void free(void *p);
    free的作用是释放指针p指向的动态内存空间,p是调用malloc函数时返回的地址,free函数无返回值。
    例如:
      free(pi);     // 释放指针变量pi指向的已分配的动态空间
    

    特别注意:
    free中的指针变量,一定是mallocd动态分配出来的否则会一直报free()invalid size的错误。
    3. 示例

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
     
    struct st_girl  // 超女结构体
    {
      char name[50];     // 姓名
      int  age;          // 年龄
    };
     
    int main(int argc,char *argv[])
    {
      int    *pi=malloc(sizeof(int));    // 分配int类型大小的内存
      long   *pl=malloc(sizeof(long));   // 分配long类型大小的内存
      double *pd=malloc(sizeof(double)); // 分配double类型大小的内存
      char   *pc=malloc(101);            // 分配101字节的内存,可存放100个字符的字符串
      struct st_girl *pst=malloc(sizeof(struct st_girl)); // 分配struct st_girl结构体大小的内存
     
      // 以下代码是像普通指针和变量一样使用动态分配的内存
      *pi=10;     printf("*pi=%d\n",*pi);
      *pl=20;     printf("*pl=%d\n",*pl);
      *pd=10.5;   printf("*pd=%.1f\n",*pd);
      strcpy(pc,"西施"); printf("*pc=%s\n",pc);
      strcpy(pst->name,"杨玉环"); pst->age=21;
      printf("name=%s,age=%d\n",pst->name,pst->age);
     
      // 释放动态分配的内存
      free(pi); free(pl); free(pd); free(pc); free(pst);
    }
    

    运行结果:
    在这里插入图片描述

    1. 野指针
    1、内存指针变量未初始化
    指针变量在创建的同时应当被初始化,要么将指针的值设置为0,要么让它指向合法的内存。
    
      int *pi=0;int i;
      int *pi=&i;
    
    
    2、内存释放后之后指针未置空
    调用free函数把指针所指的内存给释放掉,但指针不一定会赋值 0(也与编译器有关),如果对释放后的指针进行操作,相当于非法操作内存。释放内存后应立即将指针置为0free(pi);
      pi=0;
    

    22.文件操作

    参考链接
    1. 文本文件和二进制文件
    文本文件或ASCII文件:按文本格式存放数据的文件;文件可以用vi和记事本打开,看到的都是ASCII字符。

    二进制文件:按二进制格式存放数据的文件。
    在这里插入图片描述

    - 文本文件读写:
    1. 文件的打开

    FILE *fopen( const char * filename, const char * mode );
    
    参数filename 是字符串,表示需要打开的文件名,可以包含目录名,如果不包含路径就表示程序运行的当前目录。实际开发中,采用文件的全路径。
    
    参数mode也是字符串,表示打开文件的方式(模式),打开方式可以是下列值中的一个。
    
    返回值:
    为空即为0,就是打开失败,反之为内存地址。
    

    在这里插入图片描述
    fopen返回的是文件结构体指针FILE:
    FILE结构体指针习惯称为文件指针。
    操作文件的时候,C语言为文件分配一个信息区,该信息区包含文件描述信息、缓冲区位置、缓冲区大小、文件读写到的位置等基本信息,这些信息用一个结构体来存放(struct _IO_FILE),这个结构体有一个别名FILE(typedef struct _IO_FILE FILE),FILE结构体和对文件操作的库函数在 stdio.h 头文件中声明的。
    打开文件的时候,fopen函数中会动态分配一个FILE结构体大小的内存空间,并把FILE结构体内存的地址作为函数的返回值,程序中用FILE结构体指针存放这个地址。
    关闭文件的时候,fclose函数除了关闭文件,还会释放FILE结构体占用的内存空间。

    2. 关闭文件

    int fclose(FILE *fp);
    fp为fopen函数返回的文件指针。
    
    返回值:
    一般不使用此返回值做判断。
    

    示例:

    #include <stdio.h>
    
    int main()
    {
      FILE *fp=0;     // 定义文件指针变量fp
     
      // 以只读的方式打开文件/root/book.c
      if ( (fp=fopen("/root/book.c","r")) == 0 )
      {
        printf("打开文件/root/book.c失败。\n"); return -1;
      }
       // 关闭文件
      fclose(fp);
    }
    

    注意事项:

    1)调用fopen打开文件的时候,一定要判断返回值,如果文件不存在、或没有权限、或磁
    盘空间满了,都有可能造成打开文件失败。
    
    2)文件指针是调用fopen的时候,系统动态分配了内存空间,函数返回或程序退出之前,
    必须用fclose关闭文件指针,释放内存,否则后果严重。
    
    3)如果文件指针是空指针或野指针,用fclose关闭它相当于操作空指针或野指针,后果
    严重。
    

    3. 向文件中写入数据

    C语言向文件中写入数据库函数有fputc、fputs、fprintf,在实际开发中,
    fputc和fputs没什么用,只介绍fprintf就可以了。fprintf函数的声明如下:
    int fprintf(FILE *fp, const char *format, ...);
    fprintf函数的用法与printf相同,只是多了第一个参数文件指针,表示把数据输出到文件。
    程序员不必关心fprintf函数的返回值。
    

    示例:

    #include <stdio.h>
     
    int main()
    {
      int   ii=0;
      FILE *fp=0;     // 定义文件指针变量fp
     
      // 以只写的方式打开文件/tmp/test1.txt
      if ( (fp=fopen("/tmp/test1.txt","w")) == 0)
      {
        printf("fopen(/tmp/test1.txt) failed.\n"); return -1;
      }
     
      for (ii=0;ii<3;ii++) // 往文件中写入3行
      {
        fprintf(fp,"这是第%d个出场的超女。\n",ii+1);
      }
     
      // 关闭文件
      fclose(fp);
    }
    

    使用cat命令查看结果:
    在这里插入图片描述

    4. 从文件中读取数据

    	C语言从文件中读取数据的库函数有fgetc、fgets、fscanf,在实际开发中,
    fgetc和fscanf没什么用,只介绍fgets就可以了。fgets函数的原型如下:
    char *fgets(char *buf, int size, FILE *fp);
    
    fgets的功能是从文件中读取一行。
    参数buf是一个字符串,用于保存从文件中读到的数据。
    参数size是打算读取内容的长度。
    参数fp是待读取文件的文件指针。
    	如果文件中将要读取的这一行的内容的长度小于size,fgets函数就读取一行,如
    果这一行的内容大于等于size,fgets函数就读取size-1字节的内容。
    
    返回值:
    	调用fgets函数如果成功的读取到内容,函数返回buf,如果读取错误或文件已结束,
    返回空,即0。如果fgets返回空,可以认为是文件结束而不是发生了错误,
    因为发生错误的情况极少出现。
    
    
    
    在读取到 size-1 个字符之前如果出现了换行,或者读到了文件末尾,则读取结束。
    不管 size 的值多大,fgets函只读取一行数据,不能跨行。如果size设置过小,会导致中文字符读一半,从而导致读出数据不符合预期的问题。
    
    在实际开发中,可以将 size 的值设置地足够大,确保每次都能读取到一行完整的数据。
    

    特别注意:文件读和写不能共用一个FILE文件结构指针,否则谁在后面谁就失效!

    - 二进制文件的读写
    参考链接

    二进制文件没有行的概念,没有字符串的概念。

    我们把内存中的数据结构直接写入二进制文件,读取的时候,也是从文件中读取数据结构的大小一块数据,直接保存到数据结构中。注意,这里所说的数据结构不只是结构体,是任意数据类型。

    向文件中写入数据:

    fwrite函数用来向文件中写入数据块,它的原型为:
    
    size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
    参数的说明:
    
    ptr:为内存区块的指针,存放了要写入的数据的地址,它可以是数组、变量、结构体等。
    
    size:固定填1。
    
    nmemb:表示打算写入数据的字节数。
    
    fp:表示文件指针。
    
    返回值是本次成功写入数据的字节数,一般情况下,程序员不必关心fwrite函数的返回值。
    

    从文件中读取数据:

    fread函数用来从文件中读取数据块,它的原型为:
    
    size_t fread(void *ptr, size_t size, size_t nmemb, FILE *fp);
    ptr:用于存放从文件中读取数据的变量地址,它可以是数组、变量、结构体等。
    
    size:固定填1。
    
    nmemb:表示打算读取的数据的字节数。
    
    fp:表示文件指针。
    
    返回值:
    读取成功返回字节数;
    错误或结束返回空,即0。
    如果fread返回空,可以认为是文件结束而不是发生了错误,因为发生错误的情况极少出现。
    

    综上:二进制文件中一般只写一种结构的数据,因为二进制文件无换行和字符等概念,这样易于读取。

    - 文件定位

    1. 概念

    在文件内部有一个位置指针,用来指向文件当前读写的位置。在文件打开时,如果打开方式是r和w,位置指针指向文件的第一个字节,如果打开方式是a,位置指针指向文件的尾部。每当从文件里读取n个字节或文件里写入n个字节后,位置指针也会向后移动n个字节。

    文件位置指针与C语言中的指针不是一回事。位置指针仅仅是一个标志,表示文件读写到的位置,不是变量的地址。文件每读写一次,位置指针就会移动一次,它不需要您在程序中定义和赋值,而是由系统自动设置,对程序员来说是隐藏的。

    在实际开发中,偶尔需要移动位置指针,实现对指定位置数据的读写。我们把移动位置指针称为文件定位。

    1. 相关函数

    C语言提供了ftell、rewind和fseek三个库函数来实现文件定位功能。

    1、ftell函数
    ftell函数用来返回当前文件位置指针的值,这个值是当前位置相对于文件开始位置的字节数。它的声明如下:
    long ftell(FILE *fp);
    返回值:当前位置所在位置。
    
    2、rewind函数
    rewind函数用来将位置指针移动到文件开头,它的声明如下:
    void rewind ( FILE *fp );
    无返回值;
    
    3、fseek函数
    fseek() 用来将位置指针移动到任意位置,它的声明如下:
    int fseek ( FILE *fp, long offset, int origin );
    参数说明:
    
    1)fp 为文件指针,也就是被移动的文件。
    
    2)offset 为偏移量,也就是要移动的字节数。之所以为 long 类型,是希望移动的范围更大,能处理的文件更大。offset 为正时,向后移动;offset 为负时,向前移动。
    
    3)origin 为起始位置,也就是从何处开始计算偏移量。C语言规定的起始位置有三种,分别为:0-文件开头;1-当前位置;2-文件末尾。
    
      fseek(fp,100,0);     // 从文件的开始位置计算,向后移动100字节。
      fseek(fp,100,1);     // 从文件的当前位置计算,向后移动100字节。
      fseek(fp,-100,2);    // 从文件的尾部位置计算,向前移动100字节。
    

    总结:
    1)可以发现用fopen函数打开文件时,位置指针都是处在文件开头处,包括用a和a+方式打开,只是在第一次写时位置指针会移动到文件末尾处。

    2)只要用"a”方式打开,那么无论用fseek,rewind等文件位置指针定位函数,在写入文件信息时,均只能写入到文件末尾。

    - 文件缓冲区
    在操作系统中,存在一个内存缓冲区,当调用fprintf、fwrite等函数往文件写入数据的时候,数据并不会立即写入磁盘文件,而是先写入缓冲区,等缓冲区的数据满了之后才写入文件。还有一种情况就是程序调用了fclose时也会把缓冲区的数据写入文件。

    如果程序员想把缓冲区的数据立即写入文件,可以调用fflush库函数,它的声明如下:
    int fflush(FILE *fp);
    函数的参数只有一个,即文件指针,返回0成功,其它失败,程序员一般不关心它的返回值。
    

    - 标准输入、标准输出和标准错误

    Linux操作系统为每个程序默认打开三个文件,
    即标准输入stdin、标准输出stdout和标准错误输出stderr,
    其中0就是stdin,表示输入流,指从键盘输入,
    1代表stdout2代表stderr1,2默认是显示器。
    这三个都是文件指针 FILE *.
    
      printf("Hello world.\n");//
    等同于
      fprintf(stdout,"Hello world.\n");//表示把数据输出到文件(第一个参数代表文件指针),只不过这里stdout默认指的是屏幕文件指针。
    这几个文件指针没什么用,让大家了解一下就行。在实际开发中,我们一般会关闭这几个文件指针。
    

    补充知识点:

    1. 进制转换
    参考链接
    2. C语言代码的多行书写
    如果我们在一行代码的行尾放置一个反斜杠,c语言编译器会忽略行尾的换行符,而把下一行的内容也算作是本行的内容。
    示例:

      strcpy(str,"aaaaaaaaaa\
    bbbbbbbbb");
    
    展开全文
  • C语言知识点完美总结

    千次阅读 2020-03-18 14:39:50
    C语言重要知识点 总体上必须清楚的: 1)程序结构是三种: 顺序结构 、选择结构(分支结构)、循环结构。 其实做为一个开发者,有一个学习的氛围跟一个交流圈子特别重要这里我推荐一个C语言C++交流群583650410,不管...

    C语言最重要的知识点

    总体上必须清楚的:

    1)程序结构是三种: 顺序结构 、选择结构(分支结构)、循环结构。
    在这里插入图片描述
    其实做为一个开发者,有一个学习的氛围跟一个交流圈子特别重要这里我推荐一个C语言C++交流群583650410,不管你是小白还是转行人士欢迎入驻,大家一起交流成长。免费的公开课供你学习!

    2)读程序都要从main()入口, 然后从最上面顺序往下读(碰到循环做循环,碰到选择做选择),有且只有一个main函数。

    3)计算机的数据在电脑中保存是以 二进制的形式. 数据存放的位置就是 他的地址.

    4)bit是位 是指为0 或者1。 byte 是指字节, 一个字节 = 八个位.

    概念常考到的:

    1、编译预处理不是C语言的一部分,不占运行时间,不要加分号。C语言编译的程序称为源程序,它以ASCII数值存放在文本文件中。

    2、define PI 3.1415926; 这个写法是错误的,一定不能出现分号。 -

    3、每个C语言程序中main函数是有且只有一个。

    4、在函数中不可以再定义函数。

    5、算法:可以没有输入,但是一定要有输出。

    6、break可用于循环结构和switch语句。

    7、逗号运算符的级别最低,赋值的级别倒数第二。

    第一章 C语言的基础知识

    第一节、对C语言的基础认识

    1、C语言编写的程序称为源程序,又称为编译单位。

    2、C语言书写格式是自由的,每行可以写多个语句,可以写多行。

    3、一个C语言程序有且只有一个main函数,是程序运行的起点。

    第二节、熟悉vc++

    1、VC是软件,用来运行写的C语言程序。

    2、每个C语言程序写完后,都是先编译,后链接,最后运行。(.c—.obj—.exe)这个过程中注意.c和.obj文件时无法运行的,只有.exe文件才可以运行。(常考!)

    第三节、标识符

    1、标识符(必考内容):

    合法的要求是由字母,数字,下划线组成。有其它元素就错了。

    并且第一个必须为字母或则是下划线。第一个为数字就错了

    2、标识符分为关键字、预定义标识符、用户标识符。

    关键字:不可以作为用户标识符号。main define scanf printf 都不是关键字。迷惑你的地方If是可以做为用户标识符。因为If中的第一个字母大写了,所以不是关键字。

    预定义标识符:背诵define scanf printf include。记住预定义标识符可以做为用户标识符。

    用户标识符:基本上每年都考,详细请见书上习题。

    第四节:进制的转换

    十进制转换成二进制、八进制、十六进制。

    二进制、八进制、十六进制转换成十进制。

    第五节:整数与实数

    1)C语言只有八、十、十六进制,没有二进制。但是运行时候,所有的进制都要转换成二进制来进行处理。(考过两次)

    a、C语言中的八进制规定要以0开头。018的数值是非法的,八进制是没有8的,逢8进1。

    b、C语言中的十六进制规定要以0x开头。

    2)小数的合法写法:C语言小数点两边有一个是零的话,可以不用写。

    1.0在C语言中可写成1.

    0.1在C语言中可以写成.1。

    3)实型数据的合法形式:

    a、2.333e-1 就是合法的,且数据是2.333×10-1。

    b、考试口诀:e前e后必有数,e后必为整数。请结合书上的例子。

    4) 整型一般是4个字节, 字符型是1个字节,双精度一般是8个字节:

    long int x; 表示x是长整型。

    unsigned int x; 表示x是无符号整型。

    第六、七节:算术表达式和赋值表达式

    核心:表达式一定有数值!

    1、算术表达式:+,-,*,/,%

    考试一定要注意:“/” 两边都是整型的话,结果就是一个整型。 3/2的结果就是1.

    “/” 如果有一边是小数,那么结果就是小数。 3/2.0的结果就是0.5

    “%”符号请一定要注意是余数,考试最容易算成了除号。)%符号两边要求是整数。不是整数就错了。[注意!!!]

    2、赋值表达式:表达式数值是最左边的数值,a=b=5;该表达式为5,常量不可以赋值。

    1、int x=y=10: 错啦,定义时,不可以连续赋值。

    2、int x,y;

    x=y=10; 对滴,定义完成后,可以连续赋值。

    3、赋值的左边只能是一个变量。

    4、int x=7.7;对滴,x就是7

    5、float y=7;对滴,x就是7.0

    3、复合的赋值表达式:

    int a=2;

    a*=2+3;运行完成后,a的值是12。

    一定要注意,首先要在2+3的上面打上括号。变成(2+3)再运算。

    4、自加表达式:

    自加、自减表达式:假设a=5,++a(是为6), a++(为5);

    运行的机理:++a 是先把变量的数值加上1,然后把得到的数值放到变量a中,然后再用这个++a表达式的数值为6,而a++是先用该表达式的数值为5,然后再把a的数值加上1为6,

    再放到变量a中。 进行了++a和a++后 在下面的程序中再用到a的话都是变量a中的6了。

    考试口诀:++在前先加后用,++在后先用后加。

    5、逗号表达式:

    优先级别最低。表达式的数值逗号最右边的那个表达式的数值。

    (2,3,4)的表达式的数值就是4。

    z=(2,3,4)(整个是赋值表达式) 这个时候z的值为4。(有点难度哦!)

    z= 2,3,4 (整个是逗号表达式)这个时候z的值为2。

    补充:

    1、空语句不可以随意执行,会导致逻辑错误。

    2、注释是最近几年考试的重点,注释不是C语言,不占运行时间,没有分号。不可以嵌套!

    3、强制类型转换:

    一定是 (int)a 不是 int(a),注意类型上一定有括号的。

    注意(int)(a+b) 和(int)a+b 的区别。 前是把a+b转型,后是把a转型再加b。

    4、三种取整丢小数的情况:

    1、int a =1.6;

    2、(int)a;

    3、1/2; 3/2;

    第八节、字符

    1)字符数据的合法形式::

    ‘1’ 是字符占一个字节,”1”是字符串占两个字节(含有一个结束符号)。

    ‘0’ 的ASCII数值表示为48,’a’ 的ASCII数值是97,’A’的ASCII数值是65。

    一般考试表示单个字符错误的形式:’65’ “1”

    字符是可以进行算术运算的,记住: ‘0’-0=48

    大写字母和小写字母转换的方法: ‘A’+32=’a’ 相互之间一般是相差32。

    2)转义字符:

    转义字符分为一般转义字符、八进制转义字符、十六进制转义字符。

    一般转义字符:背诵\0、 \n、 \’、 \”、 \。

    八进制转义字符: ‘\141’ 是合法的, 前导的0是不能写的。

    十六进制转义字符:’\x6d’ 才是合法的,前导的0不能写,并且x是小写。

    3、字符型和整数是近亲:两个具有很大的相似之处

    char a = 65 ;

    printf(“%c”, a); 得到的输出结果:a

    printf(“%d”, a); 得到的输出结果:65

    第九章、位运算

    1)位运算的考查:会有一到二题考试题目。

    总的处理方法:几乎所有的位运算的题目都要按这个流程来处理(先把十进制变成二进制再变成十进制)。

    例1: char a = 6, b;

    b = a<<2; 这种题目的计算是先要把a的十进制6化成二进制,再做位运算。

    例2: 一定要记住,异或的位运算符号” ^ ”。0 异或 1得到1。

    0 异或 0得到0。两个女的生不出来。

    考试记忆方法:一男(1)一女(0)才可以生个小孩(1)。

    例3: 在没有舍去数据的时候,<<左移一位表示乘以2;>>右移一位表示除以2。

    第二章

    第一节:数据输出(一)(二)

    1、使用printf和scanf函数时,要在最前面加上#include“stdio.h”

    2、printf可以只有一个参数,也可以有两个参数。(选择题考过一次)

    3、printf(“ 第一部分 ”,第二部分 );把第二部分的变量、表达式、常量以第一部分的形式展现出来!

    4、printf(“a=%d,b=%d”,12, 34) 考试重点!

    一定要记住是将12和34以第一部分的形式现在在终端也就是黑色的屏幕上。考试核心为:一模一样。在黑色屏幕上面显示为 a=12,b=34

    printf(“a=%d,\n b=%d”,12, 34)那么输出的结果就是:a=12,

    b=34

    5、int x=017; 一定要弄清楚为什么是这个结果!过程很重要

    printf(“%d”, x); 15

    printf(“%o”, x); 17

    printf(“%#o”,x); 017

    printf(“%x”, x); 11

    printf(“%#x”,x); 0x11

    6、int x=12,y=34; 注意这种题型

    char z=‘a’;

    printf(“%d ”,x,y); 一个格式说明,两个输出变量,后面的y不输出

    printf(“%c”,z); 结果为:12a

    7、一定要背诵的

    格式说明 表示内容 格式说明 表示内容

    %d 整型 int %c 字符 char

    %ld 长整型 long int %s 字符串

    %f 浮点型 float %o 八进制

    %lf double %#o 带前导的八进制

    %% 输出一个百分号 %x 十六进制

    %5d %#x 带前导的十六进制

    举例说明:

    printf(“%2d”,123 ); 第二部分有三位,大于指定的两位,原样输出123

    printf(“%5d”,123 ); 第二部分有三位,小于指定的五位,左边补两个空格 123

    printf(“%10f”,1.25 ); 小数要求补足6位的,没有六位的补0,。结果为 1.250000

    printf(“%5.3f”,125 ); 小数三位,整个五位,结果为1.250(小数点算一位)

    printf(“%3.1f”,1.25 );小数一位,整个三位,结果为1.3(要进行四舍五入)

    第三节 数据输入

    1、scanf(“a=%d,b=%d”,&a,&b) 考试超级重点!

    一定要记住是以第一部分的格式在终端输入数据。考试核心为:一模一样。

    在黑色屏幕上面输入的为 a=12,b=34才可以把12和34正确给a和b 。有一点不同也不行。

    2、scanf(“%d,%d”,x,y);这种写法绝对错误,scanf的第二个部分一定要是地址!

    scanf(“%d,%d”,&x,&y);注意写成这样才可以!

    3、特别注意指针在scanf的考察

    例如: int x=2;int *p=&x;

    scanf(“%d”,x); 错误 scanf(“%d”,p);正确

    scanf(“%d”,&p); 错误 scanf(“%d”,*p)错误

    4、指定输入的长度 (考试重点)

    终端输入:1234567

    scanf(“%2d%4d%d”,&x,&y,&z);x为12,y为3456,z为7

    终端输入:1 234567 由于1和2中间有空格,所以只有1位给x

    scanf(“%2d%4d%d”,&x,&y,&z);x为1,y为2345,z为67

    5、字符和整型是近亲:

    int x=97;

    printf(“%d”,x); 结果为97

    printf(“%c”,x); 结果为 a

    6、输入时候字符和整数的区别(考试超级重点)

    scanf(“%d”,&x);这个时候输入1,特别注意表示的是整数1

    scanf(“%c”,&x);这个时候输入1,特别注意表示的是字符‘1’ASCII为整数48。

    补充说明:

    1)scanf函数的格式考察:

    注意该函数的第二个部分是&a 这样的地址,不是a;

    scanf(“%d%d%*d%d”,&a,&b,&c); 跳过输入的第三个数据。

    2)putchar ,getchar 函数的考查:

    char a = getchar() 是没有参数的,从键盘得到你输入的一个字符给变量a。

    putchar(‘y’)把字符y输出到屏幕中。

    3)如何实现两个变量x ,y中数值的互换(要求背下来)

    不可以把 x=y ,y=x; 要用中间变量 t=x;x=y;y=t。

    4)如何实现保留三位小数,第四位四舍五入的程序,(要求背下来)

    y=(int)(x*100+0.5)/100.0 这个保留两位,对第三位四舍五入

    y=(int)(x*1000+0.5)/1000.0 这个保留三位,对第四位四舍五入

    y=(int)(x*10000+0.5)/10000.0 这个保留四位,对第五位四舍五入

    这个有推广的意义,注意 x = (int)x 这样是把小数部分去掉。

    第三章

    特别要注意:C语言中是用非0表示逻辑真的,用0表示逻辑假的。

    C语言有构造类型,没有逻辑类型。

    关系运算符号:注意<=的写法,==和=的区别!(考试重点)

    if只管后面一个语句,要管多个,请用大括号!

    1)关系表达式:

    a、表达式的数值只能为1(表示为真),或0(表示假)。

    如 9>8这个关系表达式是真的,所以9>8这个表达式的数值就是1。

    如 7<6这个关系表达式是假的,所以7<6这个表达式的数值就是0

    b、考试最容易错的:就是int x=1,y=0,z=2

    展开全文
  • C语言知识点汇总

    千次阅读 2022-04-06 22:20:30
    C语言知识点保姆级总结,这不得进你的收藏夹吃灰?! 拖了很久的C语言所学知识的简单小结,内容有点多,第一次总结也可能有错误或者不全面,欢迎随时补充说明! 数据类型 ​ 用不同数据类型所定义的变量所占空间...

    C语言——只看这一篇就够了!

    C语言知识点保姆级总结,这不得进你的收藏夹吃灰?!
    拖了很久的C语言所学知识的简单小结,内容有点多,第一次总结也可能有错误或者不全面,欢迎随时补充说明!

    数据类型

    ​ 用不同数据类型所定义的变量所占空间大小不一样,定义的变量不是保存于数据类型中,而是因为只有定义了该数据类型的变量才能保存数据。

    一、整型

    ​ 1、整型(int) 四字节,默认有符号(-231-231-1),无符号加unsigned(0-2^32-1)(十位数)

    ​ 2、短整型(short int) ,两字节(-215-215-1)(五位数)

    ​ 3、长整型(long int) ,四字节(同int,在目前的操作系统中几乎没有区别)

    ​ 4、长长整型(long long int), 八字节(-232-232-1)

    二、浮点型

    ​ 1、单精度浮点数(float),四字节,保留小数点后6位

    ​ 2、双精度浮点数(double),八字节,保留小数点后15位

    int为一个32为的存储单元,long long为64为的存储单元

    1 B/byte(字节) = 8 bit(比特)(位)

    1 KB(千字节) = 1024 B/byte(字节)

    1 MB = 1024 KB

    1 GB = 1024 MB

    1TB =1024 GB

    1 PB = 1024 TB

    1 EB = 1024 PB

    三、字符型

    ​ char,用于储存字符,和int很像,可用ASCII码来储存字符,
    eg:

     char grade=’A’;
     char grade=65;
    
    	'  '单引号为字符,eg:char a='a';
    

    ​ " "双引号为字符串,eg:char* a=“asd”;编译器会自动给字符串结尾添加’\0‘来作为字符结束标识,strlen函数中不统计\0,但是\0在内存中占据空间。

    ​ 除此之外,还有转义字符,通过反斜杠来完成相关操作,如果要特殊字符转字面字符需要另外添加反斜杠,转义字符在字符串中占空间,但是只计算一个长度,\0不计长度。

    在这里插入图片描述

    四、变量和常量

    ​ 作用域(scope),程序设计概念,通常来说,一段程序代码中所用到的名字并不总是有效/可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。

    ​ 生命周期:变量的生命周期指的是变量的创建到变量的销毁之间的一个时间段

    在这里插入图片描述

    #include <stdio.h>
    int global = 2019;//全局变量
    int main()
    {
    	int local = 2018;//局部变量
    	return 0;
    }  
    

    分支及循环语句

    一、分支语句

    ​ 1、if语句

    ​ 语法结构:

    if(表达式)
    语句;
    
    if(表达式){
    	语句列表1
    }
    else{
    	语句列表2;
    }
    
    //多分支
    if(表达式1){
    	语句列表1;
    }
    else if(表达式2){
    	语句列表2;
    }
    else{
    	语句列表3;
    }
    

    ​ 表达式部分为真则执行语句(0为假,非0为真),尽量在每个分支语句后都加{},否则只会执行分支后第一条语句。

    else在没有括号的情况下遵循就近原则所以在多重if语句嵌套使用情况下一定要加括号!

    ​ 2、switch语句

    ​ switch作为分支结构常用于多分支的情况,可以简化多重if语句嵌套的情况。

    ​ 语法结构

    switch(表达式A){
               case  常量表达式1;
                        语句1;
                        break;
               case  常量表达式2;
                        语句2;
                        break;
                        ……
               case  常量表达式n;
                        语句n;
                        break;
               default:
                        语句n+1; 
                         break;
    } 
    

    ​ 其中

    ​ 1、case后第一句不可定义变量,必须跟常量或者常量表达式,并且不可相同;

    ​ 2、break在语句中可以起到划分作用,不可省略,否则无法实现分支功能;

    ​ 3、default语句不应该省略,一般推荐位语句列表末尾;

    ​ 4、switch语句结束条件:①遇到break;②执行到语句列表末尾。

    二、循环语句

    ​ 1、while语句

    ​ 语法结构

    while(表达式){
    	循环语句;
    }
    

    注:在循环语句中break的作用是停止后期所有循环,continue的作用是终止本次循环,开始下一次循环的判断。

    ​ 2、for语句

    for(表达式1;表达式2;表达式3){
    	循环语句;
    }
    

    ​ 表达式1为初始化部分,用于初始化循环变量,当表达式1为空时直接进入循环;

    ​ 表达式2 为条件判断部分,用于判断循环是否终止,当表达式2为空时为死循环;

    ​ 表达式3为调整部分,用于循环条件的调整 。

    注:建议使用“前闭后开”来限定区间范围。

    for(i=0; i<10; i++){
    	a[i]=i;
    }
    

    ​ 3、do while语句

    do{
    	循环语句;
    }while(表达式);
    

    ​ 循环体至少执行一次,while之后记得加分号。

    ​ 二分查找函数循环实现范例:

    int bin_search(int arr[], int left, int right, int key)
    {
    	int mid = 0;
    	while(left<=right){
    		mid = (left+right)>>1;
    		if(arr[mid]>key)
    		{
    			right = mid-1;
    		}
    		else if(arr[mid] < key)
    		{
    			left = mid+1;
    		}
    		else
    		{
    			return mid;//找到了,返回下标
    		}
    	}
    	return -1;//找不到
    }
    

    函数

    一、库函数

    ​ C语言基础库中的函数,在添加头文件后可直接调用。

    二、自定义函数

    1、函数组成

    ​ 由函数名、返回值类型、函数参数以及函数体组成。

    ​ 实参:真实的传入函数的变量,**在被调用时会发生临时拷贝,并非把原来的变量直接放入函数中,**只是把实参的数据拷贝给形参。

    ​ 形参:函数名括号后的变量,因为形参只有在被调用的时候才被实例化并分配空间(形参实例化),**在被调用过后即被销毁,只在该函数中有效(局部变量),**所以叫形参。

    函数声明,要满足先声明后使用的原则,由返回值类型、函数名与函数参数组成(需要加分号), 当我们用到很多函数声明的时候,为了方便我们的调用,我们可以创建一个头文件.h(比如test.h),将函数声明放在头文件当中 ,在写头文件时,要注意加上#pragma once 。

    //函数定义
    double	Add(double x, double y){
    	return x+y;
    }
    //函数声明
    double 	Add(double x, double y);
    

    2、函数调用

    ​ 分为传值调用与传址调用,其中传址调用是把函数外部创建的内存地址传递给函数,可以真正与原值建立起联系,直接操纵函数外部的变量。

    ​ 函数也可以进行嵌套调用以及链式访问。

    ​ 嵌套调用样例:

    #include <stdio.h>
    void new_line()
    {
    	printf("hehe\n");
    }
    void three_line()
    {
    	int i = 0;
    	for(i=0; i<3; i++)
    	{
    		new_line();
    	}
    }
    int main()
    {
    	three_line();
    	return 0;
    }
    

    ​ 链式访问样例:

    #include <stdio.h>
    #include <string.h>
    int main()
    {
    	char arr[20] = "hello";
    	int ret = strlen(strcat(arr,"bit"));
    	printf("%d\n", ret);
    	return 0;
    }
    

    3、函数递归

    ​ 程序自身调用被称为递归,把复杂问题层层转化为与原问题类似的小问题,函数在调用自身的情况下存在不合法递归(即无限次的递归导致栈溢出)。

    ​ 所以在使用递归的时候一定要有递归出口,否则会陷入死循环导致栈溢出!

    ​ **注:**栈结构为电脑存储的一部分,从高地址处向下开辟存储空间,与用于开辟动态存储空间的堆相向开辟(堆为从低地址出向上开辟存储空间),而函数调用将会形成栈帧,函数返回后自动释放栈帧结构,在此过程中,该函数定义的所有局部变量都在该函数的栈帧内进行空间开辟。

    样例:求n的阶乘

    int factorial(int n)
    {
    	if(n <= 1)
    		return 1;
    	else
    		return n* factorial(n-1);
    }
    

    递归与迭代

    ​ 递归在使用过程中由于频繁进行函数调用,且每次调用都需要时间成本以及空间成本,所以递归程序简单,但是可能导致递归效率变低的问题,而迭代方案通过对变量值进行替换所以不会造成栈溢出,解决了效率低下的问题。

    ​ 样例(求斐波那契数列第n个的值):

    //递归实现
    int fibrec(int n){
    	if(n<=2) retuen 1;
    	else return fibrec(n-1)+fibrec(n-2);
    }
    //迭代实现
    int fibite(int n){
    	int fir=1,sec=1,thd=2;
    	if(n<=2) return 1;
    	else{
    		while(n>2){
    			fir=sec;
    			sec=thd;
    			thd=fir+sec;
    			n--;
    		}
    		return thd;
    	}
    }
    

    数组

    一、一维数组的创建与初始化

    ​ 创建数组时数组空间为整体开辟整体释放,在内存中是连续存放,在定义时就已经确定数组大小(下标不可为0),且不可被整体赋值。在数组的创建过程中,如果进行了初始化则可不指定数组的大小,多维数组按照一维数组进行理解。

    数组传参发生降维,降维成指向其(数组)内部元素类型的指针。

    ​ 数组名一般情况下都指的是首元素的地址,但如果sizeof()单独出现以及&后跟数组名时表示的是整个数组

    int s[5];
    //表示数组首元素地址
    printf("%d\n", sizeof(s+1));//结果为4
    //表示整个数组
    printf("%d\n", sizeof(s));//结果为5
    

    二、数组传参(函数)

    ​ 由于在传参过程中如果拷贝整个指针会导致效率大大降低甚至导致栈溢出,所以数组传参要发降维问题,函数内数组作为参数时,实参为首元素地址,形参为指针。

    ​ 在访问结构体成员时也同样要发生类似的操作,用指向结构体的指针来指代结构体。

    typedef struct node{
     int a;
     int b;
    }point;
    
    void pop(int* p){
    	
    }
    
    int main(){
    	point a;
    	int* p=a;
    	pop(p);
    	return 0;
    }
    

    ​ 传参样例:

    //用数组的形式传递参数,不需要指定参数的大小,传的只是数组首元素的地址。
    void test(int arr[])
    {}
    
    //也可以传入数组大小
    void test(int arr[10])
    {}
    
    //数组降维,用指针进行接收,传的是数组首元素的地址
    void test(int *arr)
    {}
    
    //二维数组传参,第一个方括号可以空,但是第二个不可以空
    void test(int arr[][5])
    {}
    
    void test(int arr[4][5])
    {}
    
    //传过去的是二维数组的数组名,即数组首元素的地址,也就是第一行的地址,第一行也是个数组,用一个数组指针接收(比较少用)
    void test(int (*arr)[5])
    {}
    

    三、字符数组

    char a[]={'a','x','d'};
    //此处由于结尾没有'\0',strlen的机制是遇到'\0'即停止,所以在结尾没有'\0'时为随机数
    //strlen(a)为随机数
    //sizeof(a)为3
    
    char a[]={'a','x','d','\0'};
    //strlen(a)为3
    //sizeof(a)为4
    
    char* a="axd";//或char a[]="axd"
    //直接通过""定义字符串时,会自动在结尾补'\0',不需要自行补充,但'\0'依旧会占据一个字节
    //strlen(a)为3
    //sizeof(a)为4
    
    
    char c[5]={'a', 'b', '\0', 'c', '\0'};
    printf("%s", c);//结果为ab,因为字符串结束标志位'\0'
    

    操作符

    一、运算优先级

    ​ 注:①++/–高于解引用;

    ​ ②解引用高于±*/

    ​ ③±*/高于位运算;

    ​ ④位运算高于+=、-=、/=、*=;

    %操作两边必须是整数。

    二、二进制中的操作符

    1、位运算基本介绍

    与运算:&

    ​ 同1则1,否则为0;

    或运算:|

    ​ 同0为0,否则为1

    非运算:~

    ​ 1取0 0 取1

    异或运算:^

    ​ 两者相等为0,不等为1

    移位运算操作符:<< 左移 ; >> 右移

    ​ ①**<<左移:**左边抛弃末尾补0;负数对反码的补码进行移位操作;相当于乘2;

    ​ ②**>> 右移:有符号的补符号位**,无符号的补0;相当于除以2。

    2、反码与补码

    ​ **反码:**正数的反码为原码本身,负数反码符号位不变,剩余的数字位取反;

    ​ **补码:**正数的补码为原码本身,负数的补码为反码+1 。

    三、隐式类型转换

    ​ 隐式类型转换的原因:参与计算的数据如果类型不同无法直接进行计算。

    ​ 整型提升:有符号的补符号位,无符号的补0(符号位为最外面的那位)

    ​ 样例:

    在这里插入图片描述

    ​ 例题,求循环次数

    在这里插入图片描述

    ​ 解答:

    ​ unsigned char 8位数据位,范围在0-255,所以-2(11111110)时,变成254;同理-1(11111111)时,变成255;最后减到0时,不满足循环条件,for停止。刚好173次。 (7 4 1 ==> 共(7-1)/3+1=3次,1-3=-2,即254,继续循环)
    254 251 … 5 2 ==> 共(254-2)/3+1=85次(2-3=-1,即255,继续循环)
    255 252 … 6 3 ==> 共(255-5)/3+1=85次(3-3=0,退出循环) 所以总共173次

    指针

    ​ 指针变量是个变量,指针本身是个地址,用于存放内存单元的地址。

    ​ 指针时用来存放地址的,指针类型的变量无论指向目标是什么类型,指针本身在32位平台所占大小都为4个字节,在64位平台是8个字节 。

    #include <stdio.h>
    int main()
    {
    	int a = 10;//在内存中开辟一块空间,左值为空间,右值为内容
    	int *p = &a;//type* p
    				//这里我们对变量a,取出它的地址,可以使用&操作符。
    				//将a的地址存放在p变量中,p就是一个之指针变量。
    	return 0;
    }
    

    一、指针的解引用

    ​ 1、对指针的解引用只能看到sizeof(type)个字节的数据;

    ​ 2、按字节为单位,数据有高权值和低权值之分,地址有低地址和高地址之分;

    ​ 3、数据低权值位在低地址处即为小端存储,反之则为大端存储。
    (“小小小”)

    二、野指针

    概念

    ​ 指向的位置是不可知的指针。

    规避

    ​ 1、指针在定义时就进行初始化

    ​ 2、避免指针越界(eg:注意循环时循环次数的限制);

    ​ 3、指针使用完即进行指针指向空间释放

    ​ 4、避免返回局部变量的地址;

    ​ 5、指针使用前检查其有效性

    三、指针运算

    ​ 1、指针±整数,等价于±sizeof(type)

    ​ 2、指针-指针,两指针必须同一类型,一般用于数组与字符串求其两地址间相隔的单元格数,否则无效(指针+指针为非法操作);

    ​ 3、指针的关系运算。

    ​ 4、指针和数组都可以用中括号或者解引用(二者互通)。

    四、字符指针

    1、字符指针

    在指针的类型中我们知道有一种指针类型为字符指针 char* ;

    一般使用方法

    int main()
    {
    	char ch = 'w';
    	char *pc = &ch;
    	*pc = 'w';
    	return 0;
    }
    

    用char*指针指向字符串

    int main()
    {
    	const char* pstr = "hello bit.";
    	printf("%s\n", pstr);
    	return 0;
    }
    //上述代码中把字符串hello bit.的首地址放入了指针中
    

    需注意字符串可以以字符数组的形式给出,但是此时的字符数组附有存储功能,而字符指针具有常量属性,指向的是常量区的内容,因此不可被修改,可以写作:

    const char* str="hello world";//从上图表示不可被修改
    

    ​ 也正因为这个原因 C/C++会把常量字符串存储到单独的一个内存区域,当几个指针指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块

    #include <stdio.h>
    int main()
    {
    	char str1[] = "hello world.";
    	char str2[] = "hello world.";
    	const char *str3 = "hello world.";
    	const char *str4 = "hello world.";
    	if(str1 ==str2)
    		printf("str1 and str2 are same\n");
    	else
    		printf("str1 and str2 are not same\n");
    	if(str3 ==str4)
    		printf("str3 and str4 are same\n");
    	else
    		printf("str3 and str4 are not same\n");
    	return 0;
    } 
    

    在这里插入图片描述

    2、const知识点

    ​ 在此说一下const的一些知识点:

    ​ ① const修饰的变量不能被直接修改,但是可以通过指针在进行类型强转来修改(只是可以但是完全没必要);

    ​ ② const修饰指针,表示不可以通过指针来修改所指目标;

    ​ ③ const能用则用,会很好的保护数据,

    ​ const的作用:

    ​ ① 写给编译器看,提前发现可能错误的修改;

    ​ ② 写给程序员看,提示该变量不建议修改。

    const int* p=&a;
    *p = 20; //错误,*p所指的值不可以修改
    p = &n;//正确,*p的指向可以修改
    
    int* const q = &m;
    *q = 20;//正确,此时const修饰的是q,此时q所指向的值可以进行修改
    q = &t;//错误,由于const修饰的是q,此时的q的指向不可以进行修改
    
    const int a=10; //若const a=10,编译器也会默认为a是int类型的
    int *P=(int*)&a; //注意需要强制&a前需要加int*类型强制类型转换(&a的原本类型为const int*)
    *P=12;
    

    五、指针数组

    指针数组本质上是数组,该类数组内存放的的元素是指针。

    int* arr1[10]; //整形指针的数组
    char *arr2[4]; //一级字符指针的数组
    char **arr3[5];//二级字符指针的数组
    

    六、数组指针

    1、数组指针定义

    指针数组本质上是指针,该类指针指向的是一个数组。

    example:

    int (*p)[10];
    //解释:p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个
    指针,指向一个数组,叫数组指针
    

    2、数组指针&数组

    首先需要看的是数组名与&数组名(可以等价于数组指针)之间的关系

    #include <stdio.h>
    int main()
    {
    	int arr[10] = { 0 };
    	printf("arr = %p\n", arr);
    	printf("&arr= %p\n", &arr);
    	printf("arr+1 = %p\n", arr+1);
    	printf("&arr+1= %p\n", &arr+1);
    	return 0;
    }
    

    在这里插入图片描述

    根据上面的代码我们发现,其实&arr和arr,虽然值是一样的,但是意义应该不一样的。实际上: &arr 表示的是数组的地址,而不是数组首元素的地址。(细细体会一下)本例中 &arr 的类型是: int(*)[10] ,是一种数组指针类型数组的地址+1,跳过整个数组的大小,所以 &arr+1 相对于 &arr 的差值是40

    七、数组传参,指针传参

    1、一维数组传参

    数组传参会发生降维,最终传入的是首元素的地址(指针),并利用此来访问数组内其他元素。

    #include <stdio.h>
    void test(int arr[])//ok
    {}
    void test(int arr[10])//ok
    {}
    void test(int *arr)//ok
    {}
    void test2(int *arr[20])//不ok
    {}
    void test2(int **arr)//不ok
    {}
    int main()
    {
    	int arr[10] = {0};
    	int *arr2[20] = {0};
    	test(arr);
    	test2(arr2);
    }
    

    2、二维数组传参

    二维数组传参,函数形参的设计只能省略第一个[]的数字
    因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。

    void test(int arr[3][5])//ok
    {}
    void test(int arr[][])//不ok
    {}
    void test(int arr[][5])//ok
    {}
    void test(int *arr)//不ok
    {}
    void test(int (*arr)[5])//ok
    {}
    void test(int* arr[5])//不ok
    {}
    void test(int **arr)//ok
    {}
    int main()
    {
    	int arr[3][5] = {0};
    	test(arr);
    }
    

    3、一级指针传参

    需要传入一个地址

    #include <stdio.h>
    void print(int *p, int size)
    {
    	int i = 0;
    	for(i=0; i<size; i++)
    	{
    		printf("%d\n", *(p+i));
    	}
    }
    int main()
    {
    	int arr[10] = {1,2,3,4,5,6,7,8,9};
    	int *p = arr;
    	int size = sizeof(arr)/sizeof(arr[0]);
    	//一级指针p,传给函数
    	print(p, size);
    	return 0;
    }
    

    4、二级指针传参

    #include <stdio.h>
    void test(int** ptr)
    {
    	printf("num = %d\n", **ptr);
    }
    int main()
    {
    	int n = 10;
    	int*p = &n;
    	int **pp = &p;
    	test(pp);
    	test(&p);
    	return 0;
    }
    

    八、函数指针

    1、函数指针定义

    ​ 函数指针是指指向函数而非指向对象的指针。像其他指针一样,函数指针也指向某个特定的类型(特定的函数类型)。函数类型由其返回类型以及形参表确定,而与函数名无关 。

    ​ 代码在电脑中同样占据内存空间,所以具有存储地址,而代码部分在电脑中也是不可被修改的类似字符串常量

    ​ 在函数中,函数名单独时即为函数的地址(eg:main=&main),所以在用指针调用函数时,可以直接用指针调用不需要加*

    Type (*pFunc)(datatype args);
    
        //pFunc为函数指针,Type为数据类型,参数(datatype args)可以有多个,也可以没有。
    

    使用示例

    bool max(int a, int b)
    {
    	if (a>b)
    	{
    		return a;
    	}else{
    		return b;
    	}
    }
     
    void Test()
    {
    	bool (*pFunc)(int, double);
    	pFunc = max;
    	cout << max(5, 10) << endl;
    }
    

    九、函数指针数组

    指针指向一个数组 ,数组的元素都是函数指针

    使用方法: 把几个相同类型的函数地址放到一个数组中,这个数组就是函数指针的数组。

    十、回调函数

    解释:调用库中函数,但是库中函数需要编写程序时编写一个调用函数,该库中的函数为回调函数。也就是说一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。

    ​ 回调函数必须在中间函数以及回调函数同时具备时才可以实现。

    作者:no.body
    链接:https://www.zhihu.com/question/19801131/answer/27459821
    来源:知乎
    //大佬在这个帖子里对于概念解释的很好,可以做参考!
    

    回调函数就是回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

    结构体

    1、基本定义

    ​ **注:**①结构体不可以自引用!但可以在结构体内定义该结构体类型的指针!

    ​ ②定义结构体本质是新增一种类型

    ​ ③结构体传参要传结构体地址(指针),以此提高效率。

    struct node1{
    	int  a;
    	int  b;
    };
    
    typedef struct node2 {
    	int c;
    	int d;
    }node2;//通过typedef为结构体变量定义一个别名node2,在以后使用中可以使用别名以此提高编写效率
    
    int main(){
    	struct node1 s;//用结构体定义变量
    	node2  q;//用别名定义变量
    }
    

    ​ **注:**结构体不可以自引用!但可以在结构体内定义该结构体类型的指针!

    struct Node
    {
    	int data;
    	struct Node next;
    };//错误
    
    struct Node
    {
    	int data;
    	struct Node* next;
    };//正确
    
    typedef struct Node
    {
    	int data;
    	struct Node* next;
    }Node;//正确
    

    2、结构体变量的定义和初始化

    struct Point1
    {
    	int x;
    	int y;
    }p1; //声明类型的同时定义变量p1
    struct Point p2; //定义结构体变量p2
    
    typedef struct Point2
    {
    	int x;
    	int y;
    }p;
    p p1;
    p p2;
    

    在定义结构体变量时,可以在初始化的部分定义其内容,也可以在之后定义。

    typedef struct Point2
    {
    	int x;
    	int y;
    }p;
    p p1;
    p1.x=1;
    p1.y=2;//可以直接用结构体类型的变量进行定义
    
    typedef struct Point2
    {
    	int x;
    	int y;
    }p;
    p* p2=(p*)malloc(sizeof(p));
    p2->x=1;
    p2->y=2;//定义一个指向结构体的指针并为其分配空间即可进行定义
    

    3、结构体的内存对齐(结构体的占用大小的计算)

    结构体内存空间占用的大小并不是单纯的元素相加,而是通过浪费一定量的空间来换取目标数据读取速度的加快

    计算方式:

    ① 第一个成员在与结构体变量偏移量为0的地址处。

    ② 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。

    ​ (起始偏移量要能整除该成员的对齐数)

    对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。

    ​ VS中默认的值为8

    ③ 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。

    ④ 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处(即结构体大小),结构体的整

    体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

    样例:

    struct S1
    {
        char a;
        int b;
        char C;
    };
    printf("%d\n", sizeof(struct S1));
    

    char 为1个字节, int 为4个字节;
    char a 从0偏移开始,占用一个字节;偏移量为1,接下来存放 int b,偏移量1不是对齐数4 的整数倍,所以向后继续偏移一直到4,4是对齐数4的整数倍,所以将int b存放在偏移地址为4处,占用4个字节;偏移量变为8,存放 char c ,占一个字节,偏移量9不是最大对齐数4的整数倍,所以向后继续偏移直到偏移处为12的地方。

    图示如下:

    在这里插入图片描述

    自主设置默认对齐数

    #pragma pack(a)//通过该指令可设置默认对齐数为a
    

    位断

    位段的声明和结构是类似的,有两个不同:
    1.位段的成员必须是 int、unsigned int 、signed int或者char(同属于整型家族);
    2.位段的成员名后边有一个冒号和一个数字

    struct A
    {
    int _a:2;
    int _b:5;
    int _c:10;
    int _d:30;
    };
    

    注: ① 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型;

    ② 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的;

    ③ 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段;

    ④位断不需要考虑内存对齐问题所以较为节省空间。

    总而言之, 跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在。

    枚举

    即一一列举

    enum Day//星期
    {
    	Mon,
    	Tues,
    	Wed,
    	Thur,
    	Fri,
    	Sat,
    	Sun   //最后一个不加逗号
    };
    enum Sex//性别
    {
    	MALE,
    	FEMALE,
    	SECRET
    };
    

    与宏定义相比枚举的优点
    ① 增加代码的可读性和可维护性;
    ② 和#define定义的标识符比较枚举有类型检查,更加严谨;
    ③ 防止了命名污染(封装);
    ④ 便于调试;
    ⑤ 使用方便,一次可以定义多个常量 。

    联合体

    联合也是一种特殊的自定义类型
    这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以联合也叫共用体), 联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。

    //联合类型的声明
    union Un
    {
    	char c;
    	int i;
    };
    //联合变量的定义
    union Un un;
    

    **注:**联合体需要考虑内存对齐,要求为最大内存数的整数倍。

    动态内存管理

    ​ 程序开始运行后在堆上开辟大量空间(数组之类的空间开辟在栈上进行),而在堆上开辟的空间使用完毕后需要在使用完成后由free函数进行释放,然后令指向该空间的指针指空,如果只申请不释放会造成内存泄漏问题。

    动态申请空间主要涉及三个函数:malloc函数,calloc函数,relloc函数。

    void* malloc (size_t size);
    

    ​ 只申请空间,不对空间进行初始化,传入的参数size为要开辟的空间大小;

    void* calloc (size_t num, size_t size);
    

    ​ 申请空间,与malloc唯一的不同之处在于calloc会初始化为0,传入的参数size为单个空间的大小,参数a为所需要的单个空间的数量;

    void* realloc (void* ptr, size_t size);
    

    将分配size个大小的空间,然后在调整原内存空间大小的基础上,将原来内存中的数据移动到新的空间,返回值为调整之后的内存起始位置。

    由于realloc可能会申请失败返回NULL所以不建议直接用原指针接收返回地址,正确使用方法为:

    int* ptr = (int*)malloc(100);
    int* p = NULL;
    p = realloc(ptr, 1000);
    if (p = !NULL) {
    	ptr = p;
    }
    

    内存释放操作

    int* p=(int*)malloc(100);
    ......
    free(p);
    p = NULL;
    

    柔性数组

    1、定义

    ​ 在结构体内大小为0(a[0])或空(a[])的数组(必须为结构体内最后一个元素且不能是唯一元素)

    这样可以在结构体内具有一个变长数组包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配

    的内存应该大于结构的大小,以适应柔性数组的预期大小 ,sizeof 返回的这种结构大小不包括柔性数组的内存

    2、使用方法

    typedef struct st_type
    {
    	int i;
    	int a[0];//柔性数组成员
    }type_a;
    printf("%d\n", sizeof(type_a));//输出的是4
    //初始化
    type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));
    p->i = 100;
    for(i=0; i<100; i++)
    {
    	p->a[i] = i;
    }
    free(p);
    
    展开全文
  • C语言知识点复习梳理

    千次阅读 2020-10-29 09:46:30
    C语言知识点复习梳理 C语言知识点讲完了,接下来就是做一下整理与总结,然后就会进入其他知识的学习。 本文目录如下: 基础知识。 顺序程序设计。 数据类型。 标准输入输出。 ...
  • C语言函数知识点概述

    2021-05-26 17:32:35
    (二),C语言中函数的分类1,库函数2,自定义函数(二),函数的定义,声明,调用与返回1,函数的定义2,函数的声明 (一),函数是什么? 在维基百科中对函数的定义:子程序 子程序是一个大型程序中的某部分代码,由一个...
  • 谭浩强第五版C语言程序设计知识点总结归纳 把整本书的知识点给总结和记录了以下 可为笔记 可为总结!
  • C语言指针知识点总结

    万次阅读 多人点赞 2020-05-11 09:37:26
    以前总是搞不懂指针这一章节的知识,学得非常的混乱,这可不,马上要考试了,必须火力全开呀,今天在CSDN博客上看到一篇关于指针的知识点总结,觉得受益匪浅,感触颇丰! 指针 指针的定义: 指针是一个变量,用来...
  • C语言各章节干货汇总、考研知识点归纳

    千次阅读 多人点赞 2019-12-24 16:17:15
    注意:每个人学习C的目的不同,如果你是靠C吃饭,一定要仔仔细细看清每个知识点。而我的目的是考试,所以我会把我认为干的东西总结下来。 NEXTPART LET’S BEGIN ! 1:数据两种表现形式及其运算 1.1常量 1.整形常量 ...
  • 与C++、Java相比,C语言其实很简单,但却非常重要。因为它是C++、Java的基础。不把C语言基础打扎实,很难成为程序员高手。 1、C语言的结构 先通过一个简单的例子,把C语言的基础打牢。 C语言的结构要掌握以下几...
  • C语言面试相关知识点

    2021-05-21 17:28:15
    不过,右左法则其实并不是C标准里面的内容,它是从C标准的声明规定中归纳出来的方法。C标准的声明规则,是用来解决如何创 建声明的,而右左法则是用来解决如何辩识一个声明的,两者可以说是相反的。右左法则的英文...
  • C语言知识点笔记完全整理

    千次阅读 多人点赞 2020-12-29 19:18:23
    C语言 期末复习 新手必知 C程序是一个字符序列,字符序列先被分解为称之为记号的词法元素,再根据语法规则检查这些记号组合是否合法。所以别把它想象成一门玄难的学问,它只是我们的计算机人员为了方便而制定的一种...
  • 归纳C语言基础知识回顾

    万次阅读 多人点赞 2018-05-29 10:48:07
    本章归纳C语言中疑难知识点,容易出错的用法及语法,特殊用法扩展等 目录 第一章 C语言概述 第二章 算法——程序的灵魂 第三章 数据的表现形式及其运算  3.1 数据类型及运算  3.1.1 常量和变量  3.1.2 ...
  • PAGE PAGE 1 芅C语言重要知识点 肃总体上必须清楚的: 芀1)程序结构是三种:顺序结构选择结构(分支结构)循环结构 螈2)读程序都要从main)入口,然后从最上面顺序往下读(碰到循环做循环,碰到选择做选择)有且只有一个...
  • C语言可以说是理工科大学生的必备基础知识,并且应用十分广泛,下面为大家带来C语言基础知识梳理总结,C语言零基础入门绝对不是天方夜谭! 更多免费视频教程等你领取!可以关注公众号 “C和C加加” 回复“ZXC”即可...
  • C语言指针知识点小结

    万次阅读 多人点赞 2020-01-10 15:40:06
      C语言指针基础知识点(一)–指针及指针变量   C语言指针基础知识点(二)–指针变量的引用   C语言指针基础知识点(三)–指针变量作为函数参数   C语言指针基础知识点(四)–通过指针引用数组   C语言指针...
  • C语言公共基础归纳试题及重点.pdf
  • 大一下学期学习了C语言、暑假在家没事又把C语言复习了一遍。因此,趁热打铁,把所学内容写成笔记,以便以后复习,也希望能够帮助大家学好C语言。以下内容如有错误,请大家在评论区指正,谢谢支持,共同进步。 go、go...
  • C语言重要知识点总结(一)

    千次阅读 2015-10-15 20:04:38
    C语言重要知识点总结(一)最近重新看了下C语言,发现先前学习的时候很多的重点都被忽略了,现在回头看发现了很多以前没有注意的东西,所以写博客记录下,方便以后可以参考查询。 变量的存储类别C语言中变量的存储...
  • C语言深度挖掘笔记摘要数据类型基本数据类型类型名称说明char字符类型存放字符的ASCII码int整型存放有符号整数short短整型存放有符号整数long长整型存放有符号整数long long存放有符号整数float单精度浮点型存放精度...
  • c语言入门基础知识总结

    千次阅读 2021-07-12 17:47:25
    c语言入门基础知识点有: c语言的命名;变量及赋值;基本数据类型;格式化输出语句;强制类型转换;不可改变的常量;自动类型转换。C语言是一种通用的、面向过程式的计算机程序设计语言。
  • 知识点总结: 1.所有宏定义、枚举常量、只读常量全用大写字母命名,用下划线分割单词。 2.不同类型数据之间的运算注意精度扩张问题,一般低精度向高精度扩张。 3.sizeof在计算变量的时候,括号可以省略;在计算数据...
  • 第二章 C程序设计的初步知识 1.简单C语言程序的构成和格式 #include <stdio.h> //stdio.h中i表示input,o代表output,h代表head; int main() //int函数的返回值类型 main()主函数,是程序执行的入口地址 ...
  • C语言是计算机语言中比较基础也是比较难的一门语言,因此在学习C语言之前我们先了解一些关于计算机的基础知识。计算机基础知识计算机发展到现在也有很长的一段时间了,自1946年美国宾夕法尼亚大学研制成第一台计算机...
  • C语言数组知识点

    2018-11-29 22:12:27
    1.数组中每一个元素都属于同一个数据类型。 2.数组下标从0开始,最大下标比数组数据长度小。 3.C语言不允许对数组大小作动态定义。 4.引用数组元素只是引用...9.ASCII代码也属于整数形式,故字符型也归纳为整型类...
  • C语言知识点总结(完美版)

    千次阅读 多人点赞 2020-02-02 21:24:24
    C语言重要知识点 总体上必须清楚的: 1)程序结构是三种: 顺序结构 、选择结构(分支结构)、循环结构。 2)读程序都要从main()入口, 然后从最上面顺序往下读(碰到循环做循环,碰到选择做选择),有且只有一个main...
  • 计算机二级C语言第1章 数据结构与算法1.1.1算法的基本概念所谓算法是指解方案的准确而完整的描述1. 算法的基本特征 1)可行性 2)确定性 3)有穷性 4)拥有足够的情报 5)2. 算法的基本要素 1)算法中对数据的运算的...
  • 总体上必须清楚的 1)程序结构是三种: 顺序结构 , 循环结构(三个循环结构), 选择结构(if 和 switch) 2)读程序都要从main()入口, 然后从最上面顺序往下读(碰到循环做循环,碰到选择做... 考试注意: 括号在这里的重要性。
  • 二级C语言程序设计(知识点)二级C语言程序设计(知识总结)公共基础知识部分:第一章:数据结构与算法1.1算法算法:指解题方案的准确而完整的描述。算法的可解:对于一个问题,如果可以通过一个计算机程序,在有限的...
  • 原标题:嵌入式学习笔记:C语言重点知识整理重点理解掌握部分:运算符、数据类型、强制转换除法 /(例8/7==1)取余%(两数必须为整形)不同精度的多个数运算,结果精度与最高精度一致精度问题常用%d(十进制整形) 、%s...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,262
精华内容 2,104
关键字:

c语言重点知识归纳