• C++初探 5-2(while循环 do while循环 输入 二维数组)


    目录

    while循环

    for 与 while

    编写延时循环

    do while循环

    基于范围的for循环(C++11)

    循环和文本输入

    使用原始的cin进行输入

    使用cin.get(char)进行补救

    使用不同的cin.get( )

    文件尾条件

    另一个cin.get( )版本

    嵌套循环和二维数组

    初始化二维数组

    使用二维数组


    本笔记参考:《C++ PRIMER PLUS(第6版)》


    while循环

            可以把while循环看作没有初始化和更新部分的for循环,即while循环只存在测试条件和循环体:

            和for循环一样,① 当while循环的测试表达式值为true时,执行循环体中的语句。② 执行完毕后,返回测试表达式重新进行评估,直到测试条件为false。

    使用例:遍历字符串,显示其中的字符及其ASCII码。

    1. #include
    2. const int ArSize = 20;
    3. int main()
    4. {
    5. using namespace std;
    6. char name[ArSize];
    7. cout << "请输入一个单词(英文):";
    8. cin >> name;
    9. cout << "这个单词拥有的字符及其ASCII码是:\n";
    10. int i = 0;
    11. while (name[i] != 0)
    12. {
    13. cout << name[i] << ": " << int(name[i]) << endl;
    14. i++;
    15. }
    16. return 0;
    17. }

    程序执行的结果是:

    【分析】

            在上述程序的while循环的最后,存在 i++; 这样的语句,while循环需要通过类似于这种语句去更新测试表达式的结果,否则可能会进入死循环。

            当然,上述while行可以改成这样:

    while (name[i])

            此时,测试表达式有两个可能值:

    • 若name[i]是常规字符,其值应该为 true 或者 一个非零值(对应字符的编码)
    • 若name[i]是空值字符,其值应该为 false 或者 0 。

            另外,在上述程序中,为了打印字符的ASCII码,使用了强制类型转换,否则cout将会把name[i]解释为字符编码。

        string对象并没有使用空字符标记字符串末尾,如果使用string对象,就要对应改变上述程序。

    for 与 while

            在本质上,for循环和while循环是相同的。例如:

    1. 下方的for循环:
      1. for (int i = 0; i < 10; i++)
      2. {
      3. cout << i << endl;
      4. }

      可改成对应的while循环:

      1. int i = 0;
      2. while (i < 10)
      3. {
      4. cout << i << endl;
      5. i++;
      6. }
    2. 或者将while循环:
      1. while (j > 0)
      2. {
      3. i += j;
      4. j--;
      5. }

      改写为对应的for循环:

      1. for ( ; j > 0; )
      2. {
      3. i += j;
      4. j--;
      5. }

            对于for循环而言,其需要的3个表达式均可以是空表达式,只有分号是必须要有的。不过如果测试表达式为空,那么这个for循环将会是一个死循环,类似于:

    1. int i = 0;
    2. for (; ;)
    3. cout << i++ << endl;

            因此,在设计循环时,需要知道3个条件:

    1. 循环终止的条件;
    2. 首次测试前的初始化条件;
    3. 在条件被再次测试之前的更新条件。

            for循环在结构上明确指出了这3个条件。而在无法预知循环的执行次数时,程序员会使用while循环。

        在书写循环时,可能会出现这种情况:

    1. i = 0;
    2. while (name[i] != '\0'); //存在问题:多写了一个分号
    3. {
    4. cout << name[i] << endl;
    5. i++;
    6. }

        上述代码在while行中多插入了一个分号,因为分号结束语句,因此分号将会结束while循环(即上述循环的循环体为空语句)。所以,上述循环不执行任何操作,成为了一个死循环。

    编写延时循环

            在一种用于个人计算机的早期技术中,会通过while循环的使用来进行延时操作:

    1. int main()
    2. {
    3. long wait = 0;
    4. while (wait < 1000000)
    5. wait++;
    6. return 0;
    7. }

    但这种延时方式受到计算机处理器的限制,在不同的计算机上可能需要不同的计数限制。另外,现在计算机的计算速度已经很快了,通过这种方式进行延时操作并不合适。

            而现在,ANSI C和C++库提供了一个函数,clock( ) ,它能够返回出现开始执行后所用的系统时间。但依旧存在一些问题:

    1. clock( )返回的时间,单位不一定是 秒 ;
    2. clock( )的返回类型并不一致,可能是long,也可能是unsigned long等等。

            为此,需要使用头文件(time.h)提供的一个符号常量:CLOCKS_PER_SEC,该常量等于每秒钟包含的 系统时间单位数 。由此就得到了两个计算公式:

            另一方面,clock_t 作为clock( )返回值的别名(类型别名),即只要把变量声明为 clock_t 类型,编译器就将自带匹配与clock( )返回值相符的类型。

    例子:

    1. #include
    2. #include
    3. int main()
    4. {
    5. using namespace std;
    6. cout << "请输入需要延迟的时间(单位是秒):";
    7. float secs;
    8. cin >> secs;
    9. clock_t delay = secs * CLOCKS_PER_SEC;
    10. cout << "开始计时\a\n";
    11. clock_t start = clock();
    12. while (clock() - start < delay);
    13. cout << "完成\a\n";
    14. return 0;
    15. }

    程序执行的结果:

            上述程序选择了以系统时间为单位(不以秒为单位)进行延迟时间的计算,省去了在每轮循环中将系统时间转换成秒的操作。

            在之前提到过,clock_t 是一个系统别名,而C++创建别名的方式有两种:

    1. 使用预处理器,例如:
      #define BYTE char

      预处理器将使用char类型替换程序中出现的所有BYTE,这就使BYTE成为了char的别名;

    2. 使用关键字typedef创建别名,例如:

      typedef char byte;    //将 byte 作为char的别名

      通用格式:

      如果要让 byte_pointer 成为char指针的别名,可以这样做:

      typedef char* byte_pointer;

        尽管上述两种方法都可以用来创建别名,但是在使用 #define 声明一系列变量时应该注意:

    1. #define FLOAT_POINTER float*
    2. FLOAT_POINTER pa, pb;

        上述代码在预处理器的转换后,会变成这样:

    float* pa, pb;    //此时,pa是一个浮点型指针,pb是一个浮点型变量

        不过typedef不会有这样的问题,它能够处理更复杂的类型别名。但注意,typedef不会创建新的类型,它只是为已有的类型创建一个新的名称。

    do while循环

            do while循环不同于之前的两种循环,它是出口条件循环:先执行循环体,再进入测试表达式。因此,do while循环至少会执行一次。其句式为:

            do while循环的程序流程:

            因为出口条件循环本身的结构, 有时候适合入口条件循环的场合并不一定适合do while循环,但在一些情况下,do while循环要更为合理。例如:请求用户输入,程序要先获得输入,再进行测试。

    使用例:

    1. #include
    2. int main()
    3. {
    4. using namespace std;
    5. int n;
    6. cout << "请输入一个数字(范围是1-10)\n";
    7. do
    8. {
    9. cin >> n; //先执行语句
    10. } while (n != 7); //再进行测试
    11. cout << "与系统数字匹配成功。\n";
    12. return 0;
    13. }

    程序执行的结果是:

        另一方面,存在如下的循环:

    1. int I = 0;
    2. for (;;)
    3. {
    4. I++;
    5. //内部执行的一些操作
    6. if (30 >= I)
    7. break;
    8. }

    或者

    1. int I = 0;
    2. for (;; I++)
    3. {
    4. if (30 >= I)
    5. break;
    6. //内部执行的一些操作
    7. }

        上述代码可读性差,也并非编写循环的通用模型。但第一个例子使用 do while循环 会使得代码表达更加清晰:

    1. //对应之前的第一个例子
    2. int I = 0;
    3. do
    4. {
    5. I++;
    6. //进行一些操作
    7. } while (30 > I);

        类似地,第二个例子使用 while循环 会更易理解:

    1. int I = 0;
    2. while (I < 30)
    3. {
    4. //进行一些操作
    5. I++;
    6. }

    基于范围的for循环(C++11)

            C++11新增了一种循环:基于范围的for循环。这简化了对于数组(或容器类,包括vector和array)的每个元素执行相同的操作,例如:

    1. #include
    2. int main()
    3. {
    4. using namespace std;
    5. double prices[5] = { 4.99, 10.99, 6.87, 7.99, 8.49 };
    6. for (double x : prices)
    7. cout << x << endl;
    8. return 0;
    9. }

    程序执行的结果是:

    【分析】

            先看语句:

    for (double x : prices)

    在这条语句中,x 最初表示数组prices的第一个元素。此后,随着循环进行,x 依次表示了数组中的每个元素。

            类似地,可以通过上述方式进行数组元素的修改:

    1. for (double& x : prices) //& 表示引用变量
    2. x = x * 0.80;

            还可以将这种语法与初始化列表结合起来:

    1. for (int x : {3, 5, 2, 8, 6})
    2. cout << x << " ";
    3. cout << "\n";

    打印结果:

    循环和文本输入

            通过循环,能够完成一类常见、重要的工作:逐字符地读取来自文件或键盘的文本。在C++中,因为cin对象支持三种不同模式的单字符输入,其用户接口各不相同,所以需要分开讨论。

    使用原始的cin进行输入

            为了明确读取文本的循环停止的条件,有时,会选择某个特殊字符(哨兵字符),将其作为停止标记。譬如,下方的例子规定:在遇到 #字符 时停止读取输入。

    例子

    1. #include
    2. int main()
    3. {
    4. using namespace std;
    5. char ch;
    6. int count = 0;
    7. cout << "请输入字符(输入 # 时,结束文本读取):\n";
    8. cin >> ch; //读取第一个字符(初始化)
    9. cout << "\n读取到的字符是:\n";
    10. while (ch != '#') //(测试)
    11. {
    12. cout << ch; //打印读取的字符
    13. ++count; //进行读取字符的计数
    14. cin >> ch; //读取下一个字符(更新)
    15. }
    16. cout << "\n\n读取了 " << count << " 个字符。\n";
    17. return 0;
    18. }

    程序执行的结果是:

    【分析】

            该循环在循环开始前就读取了第一个字符,这是为了让循环能够测试第一个字符(第一个字符也可能是'#')。

            可以发现,程序在输出时忽略了空格(seeyouagain),这是因为 cin 在读取char值(或者其他基本类型)时,将自动省略空格和换行符。因此上述程序中,空格也没有被包含在计数(count)内。

            另外,发送给cin的输入会先进入缓冲区,当用户输入回车键时,再统一发送给程序。这也解释了为什么在运行程序时,可以在 # 后面输入字符(因为程序在遇到 # 时就结束了对输入的处理)


    使用cin.get(char)进行补救

            之前提到,通过原始的cin进行输入会忽略空格和换行符。而在cin所属的istream类中,包含了一个能够读入每个字符(包括空格、制表符和换行符)的成员函数cin.get( )

    例子:

    1. #include
    2. int main()
    3. {
    4. using namespace std;
    5. char ch;
    6. int count = 0;
    7. cout << "请输入字符(输入 # 时,结束文本读取):\n";
    8. //cin >> ch;
    9. cin.get(ch);
    10. cout << "\n读取到的字符是:\n";
    11. while (ch != '#')
    12. {
    13. cout << ch;
    14. ++count;
    15. cin.get(ch);
    16. }
    17. cout << "\n\n读取了 " << count << " 个字符。\n";
    18. return 0;
    19. }

    程序执行的结果是:

    【分析】

            这次程序打印了每个字符,并对它们进行了计数,包括空格。cin.get( )同样存在一个缓冲区,这意味着输入的字符个数可能并最终打印的要多。

    解释 cin.get(ch); 

            如果用C语言的理解进行解释:该语句将 字符变量ch 传递给了 函数cin.get( )。这很明显是无效的,因为该函数的目的是修改变量ch的值,这需要的应该是ch的地址(即&ch)。

            但程序执行的结果已经告诉我们,上述代码是正确的。这是为什么?

            这是因为成员函数cin.get(ch)的参数声明是一个引用类型(引用类型是C++新增的一种类型):

    使用不同的cin.get( )

            回顾曾经提到过的例子:

    1. #include
    2. #define ArSize 20
    3. int main()
    4. {
    5. using namespace std;
    6. char name[ArSize];
    7. cout << "请输入名字:";
    8. cin.get(name, ArSize).get();
    9. cout << "输出:" << name << "\n";
    10. return 0;
    11. }

    程序执行的结果是:

            其中,存在着这条语句:

    cin.get(name, ArSize).get();

    这条语句可以被看作是两个连续的函数调用:

    1. cin.get(name, ArSize);
    2. cin.get();

            这里出现了cin.get( )的两个版本

    • 一个版本接受两个参数:① 数组名(char*类型的)字符串的地址),② ArSize(int类型的整数)
    • 另一个版本是不接受任何参数的cin.get( )。

            而在之前的例子中,还存在一个 cin.get( ) :    

      1. char ch;
      2. cin.get(ch);

      该版本下的 cin.get( ) 接受一个char类型的参数。

            这很明显区别于C语言的函数定义,而C++之所以可以这样做,是因为该语言支持被称为函数重载的OOP特性。函数重载允许创建多个同名函数,条件是它们的参数列表不同,例如:

    如果输入的参数不同,编译器将会从上述各种版本的cin.get( )中选出适合当前参数的。

            暂时总结:函数重载允许对多个相关的函数使用相同的名称,这些函数以不同的方式或针对不同的类型执行相同的基本任务。

        为区分不同的函数版本,在引用这类函数时提供参数列表。

    文件尾条件

            在之前进行字符串输入的例子中,为了检测字符串输入完毕,需要在字符串末尾添加'#',但这明显没有考虑'#'可能是合法输入的组成部分,其他符号亦然。

            如果输入来自文件,替代上述提到的特殊字符,可以使用一种技术 —— 检测文件尾(EOF - End of File)。这需要C++输入工具和操作系统协同工作。

            首先了解两个概念:

    1. 重定向操作符( < ):很多操作系统支持重定向,即允许文件替换键盘输入,例如:
      excute < word

      其中 excute.exe 是可执行文件,word 是文本文件。通过这种方式,程序将从word文件(而不是键盘)获取输入。

    2. 允许通过键盘输入模拟文件尾条件:

      1. Unix中,需要在行首按下 Ctrl + D 来实现;

      2. Windows命令提示符中,可在任意为止按下 Ctrl + Z + Enter 实现。

        键盘输入的EOF概念是命令行环境遗留下来的。总之,很多PC环境都将 Ctrl + Z 视为模拟的EOF(不过有些系统不支持这种模拟的EOF,或者支持并不完善)。

    例子:

    1. #include
    2. int main()
    3. {
    4. using namespace std;
    5. char ch;
    6. int count = 0;
    7. cin.get(ch); //尝试进行第一个字符的读取
    8. while (cin.fail() == false) //进行检测
    9. {
    10. cout << ch;
    11. ++count;
    12. cin.get(ch);
    13. }
    14. cout << "\n读取了 " << count << " 个字符。\n";
    15. return 0;
    16. }

    程序执行的结果是:

         在检测到EOF之后,cin将会把成员常量 eofbit 和 failbit 都设置为1。此时可以通过成员函数eof( )或者fail( )进行检测,如果检测到EOF,函数会返回 true ,否则返回 false 。

    (ps:fail( )和eof( )读取的都是最近的结果,它们是事后报告。)

            通过重定向,可以用该程序显示文本文件及其包含的字符数,下面是Ubantu系统上的运行结果(其中test是可执行文件,word是文本文件):

    1. EOF结束输入

            正如之前提到的,cin在检测到EOF时,会在cin对象中设置一个指示EOF条件的标记。设置完毕后,cin将不再读取。但这仅针对文件输入。对于键盘输入,是有可能因为cin.clear( )清除了EOF标记,导致输入继续进行。

        目前可知的:在一些系统里,Ctrl + Z 将结束输入和输出,但cin.clear( )将无法回复输入和输出。


    2. 常见的字符输入做法

            在程序的循环测试条件部分出现了这样的语句:

    while (cin.fail() == false)

            之前提到过的,成员函数fail( )是有返回值的,通过将这种返回值和运算符结合起来,可以得到一种更简便的测试表达式(!运算符可以转换true和false):

    while (!cin.fail())

    ------

            或者,因为istream类提供了一个可以将istream对象(如cin)转换为bool值的函数:当cin出现在需要bool值的位置时,该函数将被调用(在读取到EOF时,因为读取失败,将会得到的bool值是false)

    while (cin)    //当成功读取输入时,执行循环。

        因为通过这种方式执行的while循环可以检测更多的失败原因,如磁盘故障,所以这种写法会更加通用。

    ------

            在之前的程序中,出现的输入语句是cin.get( ),该函数的返回值就是cin。因此,可以这样简化程序:

    1. while (cin.get(ch))
    2. {
    3. //内部语句
    4. }

            这种方式使得程序在这个运行过程中只对函数cin.get(char)进行了一次调用,并且,这条语句同时凑齐了循环的三个条件,即初始化、确定结束(检测)条件和更新条件。

    另一个cin.get( )版本

            还存在一种类似于 C语言的getchar( ) 的cin.get( )版本。在不接受任何参数时,cin.get( )将会返回输入中的下一个字符,例如:

    ch = cin.get();

            这种版本的cin.get( )的工作方式是将字符编码作为int值返回(与其他版本存在区别:cin.get(ch)返回的是一个对象,而不是读取的字符)。类似地,存在cout.put( )函数,该函数可以进行字符的显示,工作方式类似于C语言的putchar( ):

    cout.put(ch);

        最初,C++标准要求put( )成员只有一个原型,即put(char),该函数可以接受int参数。但有些C++实现提供了3个原型:put(char)、put(signed char)和put(unsigned char),此时给put( )传递一个int类型参数将导致错误,为此需要使用显式强制类型转换。

            尽管提到了多种的cin版本,但此时还有一个问题没有得到解决,那就是如何判定EOF(EOF不表示输入中的字符,而是表示没有字符)

            为此,头文件iostream中定义了一个用符号常量EOF表示的特殊值,这个值必须不同于任何有效的字符值。通常,EOF被定义为 -1

            之前提到的例子的核心:

    1. char ch;
    2. cin.get(ch);
    3. while (cin.fail() == false)
    4. {
    5. cout << ch;
    6. ++count;
    7. cin.get(ch);
    8. }

    可以被替换成下方的代码:

    1. int ch; //ch变为 int类型 的变量
    2. ch = cin.get(); //使用了另一个版本的cin.get()
    3. while (ch != EOF) //EOF测试替代了原本的cin.fail()检测
    4. {
    5. cout.put(ch); //在一些实现中可用 cout.put(char(ch)) 替代
    6. ++count;
    7. ch = cin.get();
    8. }

            需要注意,EOF表示的不是有效的字符编码,在某些系统中可能与char类型不兼容。此时需要使用int类型的变量接收cin.get( )(无参版本)的返回值,并在输出时进行强制类型转换。

    使用cin.get( )方法的例子:

    1. #include
    2. int main()
    3. {
    4. using namespace std;
    5. int ch;
    6. int count = 0;
    7. while ((ch = cin.get()) != EOF)
    8. {
    9. cout.put(char(ch));
    10. ++count;
    11. }
    12. cout << "\n读取了 " << count << " 个字符。\n";
    13. return 0;
    14. }

    程序执行的结果是:

    【分析】

            先观察循环条件:

    while ((ch = cin.get()) != EOF)

    子表达式 ch = cin.get( ) 两端的括号改变了程序的运算顺序,因此,该表达式的执行顺序应为:

    1. 调用函数cin.get( );
    2. 对 ch 进行赋值;
    3. 因为赋值表达式的值为左操作数的值,因此整个子表达式的值为 ch 的值;
    4. 将子表达式(ch)的值与EOF进行比较。

            如果上述表达式中省略了子表达式的括号:

    while (ch = cin.get() != EOF)

    因为 !=运算符 的优先级高于 =运算符 ,所以程序将会优先执行 cin.get() != EOF ,并将得到的bool值赋给 ch ,这就偏离了程序本意。

            而如果使用 cin.get(ch) 进行输入操作就不会存在上述问题。因为当 cin.get(char) 到达EOF时,该函数不会将任何值赋给ch(即ch不会被用于存储非char值)

    属性cin.get(ch)ch = cin.get( )
    传递 输入字符 的方式赋给参数ch将 函数返回值 赋给ch

    函数的返回值

    (用于字符输入)

    istream对象

    (执行向bool值的转换后,得到 true

    int类型的字符编码

    函数的返回值

    (到达EOF时)

    istream对象

    (执行向bool值的转换后,得到 false

    EOF

            如果使用的是带有字符参数的版本,那么在进行字符拼接时将会更方便:

    cin.get(ch_1).get(ch_2);

    这条语句将输入中的下一个字符赋给 ch_1 ,并将接着的下一个字符赋给 ch_2 。之所以可以这样处理,是因为 cin.get(ch_1) 返回的是一个cin对象,可以使用整个对象再次进行get( )的调用。

    嵌套循环和二维数组

            如果把一维数组看作一行数据,那么二维数组就是一个表格(既有数据行,也有数据列)

            尽管C++没有提供二维数组类型,但用户可以创建这样一个数组,让数组的元素本身就是数组。例如:

    int maxtemps[4][5];

    上述声明说明:

    • maxtemps是一个包含了 4个元素 的数组;
    • 每个元素都是包含了 5个int类型元素 的数组。

            其中:

    • maxtemps[0]即是maxtemps数组的第一个元素,本身也是由5个int类型元素组成的元素;
    • maxtemps[0][0]是maxtemps[0]数组的第一个元素,本身是int类型。

            因此,可以将二维数组看作一个表格,第一个下标表示行,第二个下标表示列:

    打印二维数组maxtemps的所有内容:

    1. for (int row = 0; row < 4; row++)
    2. {
    3. for (int col = 0; col < 5; ++col)
    4. cout << maxtemps[row][col] << "\t";
    5. cout << endl;
    6. }

    初始化二维数组

            参照一维数组的初始化方式(提供一个值列表),二维数组的初始化可以看作是一系列的一维数组的初始化:

    1. int maxtemps[4][5] =
    2. {
    3. {1, 2, 3, 4, 5}, //初始化数组maxtemps[0]
    4. {6, 7, 8, 9, 10}, //初始化数组maxtemps[1]
    5. {11, 12, 13, 14, 15}, //初始化数组maxtemps[2]
    6. {16, 17, 18, 19, 20} //初始化数组maxtemps[3]
    7. };

    使用二维数组

    例子:

    1. #include
    2. const int Cities = 5;
    3. const int Years = 4;
    4. int main()
    5. {
    6. using namespace std;
    7. const char* cities[Cities] =
    8. {
    9. "厦门",
    10. "浙江",
    11. "昆明",
    12. "上海",
    13. "吉林"
    14. };
    15. int maxtemps[Years][Cities] = //数据是编的
    16. {
    17. {36, 37, 35, 30, 25},
    18. {37, 38, 36, 31, 24},
    19. {38, 36, 36, 31, 26},
    20. {40, 39, 38, 33, 27},
    21. };
    22. cout << "2008 - 2011的最高气温(编的)\n\n";
    23. for (int city = 0; city < Cities; city++)
    24. {
    25. cout << cities[city] << ":\t";
    26. for (int year = 0; year < Years; year++)
    27. cout << maxtemps[year][city] << "\t";
    28. cout << endl;
    29. }
    30. return 0;
    31. }

    程序执行的结果是:

    【分析】

            不同于之前的程序,该程序将列循环(城市索引)放在了外面,而将行循环(年份索引)放在了里面。

            另一方面,该程序提供声明一个char指针数组来存储城市的名字。但是,使用char类型的二维数组也是可行的:

    1. const char cities[Cities][25] =
    2. {
    3. "厦门",
    4. "浙江",
    5. "昆明",
    6. "上海",
    7. "吉林"
    8. };

    这种写法规定了5个字符串的最大长度为24个字符,而数组本身存储的是这5个字符串的首字符的地址。

        从这里可以看出,二维数组在进行元素修改时拥有更大优势(因为二维数组可以通过索引快速找到元素),而指针数组因为不用开辟多余的空间,所以更加经济。

            除此之外,还可以使用string对象对城市名称进行存储:

    1. const string cities[Cities] =
    2. {
    3. "厦门",
    4. "浙江",
    5. "昆明",
    6. "上海",
    7. "吉林"
    8. };

    因为string类拥有自动调整大小的特性,因此在这种地方要比二维数组来得方便。

        因为使用了const限定符,使用这里的字符串实际上是无法进行修改的。

  • 相关阅读:
    1.1.1 linux基础
    浏览器与服务器通信过程(HTTP协议)
    【408数据结构与算法】—栈与递归(十二)
    SQLServer导出数据库字典
    GitHub操作之远程仓库基本操作(团队内创建,推送,拉取等)
    【线性方程】高斯-赛德尔送代法求解线性方程组
    每天五分钟机器学习:如何根据ROC曲线计算AUC?
    RK3568平台开发系列讲解(外设篇)AP3216C 三合一环境传感器驱动
    [ACTF2020 新生赛]Exec
    【C语言】21-指针-3
  • 原文地址:https://blog.csdn.net/w_pab/article/details/127829578