7.25-7.29学习结果

时间:2025-03-31 14:07:10

1.2 标识符

1.2.1 定义

标识符:用于标识变量名、符号常量名、函数名、类型名、文件名等的有效字符序列;

1.2.2 标识符的命名规则

\1. 标识符只能由字母、数字和下划线三种字符组成,且第一个字符必须为字母或下划线;

\2. C 语言中的标识符大小写敏感;

\3. 用户自定义的标识符不能与关键字同名;

关键字:

1.3.1 定义

关键字:对编译器具有特定含义的标识符,是标识符的一个特殊的集合。C语言内所有的关键字都

是小写。

1.3.2 C**语言中的关键字**

[1] 基本数据类型关键字

void:声明函数无返回值或无参数,声明无类型指针,显示丢弃运算结果。(C89标准新增) 
    
char:字符型类型数据,属于整型数据的一种。(K&R时期引入) 
    
int:整型数据,表示范围通常为编译器指定的内存字节长。(K&R时期引入) 
    
float:单精度浮点型数据,属于浮点数据的一种。(K&R时期引入) 
    
double:双精度浮点型数据,属于浮点数据的一种。(K&R时期引入) 
    
//_Bool:布尔型(C99标准新增) 
    
//_Complex:复数的基本类型(C99标准新增) 
    
//_Imaginary:虚数,与复数基本类型相似,没有实部的纯虚数(C99标准新增) 
    
//_Generic:提供重载的接口入口(C11标准新增)

[2] 类型修饰关键字

short:修饰int,短整型数据,可省略被修饰的int。(K&R时期引入) 
    
long:修饰int,长整型数据,可省略被修饰的int。(K&R时期引入) 
    
// long long:修饰int,超长整型数据,可省略被修饰的int。(C99标准新增) 
    
signed:修饰整型数据,有符号数据类型。(C89标准新增) 
    
unsigned:修饰整型数据,无符号数据类型。(K&R时期引入) 
    
// restrict:用于限定和约束指针,并表明指针是访问一个数据对象的唯一且初始的方式。(C99标准新 增)

[3] 复杂类型关键字

struct:结构体声明。(K&R时期引入) union:联合体声明。(K&R时期引入) enum:枚举声明。(C89标准新增) typedef:声明类型别名。(K&R时期引入) sizeof:得到特定类型或特定类型变量的大小。(K&R时期引入) // inline:内联函数用于取代宏定义,会在任何调用它的地方展开。(C99标准新增)

[4] 存储级别关键字

auto:指定为自动变量,由编译器自动分配及释放。通常在栈上分配。与static相反。当变量未指定时默认 为auto。(K&R时期引入) static:指定为静态变量,分配在静态变量区,修饰函数时,指定函数作用域为文件内部。(K&R时期引入) register:指定为寄存器变量,建议编译器将变量存储到寄存器中使用,也可以修饰函数形参,建议编译器 通过寄存器而不是堆栈传递参数。(K&R时期引入) extern:指定对应变量为外部变量,即标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数 时在其他模块中寻找其定义。(K&R时期引入) const:指定变量不可被当前线程改变(但有可能被系统或其他线程改变)。(C89标准新增) volatile:指定变量的值有可能会被系统或其他线程改变,强制编译器每次从内存中取得该变量的值,阻止 编译器把该变量优化成寄存器变量。(C89标准新增)

[5] 流程控制关键字

1)跳转结构

return:用在函数体中,返回特定值(如果是void类型,则不返回函数值)。(K&R时期引入) continue:结束当前循环,开始下一轮循环。(K&R时期引入) break:跳出当前循环或switch结构。(K&R时期引入) goto:无条件跳转语句。(K&R时期引入)

2分支结构

if:条件语句,后面不需要放分号。(K&R时期引入) else:条件语句否定分支(必须与if连用)。(K&R时期引入) switch:开关语句(多重分支语句)。(K&R时期引入) case:开关语句中的分支标记,与switch连用。(K&R时期引入) default:开关语句中的“其他”分支,可选。(K&R时期引入)
  1. 循环结构

    for:循环(K&R时期引入) while:循环(K&R时期引入)
    do while
    

变量和常量

常变量

const

字符变量:

char s = 'a';

sizeof(char) = 1;

程序结构和过程控制

C语言程序结构

  1. 顺序结构

  2. 选择结构

  3. 循环结构

  *顺序结构**:***就是指按照语句在程序中的先后次序一条一条的顺次执行,且每个语句都会被执行到。先执行A模块,再执行B模块。

  *选择结构:*选择语句又称为*分支语句*,它通过对给定的条件进行判断,从而决定执行两个或多个分支中的哪一支。 当条件P的值为真时执行A模块,否则执行B模块。

  *循环结构:*是在某些条件的控制下重复执行一段代码语句,让程序“杀个回马枪”,当满足循环条件时执行循环语句,否则不执行循环语句。

