蓝桥杯第一周-C语言的学习

初始 C 语言

C 语言程序的基本框架

想象一下,C 语言程序就像一本书,它有一个固定的结构。首先,我们需要包含一些头文件,这些头文件就像是书的目录,告诉编译器我们需要使用哪些库函数。然后,我们可能会声明一些变量,这些变量就像是书中的角色,用来存储数据。最后,main 函数是书的正文,程序从这里开始执行。

 #include <stdio.h>   // 引入标准输入输出库,比如printf和scanf
 #include <stdlib.h>  // 引入标准库,包含一些常用函数,比如内存分配
 ​
 // 变量声明区域:在这里声明全局变量,它们在整个程序中都可以使用
 int a;
 int b;
 ​
 int main()   // 主函数,每个C程序有且只有一个main函数,程序从这里开始执行
 {
     // 语句执行区域:我们在这里编写程序的主要逻辑
 ​
     return 0;   // 返回0表示程序正常结束。如果main函数返回类型是void,则不需要返回值
 }

数据的基本类型

在 C 语言中,数据有不同的类型,就像我们生活中有不同种类的东西一样。比如,整数、小数、字符等。C 语言提供了几种基本数据类型:

  • 整型 (int):用来表示整数,比如 1, 2, -3, 0。

  • 浮点型 (float):用来表示小数,比如 3.14, -0.001。

  • 字符型 (char):用来表示单个字符,比如 ‘A’, ‘b’, ‘0’, ‘?’。

  • 布尔型 (bool):用来表示真或假,C 语言中通常用 0 表示假,非 0 表示真(但 C99 标准引入了_Bool 类型,以及头文件 stdbool.h 中定义了 true 和 false)。

 int a;        // 声明一个整型变量a
 float b;      // 声明一个浮点型变量b
 bool c;       // 声明一个布尔型变量c,需要包含stdbool.h头文件
 char d;       // 声明一个字符型变量d,只能存储一个字符

运算符

基本运算符

  • 算术运算符:+(加), -(减), *(乘), /(除)

    • 注意:当两个整数相除时,结果也是整数,小数部分会被丢弃(例如 5/2 结果是 2)。
  • 关系运算符:>(大于), <(小于), >=(大于等于), <=(小于等于), !=(不等于), ==(等于)

特殊运算符

  • 自增自减运算符:++(自增), --(自减)

    • 前置(如 ++a):先自增,然后使用自增后的值。

    • 后置(如 a++):先使用原来的值,然后再自增。

 int x = 2, y = 3;
 x = y++;   // 先把y的值(3)赋给x,然后y自增变成4。所以x=3, y=4
 x = ++y;   // 先让y自增变成4,然后把y的值(4)赋给x。所以x=4, y=4
  • 取余运算符:%,只能用于整数,求两个整数相除的余数。
 int a = 20;
 int remainder = a % 3;   // 20除以3的余数是2,所以remainder=2
  • 逗号运算符:,,会按顺序执行多个表达式,但整个逗号表达式的值是最后一个表达式的值。
 int a, b;
 a = (b=2, b+3);   // 先执行b=2,然后计算b+3(值为5),然后赋给a。所以a=5

占位符

在输出函数 printf 和输入函数 scanf 中,我们使用占位符来指定数据的格式。

  • %d:整数

  • %f:浮点数

  • %s:字符串

  • %c:字符

转义字符

转义字符以反斜杠 \ 开头,用来表示一些特殊字符。比如,我们想输出一个换行,就可以用 \n

转义字符 含义 示例
\a 警报 发出蜂鸣声
\b 退格 将光标后退一个字符
\f 换页 在打印机上换页,终端上可能会清屏或换页
\n 换行 将光标移动到下一行的开头
\r 回车 将光标移动到当前行的开头,不换到下一行
\t 水平制表 相当于按 Tab 键,跳到下一个制表位
\v 垂直制表 将光标移动到下一个垂直制表位
\\ 反斜杠 输出一个反斜杠
\' 单引号 输出一个单引号
\" 双引号 输出一个双引号
\? 问号 输出一个问号(用于避免三字符组)
\0 空字符 字符串结束标志

