一、概述

最近家里有点事,趁在家的这段时间,温习一下C言语中心常识,后的底层开发、音视频开发、跨渠道开发、算法等方向的进一步研究与学习埋下伏笔

本篇文章接着上一篇继续对C言语的中心语法常识进行温习

二、C 言语中心语法|函数

函数

1、函数的分类

前面已经说过,C言语中的函数便是面向目标中的”办法”,C言语的函数能够大概分为3类:

  • 主函数:
    • 也便是main函数。每个程序中只能有一个、也有必要有一个主函数。不管主函数写在什么方位,C程序总是从主函数开端履行
  • 自界说函数:
    • 开发人员自界说的函数,可有可无,数目不限
  • 库函数:
    • C言语提供的库函数,例如stdio.h中的输出函数printf()和输入函数scanf()

2、函数的声明

函数的声明

  • 函数声明会告诉编译器函数称号及怎么调用函数。函数的实践主体能够独自界说。
  • 函数声明包括以下几个部分:
    return_type function_name( parameter list );
    
  • 针对上面界说的函数 max(),以下是函数声明:
    int max(int num1, int num2);
    
  • 在函数声明中,参数的称号并不重要,只要参数的类型是必需的,因而下面也是有用的声明:
    int max(int, int);
    
  • 当您在一个源文件中界说函数且在另一个文件中调用函数时,函数声明是必需的。在这种状况下,您应该在调用函数的文件顶部声明函数。

3、函数的界说

  • C 言语中的函数界说的一般办法如下:
    return_type function_name( parameter list )
    {
       body of the function
    } 
    
  • 在 C 言语中,函数由一个函数头和一个函数主体组成。下面列出一个函数的一切组成部分:
    • 回来类型: 一个函数能够回来一个值。return_type 是函数回来的值的数据类型。有些函数履行所需的操作而不回来值,在这种状况下,return_type 是关键字 void
    • 函数称号: 这是函数的实践称号。函数名和参数列表一起构成了函数签名。
    • 参数: 参数就像是占位符。当函数被调用时,您向参数传递一个值,这个值被称为实践参数。参数列表包括函数参数的类型、次序、数量。参数是可选的,也便是说,函数或许不包括参数。
    • 函数主体: 函数主体包括一组界说函数履行使命的句子。
    • 比方:
      /* 函数回来两个数中较大的那个数 */
      int max(int num1, int num2) 
      {
         /* 部分变量声明 */
         int result;
         if (num1 > num2)
            result = num1;
         else
            result = num2;
         return result; 
      } 
      
  • 规范C言语中,函数的界说次序是有考究的
  • 默许状况下,只要后边界说的函数才能够调用前面界说过的函数
    • 第5行界说的main函数调用了第1行的sum函数,这是合法的。
    • 假如调换下sum函数和main函数的次序,在规范的C编译器环境下是不合法的(不过在Xcode中仅仅警告,Xcode中用的是Clang编译器)
    int sum(int a, int b) {
      return a + b;
    }
    int main() {
         int c = sum(1, 4);
         return 0;
    }
    
  • 假如想把其他函数的界说写在main函数后边,并且main函数能正常调用这些函数,那就有必要在main函数前面作一下函数的声明
    // 仅仅做个函数声明,并不必完成
    int sum(int a, int b);
    int main() {
        int c = sum(1, 4);
        return 0;
    }
    // 函数的界说(完成)
    int sum(int a, int b) {
        return a + b;
    }
    
  • 咱们在第2行做了sum函数的声明,然后在第6行(main函数中)就能够正常调用sum函数了。
  • 函数的声明格局:
    回来值类型  函数名 (参数1, 参数2, ...)
    
  • 能够省掉参数称号,比方上面的sum函数声明能够写成这样:
    int sum(int, int);
    
  • 只要你在main函数前面声明过一个函数,main函数就知道这个函数的存在,就能够调用这个函数。究竟这个函数是做什么用,还要看函数的界说。假如只要函数的声明,而没有函数的界说,那么程序将会在链接时出错。
  • 在大型的C程序中,为了分模块进行开发,一般会将函数的声明和界说(即完成)分别放在2个文件中,函数声明放在.h头文件中,函数界说放在.c源文件中

4、函数的形参和实参

函数参数

  • 在界说函数时,函数名后边的()中界说的变量称为办法参数(形参);在调用函数时传入的值称为实践参数(实参)。
  • 形参就像函数内的其他部分变量,在进入函数时被创立,退出函数时被毁掉。
    // b是test函数的形参(办法参数)
    void test(int b) 
    {
        b = 9; // 改动了形参b的值
    }
    int main()
    {
        int a = 10;
        printf("函数调用前的a:%d\n", a);
        test(a); // a是test函数的实参(实践参数)
        printf("函数调用后的a:%d", a);
        return 0;
    }
    

假如是根本数据类型作为函数的形参,那是简略的值传递,将实参a的值赋值给了形参b,相当于

int a = 10;
int b = a;
b = 9;

a和b是分别有着不同内存地址的2个变量,因而改动了形参b的值,并不会影响实参a的值。

上述代码的输出成果为:

03-复习C语言核心知识|函数、作用域规则、数组、枚举、字符与字符串、指针

调用类型 描绘
传值调用 该办法把参数的实践值仿制给函数的办法参数。在这种状况下,修改函数内的办法参数不会影响实践参数。
引证调用 经过指针传递办法,形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作。

5. 函数的调用

//
//  main.c
//  05-函数
//
//  Created by VanZhang on 2022/5/16.
//
#include <stdio.h>
//函数声明
int max(int num1, int num2);
int main(int argc, const char * argv[]) {
    //找出函数中最大值
    printf("找出函数中最大值,%d \n",max(66,88));
    return 0;
}
//界说
int max(int num1, int num2) {
    return (num1 > num2) ? num1 : num2;//三目运算
}

输出:

找出函数中最大值,88

6. printf函数

这是在stdio.h中声明的一个函数,因而运用前有必要参加#include <stdio.h>,运用它能够向规范输出设备(比方屏幕)输出数据

6.1 用法

1> printf(字符串)

printf("Hello, World!");

输出成果是:

03-复习C语言核心知识|函数、作用域规则、数组、枚举、字符与字符串、指针

2> printf(字符串, 格局符参数)

// 运用常量作参数
printf("My age is %d\n", 26);
// 也能够运用变量
int age = 17;
printf("My age is %d", age);
  • 格局符%d表明以有符号的十进制办法输出一个整型,格局符参数中的26和age会替代%d的方位。
  • 第2行代码中的\n是个转义字符,表明换行,所以输出了榜首句”My age is 26″后会先换行,再输出”My age is 27″

输出成果:

03-复习C语言核心知识|函数、作用域规则、数组、枚举、字符与字符串、指针

  • 假如去掉第2行中的\n,将会是这样的效果

输出成果:

03-复习C语言核心知识|函数、作用域规则、数组、枚举、字符与字符串、指针

总结:左面字符串中格局符的个数 有必要跟 右边格局符参数的个数相同;格局符的类型决定了格局符参数的类型,比方运用%d,说明对应的格局符参数有必要是整型。

再举个比方:

printf("My age is %d and no is %d", 27, 1);

输出成果:

03-复习C语言核心知识|函数、作用域规则、数组、枚举、字符与字符串、指针

6.2 常用的格局符及其意义

03-复习C语言核心知识|函数、作用域规则、数组、枚举、字符与字符串、指针

6.3 格局符还能够添加一些精密的格局操控

1> 输出宽度

  • 咱们先看看默许的整型输出
    printf("The price is %d.", 14);
    
  • 输出成果(留意,后边是有个点的):
  • 假如我把%d换成%4d:
    printf("The price is %4d.", 14);
    