选择(分支)结构:

if(表达式:true/false){

语句1;

}

如果表达式的值为true,则执行语句1

如果表达式的值为false,则跳过该if语句,执行后续语句

2.1.2 双重分支结构

如果 if 表达式的值为true(非0),将执行 if 语句(一个或一组)后的语句块。如果 if 表达式值为false(0),那么控制权将交给else 表达式后面的语句。else语句是可选的。 仅当 if 表达式的值为false时,才会执行else后的语句或语句序列。

// 仅当 表达式1 的值为true才会执行语句1,如果表达式的值为false,则执行else中的语句2
if(表达式1){       
    语句1;
}
else{
    语句2;
}

2.1.3 多重分支结构

首先判断条件1是否为真,若为真,则执行语句1并跳出,若为假则继续判断条件2是否为真,若条件2为真则执行语句2并跳出,否则继续判断条件3。以此类推。

if(表达式1){
    语句-1;
}
else if(表达式2){
    语句-2;
}
else if(表达式3){
    语句-3;
}
else{
    语句-4;
}

示例3:

程序输入成绩,为0~100之间的整数。之后输出成绩代表的分数段:

90~100为优,80~89为良,70~79为中,60~69为及格,0~59为不及格,其他则输出错误信息.

int main(){
    int nScore;
    while(1){
        printf("请输入学生成绩: ");
        scanf("%d", &nScore);   
​
        if(nScore >= 90 && nScore <= 100){
            printf("优秀!\n");
        }
        else if(nScore < 90 && nScore >= 80){
            printf("良好!\n");
        }
        else if(nScore < 80 && nScore >= 70){
            printf("中等!\n");
        }
        else if(nScore < 70 && nScore >= 60){
            printf("及格!\n");
        }
        else if(nScore < 60 && nScore >= 0){
            printf("不及格!\n");
        }
        else{
            printf("输入错误,请重新输入!\n");
        }
    }
​
    return 0;
}

2.1.4 if语句嵌套使用

在if语句中又包含一个或多个if语句称为if语句嵌套,其形式一般如下:

需要注意的是if和else的配对问题,一个匹配原则:在嵌套if语句中,else总与它上面最近的if配对

示例4:

输入一个整数,判断这个整数是正整数、0还是负整数

int main(){
    printf("请输入一个整数: ");
    int n;
    while(1){
        scanf("%d", &n);
        // if(n>0){
        //     printf("正数!\n");
        // }
        // else if(n<0){
        //     printf("负数!\n");
        // }
        // else{
        //     printf("%d\n", n);
        // }
        if(n>0){
            printf("正数!\n");
        }
        else{
            if(n<0){
                printf("负数!\n");
            }
            else{
                printf("%d\n", n);
            }
        }
    }
​
    return 0;
}

练习1:输入一个年份(正整数),判断这年是否是闰年

闰年判断标准:年份能被4整除;如若遇到100的倍数,则需判断年份能否被400整除。(逢4一闰,逢百不闰,逢400又闰)如1900年是不是闰年,1904年是闰年,2000年是闰年

多分支结构

if-else语句只能判断2个分支,若要判断多个分支则需要if-else的多次使用或嵌套使用,程序会变得很复杂,可读性差。switch是多分支选择语句。通过switch()的多分支判断可以简便地实现多分支选择结构

switch()语句的一般形式如下:

switch(表达式){
case 常量表达式1:
    语句-1;
    break;      // 如果break不写,会顺序执行,每个case都会执行一次
case 常量表达式2:
    语句-2;
    break;
case 常量表达式3:
    语句-3;
    break;
case 常量表达式4:
    语句-4;
    break;
default:
    语句-5;
    break;  // 可写可不写
}

说明:

[1] switch(表达式)表达式的值应是一个整数(包括字符数据)

[2] switch()下的{}是一段语句块,这段语句包含若干个以case开头的语句块和至多一个以default开头的语句块

[3] case后需要一个常量(或常量表达式)。

首先判断switch(表达式)的表达式的值,之后与各个case之后的值进行比对,如果某个case后的值与表达式的值相同,则跳转到此case语句;如果所有的case都不匹配,则跳转到default后的语句。

[4] 可以没有default语句。若没有default语句,则如果没有匹配的case,则程序不执行任何语句

[5] 每个case语句后的常量值必须各不相同,否则会发生互相矛盾现象