示例:

 #include <stdio.h>
 ​
 int main() {
     printf("Hello,\n\tWorld!\n");   // 输出:Hello, 然后换行,然后缩进,再输出World!,再换行
     printf("他说:\"C语言很有趣!\"\n");   // 输出:他说:"C语言很有趣!"
     printf("字符:\'A\'\n");         // 输出:字符:'A'
     printf("反斜杠:\\\n");          // 输出:反斜杠:\
     printf("警报:\a\n");            // 会发出蜂鸣声
     return 0;
 }

最小栏宽和有效位数

在输出时,我们可以控制输出的格式,比如占用的宽度和小数位数。

  • 对于整数:%n.md

    • n:最小栏宽,如果输出的整数位数不足 n,则在左侧补空格(右对齐),如果超过 n,则按实际位数输出。

    • m:有效位数,如果输出的整数位数不足 m,则在左侧补 0,如果超过 m,则按实际位数输出。

  • 对于浮点数:%n.mf

    • n:最小栏宽,包括小数点和小数部分。如果输出的浮点数位数不足 n,则左侧补空格(右对齐),如果超过,则按实际位数输出。

    • m:小数点后的位数,如果小数部分位数不足 m,则在右侧补 0,如果超过,则四舍五入到 m 位。

示例:

 int a = 123;
 float b = 3.14159;
 printf("%5d\n", a);      // 输出:  123(前面有两个空格)
 printf("%05d\n", a);     // 输出:00123(前面补0)
 printf("%.2f\n", b);     // 输出:3.14
 printf("%8.3f\n", b);    // 输出:   3.142(前面有3个空格,因为总宽度8,小数3位,四舍五入)

运算符的补充

赋值和复合赋值

除了基本的赋值运算符 =,还有复合赋值运算符,它们将算术运算符和赋值结合起来。

int i = 10;

i += 2; // 相当于 i = i + 2; → i=12

i *= 3; // 相当于 i = i * 3; → i=36

逻辑运算符

逻辑运算符用于连接多个条件,常用于条件判断。

  • 逻辑与(&&):两个条件都为真,结果才为真。

  • 逻辑或(||):两个条件至少一个为真,结果就为真。

  • 逻辑非(!):将真变假,假变真。

 int a = 5, b = 10;
 if (a > 0 && b > 0) {
     printf("a和b都大于0\n");
 }
 if (a > 10 || b > 10) {
     printf("a或b有一个大于10\n");
 }
 if (!(a > 10)) {
     printf("a不大于10\n");
 }

选择语句

if-else 语句

if-else 语句就像我们生活中的选择:如果条件成立,就做某件事;否则,做另一件事。

 if (条件) {
     // 如果条件为真,执行这里的代码
 } else {
     // 如果条件为假,执行这里的代码
 }

注意:如果 if 或 else 后面只有一条语句,可以省略花括号,但为了代码清晰,建议始终使用花括号。

嵌套 if-else

我们可以使用多个 if-else 来处理多种情况。

 if (条件1) {
     // 条件1为真时执行
 } else if (条件2) {
     // 条件1为假且条件2为真时执行
 } else {
     // 条件1和条件2都为假时执行
 }

条件运算符(三目运算符)

条件运算符 ? : 是一种简洁的条件表达式。

 表达式1 ? 表达式2 : 表达式3;

如果表达式 1 为真,则整个表达式的值为表达式 2 的值,否则为表达式 3 的值。

c

 int a = 2, b = 3, max;
 max = (a > b) ? a : b;   // 如果a大于b,max等于a,否则等于b

布尔值

在 C 语言中,我们通常用 0 表示假,非 0 表示真。C99 标准引入了_Bool 类型,以及头文件 stdbool.h 中定义了 true 和 false。

 #include <stdbool.h>
 ​
 _Bool a = true;   // 或者1
 _Bool b = false;  // 或者0

switch 语句

switch 语句就像是一个多路开关,根据表达式的值跳转到不同的 case。

 switch (表达式) {
     case 常量1:
         语句1;
         break;
     case 常量2:
         语句2;
         break;
     ...
     default:
         语句;   // 如果上面的case都不匹配,执行default
 }

注意:每个 case 后面必须是一个常量表达式,且值不能重复。break 语句用于跳出 switch,如果没有 break,程序会继续执行下一个 case,直到遇到 break 或 switch 结束。

循环语句

while 循环

while 循环就像是一个重复检查的条件:只要条件为真,就重复执行循环体。

while (条件) {

// 循环体

}