输出成果:

,你会发现”is”跟”14″的间隔被拉开了

%4d的意思是输出宽度为4,而”14″的宽度为2,因而多出2个宽度,多出的宽度就会在左面用空格添补,因而你会看到”14″左面多了2个空格;假如实践数值宽度比较大,比方用%4d输出宽度为6的”142434″,那就会按照实践数值宽度6来输出。

printf("The price is %4d.", 142434);

输出成果:

03-复习C语言核心知识|函数、作用域规则、数组、枚举、字符与字符串、指针

,”142434″的输出宽度为6

  • 假如换成%-4d
printf("The price is %-4d.", 14);

输出成果:

03-复习C语言核心知识|函数、作用域规则、数组、枚举、字符与字符串、指针

,你会发现”14″跟”.”的间隔被拉开了

%-4d表明输出宽度为4,假如比实践数值宽度大,多出的宽度会在右边用空格添补;假如4比实践数值宽度小,就按照实践数值的宽度来输出

6.4 浮点数的小数位数

  • 咱们先看下默许的浮点数输出
    printf("My height is %f", 179.95f);
    

输出成果:

03-复习C语言核心知识|函数、作用域规则、数组、枚举、字符与字符串、指针

,默许是输出6位小数

*假如只想输出2位小数,把%f换成%.2f即可 C printf("My height is %.2f", 179.95f);

输出成果:

03-复习C语言核心知识|函数、作用域规则、数组、枚举、字符与字符串、指针

  • 当然,能够一起设置输出宽度和小数位数
    printf("My height is %8.1f", 179.95f);
    

输出成果:

,输出宽度为8,保存1位小数

7.scanf函数

这也是在stdio.h中声明的一个函数,因而运用前有必要参加#include <stdio.h>。调用scanf函数时,需求传入变量的地址作为参数,scanf函数会等候规范输入设备(比方键盘)输入数据,并且将输入的数据赋值给地址对应的变量

7.1 简略用法

printf("Please input your age:");
int age;
scanf("%d", &age);
printf("Your age is %d.", age);
  • 运行程序,履行完第1行代码,操控台会输出一句提示信息:
    03-复习C语言核心知识|函数、作用域规则、数组、枚举、字符与字符串、指针
  • 履行到第4行的scanf函数时,会等候用户的键盘输入,并不会往后履行代码。scanf的第1个参数是”%d”,说明要求用户以10进制的办法输入一个整数。这儿要留意,scanf的第2个参数传递的不是age变量,而是age变量的地址&age,&是C言语中的一个地址运算符,能够用来获取变量的地址。
  • 接着咱们能够在提示信息后边输入个8:
    03-复习C语言核心知识|函数、作用域规则、数组、枚举、字符与字符串、指针
    • (由于Xcode本身的问题,咱们只能在操控台输入宽度为1的数据,假如想输入宽度大于1的数据,比方输入27,能够从别的当地仿制个27,再粘贴到操控台)
  • 输入完毕后,敲一下回车键,意图是告诉scanf函数咱们已经输入完毕了,scanf函数会将输入的8赋值给age变量
  • scanf函数赋值完毕后,才会往后履行代码,履行到第6行时,操控器会输出:
    03-复习C语言核心知识|函数、作用域规则、数组、枚举、字符与字符串、指针

7.2 其他用法

7.2.1 用scanf函数接收3个数值,在这儿,每个数值之间用中划线-离隔
1 int a, b, c;
2 scanf("%d-%d-%d", &a, &b, &c);
3 
4 printf("a=%d, b=%d, c=%d", a, b, c);
  • 留意第2行,3个%d之间是用中划线-离隔的,因而咱们在每输入一个整数后都有必要加个中划线-,比方这样输入 ,不然在给变量赋值的时分会出问题
  • 一切的数值都输入完毕后敲回车键,scanf函数会依次给变量a、b、c赋值,接着输出

留意:数值之间的分隔符是恣意的,不一定要用中划线-,能够是逗号、空格、星号*、井号#等等,乃至是英文字母

// 逗号,
scanf("%d,%d,%d", &a, &b, &c); // 输入格局:10,14,20
// 井号#
scanf("%d#%d#%d", &a, &b, &c); // 输入格局:10#14#20
// 字母x
scanf("%dx%dx%d", &a, &b, &c); // 输入格局:10x14x20
7.2.2用scanf函数接收3个数值,每个数值之间用空格离隔
1 int a, b, c;
2 scanf("%d %d %d", &a, &b, &c);
3 
4 printf("a=%d, b=%d, c=%d", a, b, c);
  • 留意第2行,3个%d之间是用空格离隔的,咱们在每输入一个整数后有必要输入一个分隔符,分隔符能够是空格、tab、回车
  • 用空格做分隔符
    03-复习C语言核心知识|函数、作用域规则、数组、枚举、字符与字符串、指针
  • 用tab做分隔符
    03-复习C语言核心知识|函数、作用域规则、数组、枚举、字符与字符串、指针
  • 用回车做分隔符
    03-复习C语言核心知识|函数、作用域规则、数组、枚举、字符与字符串、指针

三、C 言语中心语法|变量效果域

1. 效果域 规矩

任何一种编程中,效果域是程序中界说的变量所存在的区域,超越该区域变量就不能被拜访。C 言语中有三个当地能够声明变量:

  1. 在函数或块内部的部分变量
  2. 在一切函数外部的大局变量
  3. 办法参数的函数参数界说中

让咱们来看看什么是部分变量、大局变量和办法参数。

部分变量

在某个函数或块的内部声明的变量称为部分变量。它们只能被该函数或该代码块内部的句子运用。部分变量在函数外部是不可知的。下面是运用部分变量的实例。在这儿,一切的变量 a、b 和 c 是 main() 函数的部分变量。

void main(){
    //部分变量
    int a, b;
    int c;
    //初始化部分变量
    a = 10;
    b = 20;
    c = a + b;
    //%d:以十进制办法输出带符号整数(正数不输出符号)
    printf("values of a = %d,b = %d and c = %d \n", a, b, c);
} 

输出:

values of a = 10,b = 20 and c = 30

大局变量

大局变量是界说在函数外部,通常是在程序的顶部。大局变量在整个程序生命周期内都是有用的,在恣意的函数内部能拜访大局变量。

大局变量能够被任何函数拜访。也便是说,大局变量在声明后整个程序中都是可用的。下面是运用大局变量和部分变量的实例:

//大局变量声明
int g;
void main(){
   int a, b;
    //初始化部分变量
    a = 10;
    b = 20;
   //悉数变量赋值
    g = a + c;
    printf("values of a = %d,bc = %d and g = %d \n", a, c, g);
}

输出:

values of a = 10,bc = 30 and g = 40

办法参数

函数的参数,办法参数,被当作该函数内的部分变量,假如与大局变量同名它们会优先运用。下面是一个实例:

int sumA(int a, int b) {
    printf("value of a in sum() = %d\n", a);
    printf("value of b in sum() = %d\n", b);
    return x + y;
}
void main(){
  int a, b,c;
    //初始化部分变量
    a = 10;
    b = 20;
  c = sumA(a, b);
  printf("value of c in main() = %d\n", c);
} 

输出:

value of a in main() = 30

大局变量和部分变量的区别

  • 大局变量保存在内存的大局存储区中,占用静态的存储单元;
  • 部分变量保存在栈中,只要在所在函数被调用时才动态地为变量分配存储单元。

初始化部分变量和大局变量的默许值

数据类型 初始化默许值
int 0
char ‘\0’
float 0
double 0
pointer NULL

四、C 言语中心语法|数组与枚举

1. 数组

为了让咱们更好地学习和了解数组,咱们先来知道一下内存中的”地址”。