示例:程序输入成绩,为0~100之间的整数。之后输出成绩代表的分数段(用switch语句完成):

90~100为优,80~89为良,70~79为中,60~69为及格,0~59为不及格,其他则输出错误信息

循环结构

什么是循环?

循环是程序中重复执行,直到满足指定条件才停止的一段代码;

C语言中,控制循环执行的条件在编码时用到了关系和逻辑运算符;

如果一直循环,无法退出则成为了死循环。

3.1 while循环

while循环在执行循环前检查条件,条件表达式一般为关系表达式或者逻辑表达式。只要表达式为真循环就会迭代,否则退出循环。循环体可以是空语句、一个简单的语句或语句块,如果while循环中包含一组语句,必须用{}括起来

// 如果表达式的值为真,则执行语句1,否则就不执行语句
while(表达式){
    语句1;        // 
}
​
while(表达式){}    // 空循环
while(表达式);     // 空循环
​
// 如果while循环中只有一句语句,则不需要加大括号(不推荐)
while(表达式)
    语句2;
​
// 如果while循环中有一个语句块(不止一个语句),则必须要加大括号将语句块括起来
while(表达式){
    语句3;
    语句4;
    ...
}

3.2 do-while循环

先执行一次指定的循环体语句,然后判别表达式,当表达式的值为非零(“真”) 时,返回重新执行循环体语句,如此反复,直到表达式的值等于0为止,此时循环结束;while后面的分号不能省略:do {} while();

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

示例:求1+2+3+……+100=?

int main(){
    int sum = 0, i = 0;
    do{
        sum += i;
        i++;
    }while(i<=100);
    printf("sum = %d\n", sum);
​
    return 0;
}

3.3 for循环

for(表达式1; 表达式2; 表达式3){
    循环体;    // 如果循环体只有一句话,则大括号可以省略(不推荐),否则大括号必须存在
}

表达式1:循环的初始条件,只执行一次。可以为0个、1个或多个变量设置初值

表达式2:判断循环结束的条件。在每次执行循环体前判断此表达式,若表达式为真则进入循环,否则不执行循环

表达式3:作为循环的调整(即改变循环状态),在执行完循环体之后执行

注意:

[1] for()括号内的3个表达式的分隔符是分号 ";" 不是逗号 " , "

[2] for()括号内的3个表达式都可以同时省略,但是不能省略分号 ";",当省略表达式2时,程序将陷入死循环。

示例:求1+2+3+……+100=?

int main(){
    int sum = 0;
    // C99/C11支持这么写
    // i=1,设置这个循环的初始值(注意:这个部分只执行一次)
    // i<=100, 设置这个循环的结束条件(循环体执行之前需要先判断)
    // i++,改变整个循环的状态(等到循环体结束之后,再执行该语句)
    for(int i = 1; i <= 100; i++){
        sum += i;
    }
    printf("sum = %d\n", sum);
​
    return 0;
}

示例: 用for循环求奇数和,偶数和

#include <>
​
int main(){
    int sum = 0;
    int sum2 = 0;
    for(int i=0; i<=100; i++){
        if(i%2==0){ // 偶数
            sum = sum + i;
        }
        else{
            sum2 = sum2 + i;
        }
    }
    printf("偶数和:sum = %d\n", sum);
    printf("奇数和:sum2 = %d\n", sum2);
​
    return 0;
}

死循环:

while(1){}

while(1);

do{}while(1);

for循环的死循环:for(;;);

4.其他控制语句

4.1 break

break语句的用途:

用于在 switch 语句中终止case。

用来从循环体内跳出循环体,即提前结束循环,接着执行循环下面的语句。

注意:

break语句不能用于循环语句和switch语句之外的任何其他语句中。

多层循环中,break只向外跳一层

示例:从r=1开始,输出所有半径是正整数的圆的面积,直至出现面积大于100为止

4.2 continue

continue语句的作用:

结束本次循环,即跳过循环体中下面尚未执行的语句,接着进行下一次是否执行循环的判定.

注意:

在while循环和do…while循环中,程序控制权传递给条件测试语句;

在for循环中,continue影响循环的增量部分,然后执行条件测试;

/**
 *  输出100-200以内所有不能被3整除的整数 
 */
int main(){
    // for(int i=0; i<=30; i++){
    //     if(i%3){
    //         printf("%d  ", i);
    //     }    
    // }
    for(int i=100; i<=200; i++){
        if(i%3 == 0){
            continue;
        }  
        printf("%d  ", i);  
    }
    printf("\n");
    return 0;
}

