系列说明:在学习阶段我就开始着手C语言的笔记并发表博客,本文是对我学习阶段C语言笔记的整理归纳,希望和大家共同进步。什么地方有遗漏欢迎大家指出。
运行环境:本系列所有演示代码均在 Visual Studio 2022(x64)环境下编写。
目录
-
- 初识C语言:hello world 与开发环境
-
- 数据类型
-
- 变量与常量
-
- 输入输出:printf / scanf
-
- 操作符
-
- 分支与循环
-
- 数组
-
- 本章小结
正文
1. 初识C语言:hello world 与开发环境
C语言是一门面向过程的编程语言,main 函数是程序的入口,程序总是从 main 函数开始执行。在 VS2022 中新建"空项目",添加 .c 源文件后即可编写代码。
demo1_第一个C程序 - //第一个C程序:hello world
- #include <stdio.h>
- int main()
- {
- // 在屏幕上打印一行文字
- printf("hello world\n");
- return 0;
- }
复制代码预期终端输出:
说明:
- #include 是"标准输入输出"头文件,printf 的声明在其中。
- \n 是换行转义字符。
- return 0; 表示程序正常结束。
2. 数据类型
C语言为不同类型的数据提供了不同的类型,数据类型决定了数据的大小与含义。
| 类型 | 大小(64位VS2022) | 说明 |
|---|
| char | 1 字节 | 字符型,也可存储小整数 | | short | 2 字节 | 短整型 | | int | 4 字节 | 整型 | | long | 4 字节 | 长整型(VS中与int相同) | | long long | 8 字节 | 更长的整型 | | float | 4 字节 | 单精度浮点型 | | double | 8 字节 | 双精度浮点型 |
sizeof 是操作符(不是函数),用来计算一个类型或变量所占内存大小,返回 size_t 类型,用 %zu 打印。由于 sizeof 是操作符,括号内的表达式不会被执行。
demo2_sizeof.c —— 计算各类型大小 - // demo2_sizeof.c - sizeof计算各数据类型大小(单位:字节)
- #include <stdio.h>
- int main()
- {
- // sizeof 是操作符,返回 size_t 类型,用 %zu 打印
- printf("char = %zu\n", sizeof(char));
- printf("short = %zu\n", sizeof(short));
- printf("int = %zu\n", sizeof(int));
- printf("long = %zu\n", sizeof(long));
- printf("long long = %zu\n", sizeof(long long));
- printf("float = %zu\n", sizeof(float));
- printf("double = %zu\n", sizeof(double));
- return 0;
- }
复制代码预期终端输出: - char = 1
- short = 2
- int = 4
- long = 4
- long long = 8
- float = 4
- double = 8
复制代码
3. 变量与常量
C语言中,经常变化的值称为变量,不变的值称为常量。变量在定义时需指定数据类型。
3.1 全局变量与局部变量
- 局部变量:在大括号内部定义的变量,作用域为所在的局部范围,进入作用域创建、出作用域销毁。
- 全局变量:在大括号外部定义的变量,作用域为整个工程,生命周期与程序一致。
当局部变量与全局变量同名时,局部变量优先使用。
demo3_variable.c —— 变量的分类与作用域 - // demo3_variable.c - 全局变量与局部变量、作用域、生命周期
- #include <stdio.h>
- int global = 300; // 全局变量:在函数外部定义,作用于整个工程
- int main()
- {
- int local = 200; // 局部变量:在函数内部定义
- // 局部变量与全局变量同名时,局部变量优先使用
- int global = 100;
- printf("local = %d\n", local);
- printf("global = %d\n", global);
- return 0;
- }
复制代码预期终端输出:
3.2 内存区域
一般我们会关注内存中的三个区域:
- 栈区:存放局部变量(进入作用域创建,出作用域销毁)。
- 堆区:用于动态内存管理(后续文章详解)。
- 静态区:存放全局变量和 static 修饰的变量。
4. 输入输出:printf / scanf
4.1 printf 函数
printf 是格式化输出函数,使用 % 加占位符来格式化输出。
| 占位符 | 含义 |
|---|
| %d | 整型 | | %f / %lf | float / double | | %c | 字符 | | %s | 字符串 | | %zu | size_t 类型 | | %p | 地址 | | %% | 打印百分号 | | %5d | 最少5位,不足左侧补空格 | | %-5d | 最少5位,不足右侧补空格 | | %.2f | 保留2位小数 | | \n \t \\ \" \' | 换行、制表、反斜杠、双引号、单引号转义 |
printf 的返回值是打印成功的字符个数。
demo4_printf.c —— 占位符与转义字符 - // demo4_printf.c - printf 占位符与转义字符
- #include <stdio.h>
- int main()
- {
- int n = 456;
- double d = 3.14159;
- // 基本占位符
- printf("%d\n", n); // 整型
- printf("%.2f\n", d); // 浮点型保留2位小数
- printf("%5d\n", n); // 最少5位,不足左侧补空格
- printf("%-5d|\n", n); // 最少5位,不足右侧补空格
- printf("%%d\n"); // 打印百分号
- printf("Tab:\tHi\n"); // 制表符
- printf("C:\\temp\n"); // 反斜杠转义
- // printf 的返回值是打印的字符个数(链式)
- printf("%d\n", printf("%d\n", printf("%d\n", n)));
- return 0;
- }
复制代码预期终端输出: - 456
- 3.14
- 456
- 456 |
- %d
- Tab: Hi
- C:\temp
- 456
- 4
- 2
复制代码
4.2 scanf 函数
scanf 是格式化输入函数。注意:scanf 需要把输入存放到地址中,所以基本类型变量前要加取地址符 &(数组名本身就是地址,不加 &)。
scanf 的返回值是成功读取的数据个数;读取错误或遇到文件结尾返回 EOF。
demo5_scanf.c —— scanf 基本用法与返回值 - // demo5_scanf.c - scanf 基本用法与返回值
- #define _CRT_SECURE_NO_WARNINGS 1 // vs2022中使用scanf需要添加这一行代码
- #include <stdio.h>
- int main()
- {
- int a = 0;
- int b = 0;
- char ch = 0;
- char str[20] = { 0 };
- // 输入两个整数
- printf("请输入两个整数: ");
- int ret = scanf("%d %d", &a, &b);
- printf("成功读取个数 = %d, a = %d, b = %d\n", ret, a, b);
- // 读取单个字符(前面加一个空格跳过空白字符)
- printf("请输入一个字符: ");
- scanf(" %c", &ch);
- printf("ch = %c\n", ch);
- // 读取一行字符串(读到换行停止,用于含空格的字符串)
- // 注意:%c 读取后缓冲区残留换行,%[^\n] 前加空格可跳过空白
- printf("请输入一行字符串: ");
- scanf(" %[^\n]s", str);
- printf("str = %s\n", str);
- return 0;
- }
复制代码预期终端输出(键盘依次输入 10 20、A、hello world): - 请输入两个整数: 成功读取个数 = 2, a = 10, b = 20
- 请输入一个字符: ch = A
- 请输入一行字符串: str = hello world
复制代码
说明:scanf(" %c", &ch) 中 %c 前的空格用于跳过输入缓冲区中残留的空白字符(如换行),这是读取字符时的常见处理。
5. 操作符
5.1 算术操作符:+ - * / %
- /:两端都是整数时执行整数除法(取商),至少一个为浮点数时执行浮点除法。
- %:取模(求余)运算,只能用于整数;结果符号由第一个操作数决定。
5.2 赋值操作符与复合赋值
- 创建变量时给值叫初始化,之后给值叫赋值。
- 复合赋值:a += 3 等价于 a = a + 3,-= *= /= %= 同理。
5.3 自增自减
- a++:后置,先使用再自增。
- ++a:前置,先自增再使用。
- a--、--a 同理。
5.4 关系操作符
== >= > < <=:符合返回1,不符合返回0。
5.5 逻辑操作符
- &&:逻辑与。左侧为0时短路,右侧不执行。
- ||:逻辑或。左侧非0时短路,右侧不执行。
- !:逻辑取反。
5.6 条件操作符(三目)
表达式1 ? 表达式2 : 表达式3:表达式1为真执行2,为假执行3。
demo6_operator.c —— 常见操作符 - // demo6_operator.c - 常见操作符:算术、赋值、自增自减、逻辑短路
- #include <stdio.h>
- int main()
- {
- // 算术操作符:整数除法与浮点除法
- float x = 6 / 4; // 两端都是整数 => 整数除法,商为1
- float y = 6.0 / 4; // 至少一个浮点数 => 浮点除法
- printf("6/4 = %.1f\n", x);
- printf("6.0/4 = %.1f\n", y);
- // 取模运算,结果的符号由第一个操作数决定
- printf("11%%-5 = %d\n", 11 % -5);
- printf("-11%%5 = %d\n", -11 % 5);
- // 自增自减:前置先自增再使用,后置先使用再自增
- int a = 5;
- int b = a++; // b=5, a=6
- printf("b=a++ => b=%d, a=%d\n", b, a);
- int c = 5;
- int d = ++c; // d=6, c=6
- printf("d=++c => d=%d, c=%d\n", d, c);
- // 逻辑短路:&& 左侧为0,右侧不执行
- int i = 0;
- int ret = (0 && ++i); // ++i 被短路,不执行
- printf("短路后 i = %d, ret = %d\n", i, ret);
- return 0;
- }
复制代码预期终端输出: - 6/4 = 1.0
- 6.0/4 = 1.5
- 11%-5 = 1
- -11%5 = -1
- b=a++ => b=5, a=6
- d=++c => d=6, c=6
- 短路后 i = 0, ret = 0
复制代码
6. 分支与循环
C语言是结构化程序设计语言,所有逻辑可拆解为顺序、选择、循环三种结构的组合。
6.1 if 语句
if 根据表达式的"真/假"(0为假,非0为真)决定是否执行。控制多条语句必须用大括号 {}。
6.2 switch 语句
switch 用于"多值匹配",表达式必须是整型,case 后必须是整型常量。case 具有穿透性,需用 break 终止;default 处理所有不匹配的情况。
6.3 循环
- while:先判断后执行,适合循环次数不确定的场景。
- for:将"初始化、判断、调整"集中一处,最常用。
- do-while:至少执行一次。
- break:终止循环;continue:跳过本次循环剩余代码,进入下一次。
- goto:快速跳转,能不用尽量不用(多层循环跳出除外)。
demo7_loop.c —— 分支与循环综合 - // demo7_loop.c - 分支与循环综合:3的倍数之和、质数判断、多组输入
- #define _CRT_SECURE_NO_WARNINGS 1
- #include <stdio.h>
- int main()
- {
- // 1. 计算 1~100 中 3 的倍数之和
- int sum = 0;
- for (int i = 3; i <= 100; i += 3)
- {
- sum += i;
- }
- printf("3的倍数之和 = %d\n", sum);
- // 2. 判断一个正整数是否为质数
- int n = 0;
- printf("请输入一个正整数: ");
- scanf("%d", &n);
- int flag = 1; // 假设是质数
- for (int i = 2; i < n; i++)
- {
- if (n % i == 0)
- {
- flag = 0; // 能整除则不是质数
- break;
- }
- }
- if (flag && n > 1)
- printf("%d 是质数\n", n);
- else
- printf("%d 不是质数\n", n);
- // 3. 多组输入:读到文件结尾(EOF)为止
- int m = 0;
- int cnt = 0;
- while (scanf("%d", &m) != EOF)
- {
- cnt++;
- }
- printf("共读取 %d 个整数\n", cnt);
- return 0;
- }
复制代码预期终端输出(键盘输入 7,再输入 5 6 7 8 后按 Ctrl+Z 结束): - 3的倍数之和 = 1683
- 请输入一个正整数: 7 是质数
- 共读取 4 个整数
复制代码
7. 数组
数组是一类相同类型元素的集合。
7.1 一维数组
定义:类型 数组名[常量],例如 int arr[5];
初始化: - int arr1[10] = {1,2,3,4,5,6,7,8,9,10}; // 完全初始化
- int arr2[10] = {0}; // 不完全初始化,其余补0
- int arr3[] = {1,2,3}; // 省略长度,由初始化列表决定
复制代码访问:下标从0开始,arr 访问第 i 个元素。
元素个数:int sz = sizeof(arr) / sizeof(arr[0]);
特点:数组元素在内存中连续存放。
7.2 二维数组
- 把一维数组作为数组的元素,就构成二维数组,即"数组的数组"。
- 初始化:先存满一行再存下一行;分行初始化用 {};可以省略行数,但不能省略列数。
- 二维数组元素在内存中也是连续存放的(按行)。
demo8_array.c —— 一维与二维数组 - // demo8_array.c - 一维与二维数组:定义、访问、内存连续存放、元素个数
- #include <stdio.h>
- int main()
- {
- // 一维数组:完全初始化 / 不完全初始化 / 省略长度
- int arr1[5] = { 1, 2, 3, 4, 5 };
- int arr2[5] = { 1, 2 }; // 其余补0
- int arr3[] = { 1, 2, 3 }; // 由初始化列表决定长度3
- // 计算元素个数
- int sz = sizeof(arr1) / sizeof(arr1[0]);
- printf("arr1 元素个数 = %d\n", sz);
- // 打印一维数组元素及其地址(连续存放)
- printf("arr1 的地址: %p\n", arr1);
- for (int i = 0; i < 5; i++)
- {
- printf("&arr1[%d] = %p\n", i, &arr1[i]);
- }
- // 二维数组:分行初始化
- int matrix[3][5] = { {1, 2}, {3, 4, 5}, {6, 7} };
- printf("二维数组打印:\n");
- for (int i = 0; i < 3; i++)
- {
- for (int j = 0; j < 5; j++)
- {
- printf("%d ", matrix[i][j]);
- }
- printf("\n");
- }
- return 0;
- }
复制代码预期终端输出(地址数值每次运行可能不同,间隔规律相同): - arr1 元素个数 = 5
- arr1 的地址: 00000057FB4FFB30
- &arr1[0] = 00000057FB4FFB30
- &arr1[1] = 00000057FB4FFB34
- &arr1[2] = 00000057FB4FFB38
- &arr1[3] = 00000057FB4FFB3C
- &arr1[4] = 00000057FB4FFB40
- 二维数组打印:
- 1 2 0 0 0
- 3 4 5 0 0
- 6 7 0 0 0
复制代码
变长数组:C99 支持 int arr[n];(n为变量),但 VS2022 内置编译器暂不支持变长数组,需用常量指定数组大小。
8. 本章小结
本章介绍了C语言的基础:
- 数据类型:char / short / int / long / long long / float / double ,sizeof 用于计算大小。
- 变量与常量:全局/局部变量、作用域、生命周期、内存三大区域。
- 输入输出:printf 占位符与返回值、scanf 取地址与返回值。
- 操作符:算术 / 赋值 / 自增自减 / 关系 / 逻辑 / 条件操作符。
- 分支与循环:if / switch、for / while / do-while、break / continue。
- 数组:一维/二维数组定义、初始化、连续存放、元素个数计算。
声明:本文为个人学习笔记,基于笔者学习内容整理,代码均在 VS2022 环境验证通过。 |