1.1 地址

  1. 核算机中的内存是以字节为单位的存储空间
    • 内存的每一个字节都有一个仅有的编号,这个编号就称为地址。
    • 凡寄存在内存中的程序和数据都有一个地址
    • 也便是说,一个函数也有自己的内存地址。
  2. 当界说一个变量时,体系就分配一个带有仅有地址的存储单元来存储这个变量。比方:
    char a = 'A'; // A的ASCII值为65
    int b = 66;
    
    • 假设是在16bit环境下,体系为a、b分别分配1个字节、2个字节的存储单元。变量存储单元的榜首个字节的地址便是该变量的地址
      03-复习C语言核心知识|函数、作用域规则、数组、枚举、字符与字符串、指针
    • 能够看出,变量a的地址是ffc3;变量b的地址是ffc1。内存中存储的都是2进制数据。
  3. 在调试过程中,咱们采纳打印的办法查看变量的地址:
    int c = 10;
    // 以16进制办法输出地址
    printf("16进制:%x\n", &c);
    // 以10进制办法输出地址
    printf("10进制:%d", &c);
    

输出成果:

03-复习C语言核心知识|函数、作用域规则、数组、枚举、字符与字符串、指针

1.2 C言语数组

C 言语支撑数组数据结构

  • 它能够存储一个固定巨细的相同类型元素次序调集
  • 数组是用来存储一系列数据,但它往往被认为是一系列相同类型的变量。
  • 数组的声明并不是声明一个个独自的变量
    • 比方 number0、number1、…、number99,
  • 而是声明一个数组变量
    • 比方 numbers,然后运用 numbers[0]、numbers[1]、…、numbers[99] 来代表一个个独自的变量。数组中的特定元素能够经过索引拜访。
    • 一切的数组都是由接连的内存方位组成。最低的地址对应榜首个元素,最高的地址对应最终一个元素。

1.3 声明数组

在 C 中要声明一个数组,需求指定元素的类型和元素的数量,如下所示:

type arrayName [ arraySize ];

这叫做一维数组。arraySize 有必要是一个大于零的整数常量,type 能够是恣意有用的 C 数据类型。例如,要声明一个类型为 double 的包括 10 个元素的数组 balance,声明句子如下:

double balance[10];
  • 数组的界说的办法为:类型 数组名[元素个数]
    int a[5];
    
  • []只能放在数组名的后边,下面的都是过错写法:
    int[5] a; // 过错
    int[] b; // 过错
    
  • []里边的个数有必要是一个固定值,能够是常量(比方6、8)、常量表达式(比方3+4、5*7)。绝对不能运用变量或者变量表达式来表明元素个数,大多数状况下不要省掉元素个数(当数组作为函数的形参和数组初始化时除外)
    • 下面的都是正确写法:
      int  a[5];   // 整型常量
      int  b['A'];  // 字符常量,其实便是65
      int  c[3*4];  // 整型常量表达式
      
    • 下面的都是过错写法:
      int a[]; // 没有指定元素个数,过错
      int i = 9;
      int a[i]; // 用变量做元素个数,过错
      

1.4 一维数组的存储

界说数组时,体系将按照数组类型和个数分配一段接连的存储空间来存储数组元素,如int a[3]占有了接连的6字节存储空间(在16位环境下,一个int类型占用2个字节)。要留意的是,数组名代表着整个数组的地址,也便是数组的开端地址。

03-复习C语言核心知识|函数、作用域规则、数组、枚举、字符与字符串、指针

留意:其实a不算是变量,是个常量,它代表着数组的地址。上图把a放到变量一栏是为了方便咱们了解数组结构。

数组a的地址是ffc1,a[0]的地址是ffc1,a[1]的地址是ffc3,a[2]的地址是ffc5。因而a == &a[0],即榜首个元素的地址便是整个数组的地址

1.5 一维数组的初始化

  • 初始化的一般办法是:类型 数组名[元素个数] = {元素1, 元素2, …};
    int a[2] = {8, 10};
    
    • 其实相当于:
      int a[2];
      a[0] = 8;
      a[1] = 10;
      
  • 留意的是:C言语中编译器是不会对数组下标越界进行查看的,所以自己拜访数组元素时要当心
  • 元素值列表能够是数组一切元素的初值,也能够是前面部分元素的初值
    int a[4] = {2, 5};
    
  • 当数组为整型时,初始化未确定初值的元素,默许为0,所以上面的a[2]、a[3]都为0
  • 当对悉数数组元素都赋初值时,能够省掉元素个数
    int a[] = {2, 5, 7};
    
  • 说明数组a的元素个数是3
  • 数组初始化时的赋值办法只能用于数组的界说,界说之后只能一个元素一个元素地赋值
    下面的写法是过错的:
    1 int a[3];
    2 a[3] = {1, 2, 3}; // 过错
    3 a = {1, 2, 3}; // 过错
    

其实为什么是过错的写法呢?咱们能够简要剖析一下。

  • 1> 第2行的a[3]代表着拜访数组的第4个元素,首先这儿已经是数组下标越界了;就算没有越界,给a[3]赋值时也应该赋一个int类型的整数,不应该是{}。
  • 2> 第3行的a是数组名,代表着数组的地址,它是个常量!给常量赋值,那必定错了!

1.6 拜访数组元素

double value = balance[1]

比方:

//
//  main.c
//  06-数组、枚举、指针
//
//  Created by VanZhang on 2022/5/17.
//
#include <stdio.h>
//数组
void test1(int arrLength) {
//    type arrayName[ arraySize ] = {元素1,元素2...};
    int listLength = arrLength>0?arrLength:10;
//    int listLength = arrLength?:10;
    //界说两个长度为 10 的整数数组
    int xPoint[listLength], yPoint[listLength];
    //初始化数组元素
    for (int i = 0; i < listLength; i++) {
        xPoint[i] = 2 * i;
        yPoint[i] = 2 * i -1;
    }
    //核算数组的: 元素个数 = 数组总内存巨细/单个元素巨细  (由于一切元素都是相同类型的!!!)
    //总的巨细除以其间一个巨细就得到了 数组长度
    unsigned long   count  = sizeof(xPoint)/sizeof(xPoint[0]);
    printf("整数数组 n 的长度: %lu \n", count);
    //输出元素中的数据
    for (int k = 0; k < count; ++k) {
        printf("Element[%d]]=(%d,%d)\n",k,xPoint[k],yPoint[k]);
    }
}
int main(int argc, const char * argv[]) {
    test1(20); 
    return 0;
}

输出:

整数数组 n 的长度: 20
Element[0]]=(0,-1)
Element[1]]=(2,1)
Element[2]]=(4,3)
Element[3]]=(6,5)
Element[4]]=(8,7)
Element[5]]=(10,9)
Element[6]]=(12,11)
Element[7]]=(14,13)
Element[8]]=(16,15)
Element[9]]=(18,17)
Element[10]]=(20,19)
Element[11]]=(22,21)
Element[12]]=(24,23)
Element[13]]=(26,25)
Element[14]]=(28,27)
Element[15]]=(30,29)
Element[16]]=(32,31)
Element[17]]=(34,33)
Element[18]]=(36,35)
Element[19]]=(38,37)
Program ended with exit code: 0

1.7 C 中数组详解

在 C 中,数组是非常重要的,咱们需求了解更多有关数组的细节。下面列出了 C 程序员有必要清楚的一些与数组相关的重要概念:

概念 描绘
多维数组 C 支撑多维数组。多维数组最简略的办法是二维数组。
传递数组给函数 您能够经过指定不带索引的数组称号来给函数传递一个指向数组的指针。
从函数回来数组 C 答应从函数回来数组。
指向数组的指针 您能够经过指定不带索引的数组称号来生成一个指向数组中榜首个元素的指针。