4.3 return(有关return语句的用法将在函数课程中详细讲解)

return语句说明:

结束当前函数,并将返回值返回给函数调用的位置。

return语句的用法:

return 返回值;

其中返回值由函数类型决定。如main()函数是int类型,则需要返回一个整数。如果函数是void类型则无需写返回值。

注意:

表达式是可选的。一个函数中可以使用多个return语句,但是遇到第一个return语句时返回

4.4 goto

goto语句说明:

goto语句为无条件转向语句;

goto语句的用法:

goto 标签;

当执行到goto语句时,程序会跳转到同一函数内goto语句所指向的标号处,例如下图,goto语句执行后,程序会自动跳转到label_1标签处。标签的命名规则与C语言标识符的命名规则相同。

实例:

用goto语句实现1+2+3....+100?

int main(){
    int sum;
label:
    // ...注意:在同一个函数内
    goto label;
    return 0;
}

注意:

用不用goto一直是一个著名的争议话题,滥用goto语句会使程序无规律、可读性差。goto语句违背了C语言的模块化编程的基本思想,因此goto语句不推荐使用。

Linux内核源代码中对goto的应用非常广泛,但是一般只限于错误处理中。

1.一维数组及相关概念

我们之前所学过的数据类型都属于基本数据类型(整型、浮点型、字符型)。实际上C语言不仅仅可以操作基本数据类型,还可以操作构造数据类型(如结构体、数组等)。

1.1 什么是数组

数组(Array)是一组 有序的、类型相同的数据的集合,这些数据被称为数组的元素(Element)

1.2 一维数组的定义

数组定义的语法:

存储类型 数据类型 数组名[数组长度] ;

说明:

l 存储类型:auto,register,static,extern。若省略,相当于auto

l 数据类型:可以是任何一种基本数据类型或构造数据类型

l 数组名:每个数组都有一个名字,我们称之为数组名数组名代表数组的起始地址。数组名应当符合标识符的命名规则,即以字母、数字、下划线组成,但不能以数字开头。

l 数组长度:所包含的数据的个数称为数组长度(Length)

数组长度一般只能是常量和常量表达式(但在C99新增了变长数组VLA)

示例:一个拥有10个元素的int型数组a

int a[10];

注意:数组必须先定义后使用1.3一维数组元素的引用

C语言规定数组必须逐个元素引用,不能整体引用。

数组元素由索引下标(Index)标识,索引或下标从0开始。

数组元素的表示方法:

数组名[下标]

例如:int a[10];

其元素是a[0]、a[1]、a[2]、a[3]、a[4]、a[5]、a[6]、a[7]、a[8]、a[9]

注意:C语言对数组不作越界检查,使用时要注意

1.4 一维数组内存分配

数组在定义后,就在内存中划分了一块连续的空间用于存储数组。以int a[n]为例(n大于0):这块空间的大小是sizeof(int)*n,划分成n块来顺序存储a[0]~a[n-1]。数组名代表这块空间的首地址(也就是a[0]的地址)

示例:定义一个数组,反序输出

int arr[10] = {};

在数组中输入10个元素

练习1:输入数字n,使用数组存储斐波那契数列前n项,并输出

练习2:从键盘输入10个学生的成绩,如果遇到大于100或者小于0的成绩需要提示输入错误重新输入。之后计算10个学生的总成绩和平均成绩。

#include <>
​
int main(){
    int score[10];
    for(int i=0; i<10; i++){
        scanf("%d", &score[i]);
        if(score[i] < 0 || score[i] > 100){
            printf("分数输入错误,请重新输入!\n");
            i--;
            continue;
        }
    }
​
    int totalScore = 0;   // stack -- 随机值
    for(int i=0; i < 10; i++){
        totalScore += score[i];
    }
    printf("学生总分为: %d\n", totalScore);
    printf("平均成绩为: %f\n", (float)totalScore/10);
​
    return 0;
}

1.6 变长数组

C99新增了变长数组(variable-length array, VLA),允许使用变量表示数组的长度。

例如:

int lenth = 5; 
int array[lenth];

注意:变长数组不能改变大小

变长数组中的”变”不是指可以修饰已创建数组的大小,一旦创建了变长数组,它的大小则保持不变。这里的”变”指的仅仅是:在创建数组时,可以使用变量指定数组的长度。

1.7 一维数组使用实例

1.7.1冒泡排序

冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

算法描述:

①  比较相邻的元素。如果第一个比第二个大(小),就交换它们两个;

②  对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大(小)的数;

③  针对所有的元素重复以上的步骤,除了最后一个;

④  重复步骤1~3,直到排序完成。

