初始 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);
}
}