1.8 一维数组与函数参数

假如忘记了实参形参的意思,能够回看前文中 关于函数的篇幅 提及的 实参形参

*一维数组的元素作为函数实参,与同类型的简略变量作为实参相同,是单向的值传递,即数组元素的值传给形参,形参的改动不影响实参 “`C // b是test函数的形参(办法参数) void test(int b) { b = 9; }

int main()
{
    int a[3];
    a[0] = 10;
    printf("函数调用前的a[0]:%d\n", a[0]);
    test(a[0]); // a[0]是test函数的实参(实践参数)
    printf("函数调用后的a[0]:%d", a[0]);
    return 0;
}
```

输出成果:

03-复习C语言核心知识|函数、作用域规则、数组、枚举、字符与字符串、指针

  • 咱们都知道,数组名代表着整个数组的地址
    • 假如一维数组的姓名作为函数实参,传递的是整个数组,,
    • 即形参数组和实参数组彻底等同,是寄存在同一存储空间的同一个数组。
    • 这样形参数组修改时,实参数组也一起被修改了。形参数组的元素个数能够省掉。
    // b是test函数的形参(办法参数)
    void test(int b[]) { // 也能够写int b[3]
        b[0] = 9;
    }
    int main() {
        int a[3];
        a[0] = 10;
        printf("函数调用前的a[0]:%d\n", a[0]);
        test(a); // a是test函数的实参(实践参数)
        printf("函数调用后的a[0]:%d", a[0]);
        return 0;
    }
    

输出成果

03-复习C语言核心知识|函数、作用域规则、数组、枚举、字符与字符串、指针

2. 二维数组

2.1 二维数组的界说

界说办法:类型 数组名[行数][列数]

int a[2][3]; // 共2行3列,6个元素

2.2 二维数组的存储

  • C言语把二维数组当作是一维数组的调集,即二维数组是一个特殊的一维数组:
    • 它的元素是一维数组。例如int a[2][3]能够看作由一维数组a[0]和一维数组a[1]组成
    • 这两个一维数组都包括了3个int类型的元素
      03-复习C语言核心知识|函数、作用域规则、数组、枚举、字符与字符串、指针
  • 二维数组的寄存次序是按行寄存的,先寄存榜首行的元素,再寄存第2行的元素。
    • 例如int a[2][3]的寄存次序是:a[0][0] → a[0][1] → a[0][2] → a[1][0] → a[1][1] → a[1][2]
  • 再来看看在内存中的存储状况,例如int a[2][2]
    03-复习C语言核心知识|函数、作用域规则、数组、枚举、字符与字符串、指针
    • (留意:a[0]、a[1]也是数组,是一维数组,并且a[0]、a[1]便是数组名,因而a[0]、a[1]就代表着这个一维数组的地址)
    • 1> 数组a的地址是ffc1,数组a[0]的地址也是ffc1,即a = a[0];
    • 2> 元素a[0][0]的地址是ffc1,所以数组a[0]的地址和元素a[0][0]的地址相同,即a[0] = &a[0][0];
    • 3> 最终能够得出结论:a = a[0] = &a[0][0],以此类推,能够得出a[1] = &a[1][0]

3. 二维数组的初始化

  • 按行进行初始化
    int a[2][3] = { {2, 2, 3}, {3, 4, 5} };
    
  • 按存储次序进行初始化(先寄存第1行,再寄存第2行)
    int a[2][3] = {2, 2, 3, 3, 4, 5};
    
  • 对部分元素进行初始化
    int a[2][3] = { {2}, {3, 4} };
    int b[3][3] = { { }, { , , 2}, {1, 2, 3}};
    
  • 假如只初始化了部分元素,能够省掉行数,可是不能够省掉列数
    int a[][3] = {1, 2, 3, 4, 5, 6};
    int a[][3] = {{1, 2, 3}, {3, 5}, {}};
    
  • 有些人或许想不明白,为什么能够省掉行数,但不能够省掉列数。也有人或许会问,可不能够只指定行数,可是省掉列数?
    • 其实这个问题很简略,假如咱们这样写:
    int a[2][] = {1, 2, 3, 4, 5, 6}; // 过错写法
    

咱们都知道,二维数组会先寄存第1行的元素,由于不确定列数,也便是不确定第1行要寄存多少个元素,所以这儿会发生许多种状况,或许1、2是归于第1行的,也或许1、2、3、4是榜首行的,乃至1、2、3、4、5、6悉数都是归于第1行的

三维乃至更多维的数组就不再提及了,咱们以此类推。

3.1 二维数组:平面坐标

//二维数组「平面坐标」【大数组 内部 包括若干小数组】
void test2(int arrLength) {
//    type arrayName[ arraySize ] = {元素1,元素2...};
    int listLength = arrLength>0?arrLength:10;
//    int listLength = arrLength?:10;
    //界说两个长度为 10 的整数数组
//    int xPoint[listLength], yPoint[listLength];
    int xyPoints[listLength][2];
    //初始化数组元素
//    for (int i = 0; i < listLength; i++) {
//        xPoint[i] = 2 * i;
//        yPoint[i] = 2 * i -1;
//    }
    int x,y;
    for (int i = 0; i < listLength; i++) {
        x = 2*i;
        y = 2*i-1;
        printf("i:%d------(x:%d,y:%d)\n",i,x,y);
        for (int j = 0; j < 2; j++) {
            printf("j:%d\n",j);
            xyPoints[i][j] = (j==0)?x:y;
        }
    }
    //核算数组的: 元素个数 = 数组总内存巨细/单个元素巨细  (由于一切元素都是相同类型的!!!)
    //总的巨细除以其间一个巨细就得到了 数组长度
//    unsigned long   count  = sizeof(xPoint)/sizeof(xPoint[0]);
    unsigned long   count1  = sizeof(xyPoints)/sizeof(xyPoints[0]);
    unsigned long   count2  = sizeof(xyPoints[0])/sizeof(xyPoints[0][0]);
    printf("数组榜首维度的长度: %lu \n", count1);
    printf("数组第二维度的长度: %lu \n", count2);
    //输出元素中的数据
//    for (int k = 0; k < count; ++k) {
//        printf("Element[%d]]=(%d,%d)\n",k,xPoint[k],yPoint[k]);
//    }
    for (int k = 0; k < count1; ++k) {
        printf("Element[%d]=(x:%d,y:%d)\n",k,xyPoints[k][0],xyPoints[k][1]);
    }
}
int main(int argc, const char * argv[]) {
//    test1(20);
  test2(20); 
    return 0;
}

输出:

i:0------(x:0,y:-1)
j:0
j:1
i:1------(x:2,y:1)
j:0
j:1
i:2------(x:4,y:3)
j:0
j:1
i:3------(x:6,y:5)
j:0
j:1
i:4------(x:8,y:7)
j:0
j:1
i:5------(x:10,y:9)
j:0
j:1
i:6------(x:12,y:11)
j:0
j:1
i:7------(x:14,y:13)
j:0
j:1
i:8------(x:16,y:15)
j:0
j:1
i:9------(x:18,y:17)
j:0
j:1
i:10------(x:20,y:19)
j:0
j:1
i:11------(x:22,y:21)
j:0
j:1
i:12------(x:24,y:23)
j:0
j:1
i:13------(x:26,y:25)
j:0
j:1
i:14------(x:28,y:27)
j:0
j:1
i:15------(x:30,y:29)
j:0
j:1
i:16------(x:32,y:31)
j:0
j:1
i:17------(x:34,y:33)
j:0
j:1
i:18------(x:36,y:35)
j:0
j:1
i:19------(x:38,y:37)
j:0
j:1
数组榜首维度的长度: 20 
数组第二维度的长度: 2 
Element[0]=(x:0,y:-1)
Element[1]=(x:2,y:1)
Element[2]=(x:4,y:3)
Element[3]=(x:6,y:5)
Element[4]=(x:8,y:7)
Element[5]=(x:10,y:9)
Element[6]=(x:12,y:11)
Element[7]=(x:14,y:13)
Element[8]=(x:16,y:15)
Element[9]=(x:18,y:17)
Element[10]=(x:20,y:19)
Element[11]=(x:22,y:21)
Element[12]=(x:24,y:23)
Element[13]=(x:26,y:25)
Element[14]=(x:28,y:27)
Element[15]=(x:30,y:29)
Element[16]=(x:32,y:31)
Element[17]=(x:34,y:33)
Element[18]=(x:36,y:35)
Element[19]=(x:38,y:37)
Program ended with exit code: 0