示例:从键盘输入10个各不相同的整数,存储在数组中,使用冒泡排序法将数组排序并输出

#include <>
#define Max 10
​
int main(){
    int arr[Max], tmp;
    printf("请输入10个数:\n  ");
    for(int i=0; i<Max; i++){
        scanf("%d", &arr[i]);
    }
​
    // 1. 从数组中找出最大的元素(设置第一个元素为最大的元素)
    for(int i=0; i<Max-1; i++){
        for(int j=0; j<Max-1-i; j++){
            if(arr[j] > arr[j+1]){
                tmp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = tmp;
            }
        }
    }
​
    printf("排序完成: ");
    for(int i=0; i<Max; i++){
        printf("%d  ", arr[i]);
    }
    printf("\n");
​
    return 0;
}

2. 二维数组及相关概念

在实际问题中有很多数据是二维的或多维的,因此 C 语言允许构造多维数组。

2.1多维数组

一维数组只有一个下标。具有两个或两个以上下标的数组称为多维数组(multidimensional array)。

其说明的一般形式如下:

<存储类型> <数据类型> <数组名>…[<常量表达式n>] ;

例如:double b3[5]; //定义了一个三维双精度实型数组b

2.2 二维数组的定义

二维数组的声明方式:

数据类型 数组名常量表达式

示例:

int a2; //定义一个2*3的二维int型数组

float f3; //定义一个3*4的二维float型数组

2.3 二维数组的存储方式

二维数组常常被称为矩阵(matrix)。把矩阵想成行(row)和列(column)的排列方式,更有助于形象化地理解二维数组地逻辑结构。

虽然二维数组在概念上可以理解是二维的,其下标在两个方向上变化,有行和列的说法。但是内存却是连续编址的,按一维线性排列的。如何在一维的存储器中存放二维数组?

存储形式:二维数组在内存中是按行的顺序存放的,即先存放第一行的元素,再存放第二行的元素,……。

2.4 深入理解多维数组(二维数组)

在C语言中我们可以将二维数组视为一种特殊的一维数组,它的元素又是一个一维数组。例如,上图的二维数组 int a3。可以理解成由三个元素a[0],a[1],a[2]组成的数组,每个元素a[i]是包含四个元素的一维数组:

因此在C语言中的多维数组其实就是元素为数组的数组。n 维数组的元素是 n-1 维数组。例如,二维数组的每个元素都是一维数组,一维数组的元素当然就不是数组了。

示例:char screen10[80]; // 一个三维数组

数组 screen 包含 10 个元素,从 screen[0] 到 screen[9]。每个元素又是一个二维数组,它有 40 个元素,这 40 个元素均是一维数组,然后每个一维数组内都有 80 个字符。整体来说,screen 数组有 32000(10×40×80)个 char 类型元素。

2.5 二维数组元素的引用及初始化

2.5.1二维数组元素的引用

数组名行下标

例如:ary1 = 12;

2.5.2 分行初始化

分行给二维数组赋值。例如:

int a3={{1,2,3,4},{5,6,7,8},{9,10,11,12}};

每行的元素使用花括号分隔开,中间用逗号分隔开,如果有未写的则默认为0。

2.5.3 线性初始化

把所有的元素都写在一个花括号内,这样会按照数组在内存中的存储顺序给二维数组赋值。例如:

int a3={1,2,3,4,5,6,7,8,9,10,11,12};

类似于一维数组,如果有未写的则默认为0。

2.5.4 全部元素初始化

可以提供全部元素的初值,这样常量表达式1(即第一个下标)可以缺省不写,系统会根据输入的多少来计算行数。但常量表达式2(即第二个下标)不可缺省。例如:

int a={1,2,3,4,5,6,7,8,9,10,11,12};

则系统自动计算出这个二维数组是a3

注意:第一维的长度可以省略,但是第二维长度不能省,例如:

int a3={1,2,3,4,5,6,7,8,9,10,11,12};

编译程序时,会有语法错误

2.6 二维数组程序实例

示例:自定义一个3*4的矩阵,输出矩阵中值最大的元素,并输出其数组下标

#include <>
#define row 3
#define col 4
​
int main(){
    int arr[row][col] = {1,4,7,2,5,8,3,6,9,11,10,0};
    int nMax = arr[0][0];
    int x, y;
    for(int i=0; i<row;i++){
        for(int j=0; j<col; j++){
            if(nMax <= arr[i][j]){
                nMax = arr[i][j];
                x = i;
                y = j;
            }
        }
    }
    printf("最大值为: arr[%d][%d] = %d\n", x, y, nMax);
​
    return 0;
}