示例:打印 5 到 1

 int i = 5;
 while (i > 0) {
     printf("%d\n", i);
     i--;
 }

do-while 循环

do-while 循环先执行循环体,然后再检查条件。所以,循环体至少执行一次。

 do {
     // 循环体
 } while (条件);

示例:计算整数位数

 int num, count = 0;
 printf("请输入一个整数:");
 scanf("%d", &num);
 ​
 do {
     num /= 10;
     count++;
 } while (num > 0);
 ​
 printf("这个数有%d位\n", count);

for 循环

for 循环将初始化、条件判断和更新放在一起,非常适合已知循环次数的场景。

 for (初始化; 条件; 更新) {
     // 循环体
 }

示例:打印 5 到 1

 for (int i = 5; i > 0; i--) {
     printf("%d\n", i);
 }

goto 语句

goto 语句可以跳转到程序中的任意标签位置。但是,过度使用 goto 会使程序难以理解,应尽量避免。

 goto label;
 ​
 // 一些代码...
 ​
 label:
 // 代码

跳转语句 break 和 continue

  • break:立即跳出当前循环(或 switch 语句)。

  • continue:跳过当前循环的剩余部分,直接进入下一次循环。

数组

数组就像是一排连续的盒子,每个盒子可以存储相同类型的数据。我们可以通过索引(从 0 开始)来访问每个盒子。

一维数组

声明一个数组需要指定类型和大小:

 类型 数组名[大小];

示例:

 int numbers[5];   // 声明一个包含5个整数的数组

初始化数组:

 int numbers[5] = {1, 2, 3, 4, 5};
 int numbers[] = {1, 2, 3, 4, 5};   // 编译器会自动计算大小为5

访问数组元素:

 numbers[0] = 10;   // 第一个元素赋值为10
 printf("%d", numbers[0]);   // 输出10

多维数组

多维数组,比如二维数组,可以看作是一个表格。

 int matrix[3][3] = {
     {1, 2, 3},
     {4, 5, 6},
     {7, 8, 9}
 };

访问二维数组元素:

 matrix[1][2] = 10;   // 第二行第三列赋值为10

指针

指针就像是一个地址簿,它存储的是变量的内存地址。通过指针,我们可以间接访问和修改变量的值。

指针的基本概念

声明指针:

 类型 *指针变量名;

示例:

 int a = 10;
 int *p = &a;   // p是一个指向整型的指针,存储了a的地址

通过指针访问变量:

 printf("%d", *p);   // 输出10,*p表示取p指向的内存地址的值
 *p = 20;            // 通过指针修改a的值为20

指针和数组

数组名实际上就是一个指针常量,它指向数组的第一个元素。

 int arr[5] = {1,2,3,4,5};
 int *p = arr;   // p指向数组的第一个元素
 ​
 // 通过指针访问数组元素
 printf("%d", *p);        // 输出1
 printf("%d", *(p+1));    // 输出2,p+1指向第二个元素

指针的运算

指针可以进行加减运算,移动的距离是指针所指向类型的大小。

 int arr[5] = {1,2,3,4,5};
 int *p = arr;
 p++;   // 现在p指向第二个元素

函数封装

函数就像是一个黑盒子,它接收输入(参数),执行一系列操作,然后返回一个结果。使用函数可以使代码更模块化,易于维护。

函数的定义

 返回类型 函数名(参数列表) {
     // 函数体
     return 返回值;   // 如果返回类型为void,则不需要return
 }

示例:一个加法函数

 int add(int a, int b) {
     return a + b;
 }

函数的调用

 int result = add(3, 4);   // 调用add函数,传入3和4,返回值7赋给result

函数的参数传递

在 C 语言中,函数参数默认是值传递,也就是说,函数内对参数的修改不会影响外面的变量。如果想要修改外面的变量,可以传递指针。

示例:交换两个变量的值

 void swap(int *a, int *b) {
     int temp = *a;
     *a = *b;
     *b = temp;
 }
 ​
 int main() {
     int x = 10, y = 20;
     swap(&x, &y);   // 传递x和y的地址
     printf("x=%d, y=%d", x, y);   // 输出x=20, y=10
     return 0;
 }

递归函数

函数可以调用自身,这称为递归。递归需要有一个终止条件,否则会无限递归。

示例:计算阶乘

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