3.2 二维数组:空间坐标

//二维数组「三维空间坐标」
void test3(int arrLength) {
        int listLength = arrLength>0?arrLength:10;
    //    int listLength = arrLength?:10;
        //界说两个长度为 10 的整数数组
    //    int xPoint[listLength], yPoint[listLength];
        int xyzPoints[listLength][3];
        //初始化数组元素
    //    for (int i = 0; i < listLength; i++) {
    //        xPoint[i] = 2 * i;
    //        yPoint[i] = 2 * i -1;
    //    }
        int x,y,z;
        int value;
        for (int i = 0; i < listLength; i++) {
            x = 2*i;
            y = 2*i-1;
            z = 2*i+1;
            printf("i:%d------(x:%d,y:%d,z:%d)\n",i,x,y,z);
            for (int j = 0; j < 3; j++) {
                printf("j:%d\n",j);
                if (j==0) {
                    value = x;
                }else  if (j==1) {
                    value = y;
                }else{
                    value = z;
                }
                xyzPoints[i][j]= value;
            }
        }
        //核算数组的: 元素个数 = 数组总内存巨细/单个元素巨细  (由于一切元素都是相同类型的!!!)
        //总的巨细除以其间一个巨细就得到了 数组长度
    //    unsigned long   count  = sizeof(xPoint)/sizeof(xPoint[0]);
        unsigned long   count1  = sizeof(xyzPoints)/sizeof(xyzPoints[0]);
        unsigned long   count2  = sizeof(xyzPoints[0])/sizeof(xyzPoints[0][0]);
        printf("数组榜首维度的长度: %lu \n", count1);
        printf("数组第二维度的长度: %lu \n", count2);
        //输出元素中的数据
    //    for (int k = 0; k < count; ++k) {
    //        printf("Element[%d]]=(%d,%d)\n",k,xPoint[k],yPoint[k]);
    //    }
        for (int k = 0; k < count1; ++k) {
            printf("Element[%d]=(x:%d,y:%d,z:%d)\n",k,xyzPoints[k][0],xyzPoints[k][1],xyzPoints[k][2]);
        }
    }
int main(int argc, const char * argv[]) {
//    test1(20);
//    test2(20);
    test3(20);
    return 0;
}

输出:

i:0------(x:0,y:-1,z:1)
j:0
j:1
j:2
i:1------(x:2,y:1,z:3)
j:0
j:1
j:2
i:2------(x:4,y:3,z:5)
j:0
j:1
j:2
i:3------(x:6,y:5,z:7)
j:0
j:1
j:2
i:4------(x:8,y:7,z:9)
j:0
j:1
j:2
i:5------(x:10,y:9,z:11)
j:0
j:1
j:2
i:6------(x:12,y:11,z:13)
j:0
j:1
j:2
i:7------(x:14,y:13,z:15)
j:0
j:1
j:2
i:8------(x:16,y:15,z:17)
j:0
j:1
j:2
i:9------(x:18,y:17,z:19)
j:0
j:1
j:2
i:10------(x:20,y:19,z:21)
j:0
j:1
j:2
i:11------(x:22,y:21,z:23)
j:0
j:1
j:2
i:12------(x:24,y:23,z:25)
j:0
j:1
j:2
i:13------(x:26,y:25,z:27)
j:0
j:1
j:2
i:14------(x:28,y:27,z:29)
j:0
j:1
j:2
i:15------(x:30,y:29,z:31)
j:0
j:1
j:2
i:16------(x:32,y:31,z:33)
j:0
j:1
j:2
i:17------(x:34,y:33,z:35)
j:0
j:1
j:2
i:18------(x:36,y:35,z:37)
j:0
j:1
j:2
i:19------(x:38,y:37,z:39)
j:0
j:1
j:2
数组榜首维度的长度: 20 
数组第二维度的长度: 3 
Element[0]=(x:0,y:-1,z:1)
Element[1]=(x:2,y:1,z:3)
Element[2]=(x:4,y:3,z:5)
Element[3]=(x:6,y:5,z:7)
Element[4]=(x:8,y:7,z:9)
Element[5]=(x:10,y:9,z:11)
Element[6]=(x:12,y:11,z:13)
Element[7]=(x:14,y:13,z:15)
Element[8]=(x:16,y:15,z:17)
Element[9]=(x:18,y:17,z:19)
Element[10]=(x:20,y:19,z:21)
Element[11]=(x:22,y:21,z:23)
Element[12]=(x:24,y:23,z:25)
Element[13]=(x:26,y:25,z:27)
Element[14]=(x:28,y:27,z:29)
Element[15]=(x:30,y:29,z:31)
Element[16]=(x:32,y:31,z:33)
Element[17]=(x:34,y:33,z:35)
Element[18]=(x:36,y:35,z:37)
Element[19]=(x:38,y:37,z:39)
Program ended with exit code: 0

4. 枚举

枚举是 C 言语中的一种根本数据类型,它能够让数据更简练,更易读。

枚举语法界说格局为:

enum 枚举名 {枚举元素1,枚举元素2,……};

接下来咱们举个比方,比方:一星期有 7 天,假如不必枚举,咱们需求运用 #define 来为每个整数界说一个别号:

#define MON  1
#define TUE  2
#define WED  3
#define THU  4
#define FRI  5
#define SAT  6
#define SUN  7  

这个看起来代码量就比较多,接下来咱们看看运用枚举的办法:

enum DAY
{
      MON=1, TUE, WED, THU, FRI, SAT, SUN
};

这样看起来是不是更简练了。

留意: 榜首个枚举成员的默许值为整型的 0,后续枚举成员的值在前一个成员上加 1。咱们在这个实例中把榜首个枚举成员的值界说为 1,第二个就为 2,以此类推。

能够在界说枚举类型时改动枚举元素的值:

enum season {spring, summer=3, autumn, winter};

没有指定值的枚举元素,其值为前一元素加 1。也就说 spring 的值为 0,summer 的值为 3,autumn 的值为 4,winter 的值为 5

枚举变量的界说

前面咱们仅仅声明晰枚举类型,接下来咱们看看怎么界说枚举变量。

咱们能够经过以下三种办法来界说枚举变量

1、先界说枚举类型,再界说枚举变量

enum DAY
{
      MON=1, TUE, WED, THU, FRI, SAT, SUN
};
enum DAY day;

2、界说枚举类型的一起界说枚举变量

enum DAY
{
      MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;

3、省掉枚举称号,直接界说枚举变量

enum
{
      MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;

比方:

//枚举
enum {
    //改动榜首个枚举元素的初始值,才会改动其枚举选项的值,且顺为改动其他 值
//    MON=3,TUE,WED,THU,FRI,SAT,SUN
    MON=1,TUE,WED,THU,FRI,SAT,SUN
}day;
void test4(void){
    //遍历一周
    for (day = MON; day <= SUN; day++) {
        printf("周: %d \n", day);
    }
    enum color{
        red=1, green, blue ,black
    };
    //声明一个 color类型的 枚举变量
    enum color favorite_color;
//     ask user to choose color
    printf("请输入你喜爱的色彩: (1. red, 2. green, 3. blue): ");
    scanf("%d", &favorite_color);
//     输出成果
    switch (favorite_color)
    {
        case red:
            printf("你喜爱的色彩是红色\n");
            break;
        case green:
            printf("你喜爱的色彩是绿色\n");
            break;
        case blue:
            printf("你喜爱的色彩是蓝色\n");
            break;
        case black:
            printf("你喜爱的色彩是黑色\n");
            break;
        default:
            printf("你没有挑选你喜爱的色彩\n");
    }
    //将整数转化为枚举
    enum Day
    {
        saturday,
        sunday,
        monday,
        tuesday,
        wednesday,
        thursday,
        friday
    } ;
    int a = 1;
    //声明一个 day类型的 枚举变量
    enum Day weekend;
    weekend = (enum Day)a;
    printf("weekend:%d \n",weekend);
}int main(int argc, const char * argv[]) {
//    test1(20);
//    test2(20);
//    test3(20);
    test4();
    return 0;
}

输出:

周: 1
周: 2 
周: 3 
周: 4 
周: 5 
周: 6 
周: 7 
请输入你喜爱的色彩: (1. red, 2. green, 3. blue): 0
你没有挑选你喜爱的色彩
weekend:1 
Program ended with exit code: 0

五、C言语中心语法|字符与字符串

1. 字符串

  • C言语中没有String这品种型。其实字符串便是字符序列,由多个字符组成,所以在C言语中,咱们能够用字符数组来存储字符串。
  • 字符串能够看做是一个特殊的字符数组,为了跟普通的字符数组区分开来,应该在字符串的尾部添加了一个完毕标志’\0’。
  • ‘\0’是一个ASCII码值为0的字符,是一个空操作符,表明什么也不干。所以选用字符数组寄存字符串,赋值时应包括完毕标志’\0’。

在 C 言语中,字符串实践上是运用 null 字符'\0' 终止的一维字符数组。因而,一个以 null 结尾的字符串,包括了组成字符串的字符。

下面的声明和初始化创立了一个 “Hello” 字符串。由于在数组的结尾存储了空字符,所以字符数组的巨细比单词 “Hello” 的字符数多一个。

char ch[6] = {'H', 'e', 'l', 'l', 'o', '\0'};

也能够运用以下简写方式:

char ch[6] = "Hello"

字符串在 C/C++ 中内存表明:

03-复习C语言核心知识|函数、作用域规则、数组、枚举、字符与字符串、指针

其实,您不需求把 null 字符放在字符串常量的结尾。C 编译器会在初始化数组时,主动把 ‘\0’ 放在字符串的结尾。让咱们测验输出上面的字符串:

//字符数组 字符串
void test8(void) {
    //界说一个 char 数组
    char string[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
    //简写
    char string2[6] = "Hello";
    //%s:输出字符串
    printf("string message : %s\n", string);
    printf("string2 message : %s\n", string2);
}
int main(int argc, const char * argv[]) {
//    test1(20);
//    test2(20);
//    test3(20);
//    test4();
//    test5();
//    test6();
//    test7();
    test8();
    return 0;
}

输出:

string message : Hello
string2 message : Hello
Program ended with exit code: 0

C 中对字符串操作的 API

序号 函数 & 意图
1 strcpy(s1, s2); 仿制字符串 s2 到字符串 s1。
2 strcat(s1, s2); 连接字符串 s2 到字符串 s1 的结尾。
3 strlen(s1); 回来字符串 s1 的长度。
4 strcmp(s1, s2); 假如 s1 和 s2 是相同的,则回来 0;假如 s1<s2 则回来小于 0;假如 s1>s2 则回来大于 0。
5 strchr(s1, ch); 回来一个指针,指向字符串 s1 中字符 ch 的榜首次呈现的方位。
6 strstr(s1, s2); 回来一个指针,指向字符串 s1 中字符串 s2 的榜首次呈现的方位。

比方:

#include <string.h>
//对字符串操作的 体系API
void test2(void) {
    //字符串操作
     char str1[12] = "Hello";
     char str2[12] = "World";
     char str3[12];
     size_t len;
     //将 str1 仿制到 str3
     strcpy(str3, str1);
     printf("strcpy (str3,str1) :%s\n", str3);
    //    strcmp(s1, s2); 假如 s1 和 s2 是相同的,则回来 0;假如 s1<s2 则回来小于 0;假如 s1>s2 则回来大于 0。
    int isEqual = strcmp(str1, str3);
    if (isEqual==0) {
        printf("字符串相同\n");
    }else {
        printf("字符串bu同\n");
    }
     //拼接字符串  str1 + str2
     strcat(str1, str2);
     printf("strcat(str1,str2) :%s\n", str1);
     //回来字符串的长度
     len = strlen(str1);
     printf("strlen(str1) :%zu\n", len);
    //    strcmp(s1, s2); 假如 s1 和 s2 是相同的,则回来 0;假如 s1<s2 则回来小于 0;假如 s1>s2 则回来大于 0。
    isEqual = strcmp(str1, str3);
    if (isEqual==0) {
        printf("字符串相同\n");
    }else {
        printf("字符串bu同\n");
    }
//    char *l_Idx =  strchr(str1, 'l');
//    char*str_Idx =  strstr(str1, str3);
}
int main(int argc, const char * argv[]) {
//    test1();
    test2();
    return 0;
}

输出:

strcpy (str3,str1) :Hello
字符串相同
strcat(str1,str2) :HelloWorld
strlen(str1) :10
字符串bu同
Program ended with exit code: 0

六、C言语中心语法|指针与回调函数

1. 指针******

学习 C 言语的指针既简略又有趣。经过指针,能够简化一些 C 编程使命的履行,还有一些使命,如动态内存分配,没有指针是无法履行的。所以,想要成为一名优秀的 C 程序员,学习指针是很有必要的。

正如您所知道的,每一个变量都有一个内存方位,每一个内存方位都界说了可运用连字号(&)运算符拜访的地址,它表明晰在内存中的一个地址。请看下面的实例,它将输出界说的变量地址:

void main(){
    int var1;
    char var2[10];
  double *var3;
    //%p : 输出指针地址
    printf("var1 变量的地址:%p \n", &var1);
    printf("var2 变量的地址:%p \n", &var2); 
  printf("var3 变量的地址:%p \n", &var3);
}  

输出:

var1 变量的地址:0x7ffee7e976b8
var2 变量的地址:0x7ffee7e976be 
var3 变量的地址:0x7ff7bfeff270

经过上面的实例,咱们了解了什么是内存地址以及怎么拜访它。接下来让咱们看看什么是指针。

什么是指针?

指针是一个变量,其值为另一个变量的地址,即内存方位的直接地址
就像其他变量或常量相同,您有必要在运用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般办法为:

type *var-name

在这儿,type 是指针的基类型,它有必要是一个有用的 C 数据类型,var-name 是指针变量的称号。
用来声明指针的星号 * 与乘法中运用的星号是相同的。可是,在这个句子中,星号是用来指定一个变量是指针。以下是有用的指针声明:

int *i; //一个整型的指针	
double *d;//double 型指针
float *f;//浮点型指针
char *ch//字符型指针

一切实践数据类型,不管是整型、浮点型、字符型,还是其他的数据类型,对应指针的值的类型都是相同的,都是一个代表内存地址的长的十六进制数。

不同数据类型的指针之间仅有的不同是,指针所指向的变量或常量的数据类型不同。

怎么运用指针?

运用指针时会频频进行以下几个操作:界说一个指针变量把变量地址赋值给指针拜访指针变量中可用地址的值
这些是经过运用一元运算符 ***** 来回来位于操作数所指定地址的变量的值。下面的实例涉及到了这些操作:

比方:

    //怎么运用指针
    int var = 66;//实践变量的声明
    int *ip;//指针变量的声明
    ip = &var; //指针变量中存储 var 的地址
    printf("var 的地址 : %p  \n", var);
    //在指针变量中存储的地址
    printf("ip 的地址:%p  \n", ip);
    //运用指针拜访地址
    printf("ip 指针对应的地址:%p \n", *ip);
    //运用指针拜访地址对应的值
    printf("ip 指针对应的地址:%d \n", *ip);
    /*
     一个指针变量 占有内存的巨细,跟指针的类型无关:
     一个指针在32位的核算机上,占4个字节;
     一个指针在64位的核算机上,占8个字节。
     一个指针占几个字节?原理是什么呢?https://blog.csdn.net/IOSSHAN/article/details/88944637
     */
    int *a;
    long*b;
    float*c;
    double*d;
    char*e;
    printf("一个int类型指针占有内存巨细:%lu \n",sizeof(a));
    printf("一个long类型指针占有内存巨细:%lu \n",sizeof(b));
    printf("一个float类型指针占有内存巨细:%lu \n",sizeof(c));
    printf("一个double类型指针占有内存巨细:%lu \n",sizeof(d));
    printf("一个char类型指针占有内存巨细:%lu \n",sizeof(e));

输出:

var 的地址 : 0x42
ip 的地址:0x7ffee96eb6b4  
ip 指针对应的地址:0x42 
ip 指针对应的地址:66   
一个int类型指针占有内存巨细:8 
一个long类型指针占有内存巨细:8 
一个float类型指针占有内存巨细:8 
一个double类型指针占有内存巨细:8 
一个char类型指针占有内存巨细:8 

C 中的 NULL 指针

在变量声明的时分,假如没有确切的地址能够赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为指针。

NULL 指针是一个界说在规范库中的值为零的常量。请看下面的程序:

void main(){
    //赋值一个 NULL 指针
    int *ptr = NULL;
    printf("ptr 的地址是: %p \n", ptr);
    //查看一个空指针
    if (ptr) printf("假如 ptr 不是空指针,则履行"); else printf("假如 ptr 是空指针,则履行");
}

输出:

ptr 的地址是: 0x0 ptr 是空指针

C 指针详解

在 C 中,有许多指针相关的概念,这些概念都很简略,可是都很重要。下面列出了 C 程序员有必要清楚的一些与指针相关的重要概念:

概念 描绘
指针的算术运算 能够对指针进行四种算术运算:++、–、+、-
指针数组 能够界说用来存储指针的数组。
指向指针的指针 C 答应指向指针的指针。
传递指针给函数 经过引证或地址传递参数,使传递的参数在调用函数中被改动。
从函数回来指针 C 答应函数回来指针到部分变量、静态变量和动态内存分配。

2. 函数指针与回调函数

函数指针是指向函数的指针变量。

通常咱们说的指针变量是指向一个整型、字符型或数组等变量,而函数指针是指向函数。

函数指针能够像一般函数相同,用于调用函数、传递参数。

函数指针变量的声明:

typedef int (*fun_ptr)(int,int)//声明一个指向同样参数,回来值得函数指针类型

比方:

//函数指针
//声明一个函数指针:能够指向该类型的函数(回来值类型相同、形参列表相同)
typedef int(*func_Rint_I1int_I2int)(int,int);
//声明一个函数
int max(int num1, int num2) {
    return (num1 > num2) ? num1 : num2;
}
void test6(void) {
    //函数为 func_Rint_I1int_I2int 类型的函数
    func_Rint_I1int_I2int p =*max;
    int a, b, c, d;
    printf("请输入三个数字:\n");
    scanf("%d %d %d", &a, &b, &c);
    //与直接调用函数等价,d = max(max(a,b),c);
    d = p(p(a, b), c);
    printf("最大数字是: %d \n", d);
}
int main(int argc, const char * argv[]) {
//    test1(20);
//    test2(20);
//    test3(20);
//    test4();
//    test5();
    test6();
    return 0;
}

输入输出:

请输入三个数字:
1
3
5
最大数字是: 5 
Program ended with exit code: 0

回调函数

函数指针变量能够作为某个函数的参数来运用的,回调函数便是一个经过函数指针调用的函数

简略讲:回调函数是由他人的函数履行时调用你完成的函数

比方:

比方中 populate_array 函数界说了三个参数,其间第三个参数是函数的指针,经过该函数来设置数组的值。

实例中咱们界说了回调函数 getNextRandomValue,它回来一个随机值,它作为一个函数指针传递给 populate_array 函数。

populate_array 将调用 10 次回调函数,并将回调函数的回来值赋值给数组。

/*
 回调函数
 函数指针变量能够作为某个函数的参数来运用的,回调函数便是一个经过函数指针调用的函数。
 简略讲:回调函数是由他人的函数履行时调用你完成的函数。
 */ 
#include <stdio.h>
#include <stdlib.h>
//回调函数
void populate_array(int *array, size_t arraySize, int(*getNextValue)(void)) {
    printf("array 地址:%p \n", array);
    for (size_t i = 0; i < arraySize; i++) {
        array[i] = getNextValue();
        printf(" array[%zu] ,存储值:%d \n", i, array[i]);
    }
} 
//获取一个随机数
int getNextRandomValue(void) {
    return rand();
} 
void test7(void) {
    //回调函数
    int array[10];
    printf("Int array 地址:%p \n", array);
    int count  =(sizeof(array)/sizeof(array[0]));
    printf("===============数组存储========================\n");
    populate_array(array, count, getNextRandomValue);
    printf("===============遍历数组========================\n");
    for (int i = 0; i < count; ++i) {
        printf(" array[%d] , 对应值为:%d \n", i, array[i]);
    } 
}
int main(int argc, const char * argv[]) {
//    test1(20);
//    test2(20);
//    test3(20);
//    test4();
//    test5();
//    test6();
    test7();
    return 0;
}

输出:

Int array 地址:0x7ff7bfeff260
===============数组存储========================
array 地址:0x7ff7bfeff260 
 array[0] ,存储值:16807 
 array[1] ,存储值:282475249 
 array[2] ,存储值:1622650073 
 array[3] ,存储值:984943658 
 array[4] ,存储值:1144108930 
 array[5] ,存储值:470211272 
 array[6] ,存储值:101027544 
 array[7] ,存储值:1457850878 
 array[8] ,存储值:1458777923 
 array[9] ,存储值:2007237709 
===============遍历数组========================
 array[0] , 对应值为:16807 
 array[1] , 对应值为:282475249 
 array[2] , 对应值为:1622650073 
 array[3] , 对应值为:984943658 
 array[4] , 对应值为:1144108930 
 array[5] , 对应值为:470211272 
 array[6] , 对应值为:101027544 
 array[7] , 对应值为:1457850878 
 array[8] , 对应值为:1458777923 
 array[9] , 对应值为:2007237709 
Program ended with exit code: 0 

专题系列文章

1. iOS底层原理前常识

  • 01-探求iOS底层原理|总述
  • 02-探求iOS底层原理|编译器LLVM项目【Clang、SwiftC、优化器、LLVM】
  • 03-探求iOS底层原理|LLDB
  • 04-探求iOS底层原理|ARM64汇编

2. 根据OC言语探求iOS底层原理

  • 05-探求iOS底层原理|OC的实质
  • 06-探求iOS底层原理|OC目标的实质
  • 07-探求iOS底层原理|几种OC目标【实例目标、类目标、元类】、目标的isa指针、superclass、目标的办法调用、Class的底层实质
  • 08-探求iOS底层原理|Category底层结构、App启动时Class与Category装载过程、load 和 initialize 履行、关联目标
  • 09-探求iOS底层原理|KVO
  • 10-探求iOS底层原理|KVC
  • 11-探求iOS底层原理|探求Block的实质|【Block的数据类型(实质)与内存布局、变量捕获、Block的品种、内存办理、Block的修饰符、循环引证】
  • 12-探求iOS底层原理|Runtime1【isa详解、class的结构、办法缓存cache_t】
  • 13-探求iOS底层原理|Runtime2【消息处理(发送、转发)&&动态办法解析、super的实质】
  • 14-探求iOS底层原理|Runtime3【Runtime的相关运用】
  • 15-探求iOS底层原理|RunLoop【两种RunloopMode、RunLoopMode中的Source0、Source1、Timer、Observer】
  • 16-探求iOS底层原理|RunLoop的运用
  • 17-探求iOS底层原理|多线程技能的底层原理【GCD源码剖析1:主队列、串行队列&&并行队列、大局并发队列】
  • 18-探求iOS底层原理|多线程技能【GCD源码剖析1:dispatch_get_global_queue与dispatch_(a)sync、单例、线程死锁】
  • 19-探求iOS底层原理|多线程技能【GCD源码剖析2:栅门函数dispatch_barrier_(a)sync、信号量dispatch_semaphore】
  • 20-探求iOS底层原理|多线程技能【GCD源码剖析3:线程调度组dispatch_group、事件源dispatch Source】
  • 21-探求iOS底层原理|多线程技能【线程锁:自旋锁、互斥锁、递归锁】
  • 22-探求iOS底层原理|多线程技能【原子锁atomic、gcd Timer、NSTimer、CADisplayLink】
  • 23-探求iOS底层原理|内存办理【Mach-O文件、Tagged Pointer、目标的内存办理、copy、引证计数、weak指针、autorelease

3. 根据Swift言语探求iOS底层原理

关于函数枚举可选项结构体闭包特点办法swift多态原理StringArrayDictionary引证计数MetaData等Swift根本语法和相关的底层原理文章有如下几篇:

  • 01-Swift5常用中心语法|了解Swift【Swift简介、Swift的版本、Swift编译原理】
  • 02-Swift5常用中心语法|根底语法【Playground、常量与变量、常见数据类型、字面量、元组、流程操控、函数、枚举、可选项、guard句子、区间】
  • 03-Swift5常用中心语法|面向目标【闭包、结构体、类、枚举】
  • 04-Swift5常用中心语法|面向目标【特点、inout、类型特点、单例方式、办法、下标、承继、初始化】
  • 05-Swift5常用中心语法|高档语法【可选链、协议、过错处理、泛型、String与Array、高档运算符、扩展、拜访操控、内存办理、字面量、方式匹配】
  • 06-Swift5常用中心语法|编程范式与Swift源码【从OC到Swift、函数式编程、面向协议编程、响应式编程、Swift源码剖析】

4. C言语中心语法

  • 01-温习C言语中心常识|总述
  • 02-温习C言语中心常识|根本语法、数据类型、变量、常量、存储类、根本句子(判断句子、循环句子、go to句子)和运算
  • 03-温习C言语中心常识|函数、效果域规矩、数组、枚举、字符与字符串、指针
  • 04-温习C言语中心常识|结构体、共用体、位域、输入&输出、文件读写
  • 05-温习C言语中心常识|预处理、头文件、强制类型转化、过错处理、递归、内存办理

5. C++中心语法

  • 01-C++中心语法|C++概述【C++简介、C++来源、可移植性和规范、为什么C++会成功、从一个简略的程序开端知道C++】
  • 02-C++中心语法|C++对C的扩展【::效果域运算符、姓名操控、struct类型加强、C/C++中的const、引证(reference)、函数】
  • 03-C++中心语法|面向目标1【 C++编程规范、类和目标、面向目标程序设计事例、目标的结构和析构、C++面向目标模型初探】
  • 04-C++中心语法|面向目标2【友元、内部类与部分类、强化训练(数组类封装)、运算符重载、仿函数、模板、类型转化、 C++规范、过错&&反常、智能指针】
  • 05-C++中心语法|面向目标3【 承继和派生、多态、静态成员、const成员、引证类型成员、VS的内存窗口】

6. Vue全家桶

  • 01-Vue全家桶中心常识|Vue根底【Vue概述、Vue根本运用、Vue模板语法、根底事例、Vue常用特性、归纳事例】
  • 02-Vue全家桶中心常识|Vue常用特性【表单操作、自界说指令、核算特点、侦听器、过滤器、生命周期、归纳事例】
  • 03-Vue全家桶中心常识|组件化开发【组件化开发思想、组件注册、Vue调试东西用法、组件间数据交互、组件插槽、根据组件的
  • 04-Vue全家桶中心常识|多线程与网络【前后端交互方式、promise用法、fetch、axios、归纳事例】
  • 05-Vue全家桶中心常识|Vue Router【根本运用、嵌套路由、动态路由匹配、命名路由、编程式导航、根据vue-router的事例】
  • 06-Vue全家桶中心常识|前端工程化【模块化相关规范、webpack、Vue 单文件组件、Vue 脚手架、Element-UI 的根本运用】
  • 07-Vue全家桶中心常识|Vuex【Vuex的根本运用、Vuex中的中心特性、vuex事例】

7. 音视频技能中心常识

  • 01-音视频技能中心常识|了解音频技能【移动通讯技能的开展、声响的实质、深入了解音频】
  • 02-音视频技能中心常识|建立开发环境【FFmpeg与Qt、Windows开发环境建立、Mac开发环境建立、Qt开发根底】
  • 03-音视频技能中心常识|Qt开发根底【.pro文件的装备、Qt控件根底、信号与槽】
  • 04-音视频技能中心常识|音频录制【命令行、C++编程】
  • 05-音视频技能中心常识|音频播映【播映PCM、WAV、PCM转WAV、PCM转WAV、播映WAV】
  • 06-音视频技能中心常识|音频重采样【音频重采样简介、用命令行进行重采样、经过编程重采样】
  • 07-音视频技能中心常识|AAC编码【AAC编码器解码器、编译FFmpeg、AAC编码实战、AAC解码实战】
  • 08-音视频技能中心常识|成像技能【重识图片、详解YUV、视频录制、显现BMP图片、显现YUV图片】
  • 09-音视频技能中心常识|视频编码解码【了解H.264编码、H.264编码、H.264编码解码】
  • 10-音视频技能中心常识|RTMP服务器建立【流媒体、服务器环境】

其它底层原理专题

1. 底层原理相关专题

  • 01-核算机原理|核算机图形烘托原理这篇文章
  • 02-核算机原理|移动终端屏幕成像与卡顿

2. iOS相关专题

  • 01-iOS底层原理|iOS的各个烘托结构以及iOS图层烘托原理
  • 02-iOS底层原理|iOS动画烘托原理
  • 03-iOS底层原理|iOS OffScreen Rendering 离屏烘托原理
  • 04-iOS底层原理|因CPU、GPU资源消耗导致卡顿的原因和解决计划

3. webApp相关专题

  • 01-Web和类RN大前端的烘托原理

4. 跨渠道开发计划相关专题

  • 01-Flutter页面烘托原理

5. 阶段性总结:Native、WebApp、跨渠道开发三种计划功能比较

  • 01-Native、WebApp、跨渠道开发三种计划功能比较

6. Android、HarmonyOS页面烘托专题

  • 01-Android页面烘托原理
  • 02-HarmonyOS页面烘托原理 (待输出)

7. 小程序页面烘托专题

  • 01-小程序结构烘托原理