这个c语言入门,目标人群是有代码基础的,例如你之前学过javaSE,看此文章可能是更有帮助,会让你快速掌握他们之间的差异,文章内容大部分都是泛谈,详细的部分我会在之后时间发布,我也在慢慢学习,勉励同志们。
随后时间我会发布C语言详细(进阶版),数据结构,C++等的文章,喜欢的可以一起讨论。
目录
一. 第一个c语言程序(输出hello)
(1)VC Code输出
(2)Windows 终端 输出(有乱码不碍事)
二. 数据类型
1. 七个数据类型
2. 数据类型的长度
3. 数据类型的使用(举例)
三. 变量、常量
1.1. 定义变量
1.2. 变量的分类
1.3. 变量的使用
1.4. 变量的作用域与生命周期
2. 定量 / 常量
2.1 常量定义
2.2 常量类型
四. 字符串+转义字符+注释+格式修饰符
1. 字符串
2. 转义字符
3. 注释
4. 格式说明符
五. 选择语句
六. 循环语句
1. 循环类型
2. 代码示例
3. 循环控制语句(break/continue)
4. 运算符(符合运算符)此处不太明白,日后加强联系。
七. 函数
1.函数定义
2.函数代码示例
八. 数组
1. 数组的类型
2. 数组的下标
3. 数组的使用(遍历)
3.1.常规数组的使用与遍历
(1). 常规遍历-正序
(2). 常规遍历-倒叙
3.2. 二维数组的使用与(嵌套遍历)
3.3. 字符串数组与字符串的使用与遍历
3.4. 指针数组的使用与遍历
3.5. 动态数组(堆内存)的使用与遍历
4. 数组的整体代码
九. 操作符
十. 常见关键字
十一. define 定义常量和宏
关键注释说明
宏 vs 常量变量
代码示例:
十二. 指针(泛谈)
1. 什么是指针?
2. 指针操作解析表
3. 代码示例
4. 总结
指针的核心概念总结
5. 最佳实践建议
十三. 结构体(泛谈)
1. 什么是结构体?
2. 结构体操作解析表
3. 代码示例
4. 总结
5. 最佳实践建议
一. 第一个c语言程序(输出hello) - #include <stdio.h>//相当于import java中的导包,包中包含着很多你需要使用的函数等
- //main就是主函数,运行程序的入口,有且必须只有一个。
- int main() {
- printf("hello,world\n"); //输出语句,\n表示换行
- printf("哈啊哈\n");
- return 0;//因为主函数的数据类型为int,所以,返回值为int,其余同理,viod则空
- }
复制代码
输出:
(1)VC Code输出
(2)Windows 终端 输出(有乱码不碍事)
(Linux输出其实也差不多)
二. 数据类型
1. 七个数据类型
char //字符数据类型
short //短整型
int //整形
long //长整型
long long //更长的整形
float //单精度浮点数
double //双精度浮点数
C语言中没有String字符串类型吗????
c语言中string用char的数组形式来表示。下方代码有举例。
2. 数据类型的长度
3. 数据类型的使用(举例) - #include <stdio.h>
- int main() {
- char a = 'a'; // 字符型变量 a
- char a1[] = {'h', 'e', 'l', 'l', 'o', '\0'}; // 字符型数组 a1,注意加上结束符 '\0'
- char str1[] = "Hello"; // 字符数组表示字符串,即java中的string
- char *str2 = "World"; // 字符指针表示字符串
- short b = 10; // 短整型变量 b
- int c = 10; // 整型变量 c
- long d = 10; // 长整型变量 d
- float f = 10.0f; // 单精度浮点数 f
- double g = 10.00000; // 双精度浮点数 g
- // 输出变量的值
- printf("字符 a: %c\n", a);
- printf("字符数组 a1: %s\n", a1);
- printf("%s %s\n", str1, str2);
- printf("Length of str1: %zu\n", strlen(str1));//输出str1的长度,即为5
- printf("短整型 b: %d\n", b);
- printf("整型 c: %d\n", c);
- printf("长整型 d: %ld\n", d);
- printf("单精度浮点数 f: %.2f\n", f);
- printf("双精度浮点数 g: %.5f\n", g);
- return 0;
- }
复制代码
输出:
三. 变量、常量
1. 变量
1.1. 定义变量
简而言之变量即使可以更改的量,其数值可以被更改。
1.2. 变量的分类
1.全局变量
(1)即作用于代码运行全过程的变量
2.局部变量
(2)即作用于部分代码块的变量
- #include <stdio.h>
- // 全局变量
- int globalVar = 10; // 全局变量,作用域为整个文件
- void function() {
- // 局部变量
- int localVar = 5; // 局部变量,作用域仅限于此函数内
- printf(" 函数内 局部变量 localVar 的值: %d\n", localVar);
- printf(" 函数内 全局变量 globalVar 的值: %d\n", globalVar);
- }
- int main() {
- printf("全局变量 globalVar 的值: %d\n", globalVar); // 可以访问全局变量
- function();//调用上方的函数
-
- // printf("局部变量 localVar 的值: %d\n", localVar); // 这行代码会报错,因为 localVar 作用域仅限于 function 函数
- return 0;
- }
复制代码
1.3. 变量的使用
使变量等于输入两个值,使这两个变量进行加减乘除,结果取小数点后两位。
- #include <stdio.h>
- int main() {
- // 定义变量
- float num1, num2; // 用于存储输入的两个数
- float sum, difference, product, quotient; // 用于存储运算结果
- // 提示用户输入两个数,scanf的标准用法,于printf很相似
- printf("请输入第一个数: ");
- scanf("%f", &num1); // 读取第一个数
- printf("请输入第二个数: ");
- scanf("%f", &num2); // 读取第二个数
- // 进行运算
- sum = num1 + num2; // 加法
- difference = num1 - num2; // 减法
- product = num1 * num2; // 乘法
- quotient = num1 / num2; // 除法(注意:需要确保 num2 不为 0)
-
- // 输出结果,%.2f即为取余,小数点后两位,%.3f即为小数点后3位,以此类推。
- printf("加法结果: %.3f\n", sum);
- printf("减法结果: %.3f\n", difference);
- printf("乘法结果: %.3f\n", product);
-
- // 除法结果的输出需要检查除数是否为0
- if (num2 != 0) {
- printf("除法结果: %.2f\n", quotient);
- } else {
- printf("除法结果: 无法除以零\n");
- }
- return 0;
- }
复制代码
输出:
1.4. 变量的作用域与生命周期
作用域:
作用域(
scope
)是程序设计概念,通常来说,一段程序代码中所用到的名字并不总是
可用的
而限定这个名字的可用性的代码范围就是这个名字的作用域。
1.
局部变量的作用域是变量所在的局部范围。
2.
全局变量的作用域是整个代码运行过程。
生命周期:
变量的生命周期指的是变量的创建到变量的销毁之间的一个时间段
1.
局部变量的生命周期是:进入作用域生命周期开始,出作用域生命周期结束。
2.
全局变量的生命周期是:整个程序的生命周期,同理。
2. 定量 / 常量
2.1 常量定义
简而言之,定量即使不变的量。
2.2 常量类型
1. 字面常量
2. const
修饰的常变量(java中常用)
3. #define
定义的标识符常量
4. 枚举常量
- #include <stdio.h>
- // 使用 #define 定义标识符常量
- #define PI 3.14159 // 圆周率常量
- // 定义枚举常量
- enum Color {
- RED, // 默认第一个为0,以此类推。
- GREEN, // GREEN = 1
- BLUE // BLUE = 2
- };
- int main() {
- // 字面常量
- int a = 10; // 整数字面常量
- float b = 5.5; // 浮点数字面常量
- // const 修饰的常量
- const int MAX_VALUE = 100; // 最大值常量,不能被修改
- // 输出各个常量的值
- printf("字面常量 a: %d\n", a);
- printf("字面常量 b: %.2f\n", b);
- printf("标识符常量 PI: %.5f\n", PI);
- printf("const 常量 MAX_VALUE: %d\n", MAX_VALUE);
- // 使用枚举常量
- enum Color myColor = GREEN; // 赋值为枚举常量
- printf("枚举常量 myColor: %d\n", myColor); // 输出枚举常量的整数值
- return 0;
- }
复制代码
输出
四. 字符串+转义字符+注释+格式修饰符
1. 字符串
1. 即Java中的String,用来表示多个字符在一起的样子。但是C中没有String的概念,所以字符串用char【】(字符型数组)来表示。
2. 下方的代码,用了四种方式来表示String,具体看代码注释,写的很清楚。
- //可以简单认为include与java中的import是同一个意思。
- #include <stdio.h> // 引入标准输入输出库,提供输入输出函数,如printf和scanf
- #include <stdlib.h> // 引入标准库,包含内存分配(如malloc、free)、进程控制和转换等函数
- #include <string.h> // 引入字符串处理库,提供字符串操作函数,如strcpy、strlen等
- int main() {
- // 1. 使用字符数组定义字符串
- char str1[] = "hello world"; // 字符数组,自动添加'\0'
- char str11[] ={'0','1','2','3','4','\0'}; // 字符数组,自动添加'\0'<规范>
- // 2. 使用字符指针定义字符串
- const char *str2 = "hello world"; // 字符指针指向字符串常量
- // 3. 动态内存分配,malloc 返回的是 void* 类型的指针,表示它可以指向任何类型的内存。
- //指针相当于一种特殊类型的变量
- char *str3 = (char *)malloc(12 * sizeof(char)); // 开辟一个char类型,12长度的内存空间变量为str3这个指针变量
- if (str3 == NULL) { // 检查内存是否分配存在
- printf("内存分配失败\n");
- return 1; // 返回1表示异常,并输出内存分配失败
- }
- strcpy(str3, "hello world"); //
- // 输出所有字符串
- printf("%s\n", str1); // 输出字符数组
- printf("%s\n", str11); // 输出字符数组
- printf("%s\n", str2); // 输出字符指针
- printf("%s\n", str3); // 输出动态分配的字符串
- // 释放动态分配的内存
- free(str3);
- return 0; // 返回0表示程序正常结束
- }
复制代码
输出:
2. 转义字符
转义字符 释义(转义字符顾名思义就是转变意思。 )
\? 在书写连续多个问号时使用,防止他们被解析成三字母词
\' 用于表示字符常量'
\" 用于表示一个字符串内部的双引号
\\ 用于表示一个反斜杠,防止它被解释为一个转义序列符。
\a 警告字符,蜂鸣
\b 退格符
\f 进纸符
\n 换行
\r 回车
\t 水平制表符
\v 垂直制表符
\ddd ddd表示
1~3
个八进制的数字。 如:
\130 X
\xdd dd表示
2
个十六进制数字。 如:
\x30 0
举例:
仔细看注释。 - #include<stdio.h>
- #include<string.h>
- // strlen - string length - 求字符串长度
- int main()
- {
- printf("%d\n", strlen("abcdef"));
- printf("%d\n", strlen("abcdefg")); // 输出字符串 "abcdef" 的长度
- // \62被解析成一个转义字符
- printf("%d\n", strlen("c:\test\628\test.c"));//会输出错误,因为系统编译会把\t认识为转义符
- printf("%d\n", strlen("c:\\test\\628\\test.c")); // 使用双反斜杠表示反斜杠,就没有问题了。
- return 0;
- }
复制代码
详细使用方法: - #include <stdio.h>
- int main() {
- // 1. \? - 表示问号字符 原因: \? 不会被解析为转义字符,直接输出问号。
- printf("这是一个问号: \?\n"); // 输出: 这是一个问号: ?
- // 2. \' - 表示单引号字符 原因: \' 被解析为单引号字符,用于在字符串中包含单引号。
- printf("这是一个单引号: \'\n"); // 输出: 这是一个单引号: '
- // 3. " - 表示双引号字符 原因: " 被解析为双引号字符,用于在字符串中包含双引号。
- printf("这是一个双引号: "\n"); // 输出: 这是一个双引号: "
- // 4. \\ - 表示反斜杠字符 原因: \\ 被解析为一个反斜杠字符,用于在字符串中包含反斜杠。
- printf("这是一个反斜杠: \\\n"); // 输出: 这是一个反斜杠: \
- // 5. \a - 表示警告音(响铃)原因: \a 表示响铃字符,某些终端或系统可能会发出声音。
- printf("这将响铃: \a\n"); // 可能会发出警告音(依赖于终端设置)
- // 6. \b - 表示退格符 原因: \b 将光标移动到前一个字符位置,覆盖该字符。
- printf("这是一个退格符: Hello\bWorld\n"); // 输出: HelloWorld(“Hello”中的最后一个字母被删除)
- //
- // 7. \f - 表示换页符 原因: \f 在某些终端中可能会导致换页,但在其他终端中可能没有明显效果。
- printf("这是一个换页符: Hello\fWorld\n"); // 输出: Hello(在某些终端中可能表现为换页)
- // 8. \n - 表示换行符 原因: \n 将光标移动到下一行,输出内容在新行显示。
- printf("这是一个换行符: Hello\nWorld\n"); // 输出: Hello
- // // World
- // 9. \r - 表示回车符 原因: \r 将光标移动到行首,后续字符会覆盖当前行的内容。
- printf("这是一个回车符: Hello\rWorld\n"); // 输出: World(“Hello”被覆盖)
- // 10. \t - 表示水平制表符 原因: \t 插入一个水平制表符的空格,增加字符之间的间距。
- printf("这是一个制表符: Hello\tWorld\n"); // 输出: Hello World(“Hello”和“World”之间有一个制表符的空格)
- // 11. \v - 表示垂直制表符 原因: \v 在某些终端中可能表现为换行,但在其他终端中可能没有明显效果
- printf("这是一个垂直制表符: Hello\vWorld\n"); // 输出: Hello(可能会在某些终端中表现为换行)
- // 12. \ddd - 表示八进制数对应的字符 原因: 八进制数表示的字符在ASCII表中对应的字符。
- printf("这是一个八进制字符: \101\n"); // 输出: A(八进制101对应的字符是'A')
- // 13. \xdd - 表示十六进制数对应的字符 原因: 十六进制数表示的字符在ASCII表中对应的字符。
- printf("这是一个十六进制字符: \x41\n"); // 输出: A(十六进制41对应的字符是'A')
- return 0;
- }
- }
- }
复制代码
3. 注释
1.
代码中有不需要的代码可以直接删除,也可以注释掉
2.
代码中有些代码比较难懂,可以加一下注释文字
vs Code 分为单行注释与多行注释,自己搜索,默认的我也忘了。嘿嘿嘿。
4. 格式说明符
格式说明符:
%d 或 %i 解释:输出或输入有符号十进制整数。
%u 解释:输出或输入无符号十进制整数。
%f 解释:输出或输入十进制浮点数(float)。
%lf 解释:输出或输入双精度浮点数(double)。
%c 解释:输出或输入单个字符。
%s 解释:输出或输入字符串(字符数组)。
%p 解释:输出指针的地址(内存地址)。
%o 解释:输出无符号八进制整数。
%x 或 %X 解释:输出无符号十六进制整数。%x 使用小写字母,%X 使用大写字母。
%e 或 %E 解释:以科学计数法输出浮点数。%e 使用小写字母 e,%E 使用大写字母 E。
%g 或 %G 解释:根据数值的大小自动选择使用 %f 或 %e 的格式输出浮点数。%g 使用小写字母,
%G 解释:使用大写字母。
格式说明符代码解释 : - #include <stdio.h>
- int main() {
- // 1. %d 或 %i - 输出或输入有符号十进制整数 // 原因: %d 用于打印有符号整数,负数会正常显示。
- int signedInt = -42;
- printf("有符号十进制整数: %d\n", signedInt); // 输出: 有符号十进制整数: -42
- // 2. %u - 输出或输入无符号十进制整数 // 原因: %u 用于打印无符号整数,确保不会显示负号。
- unsigned int unsignedInt = 42;
- printf("无符号十进制整数: %u\n", unsignedInt); // 输出: 无符号十进制整数: 42
- // 3. %f - 输出或输入十进制浮点数(float) // 原因: %f 默认输出六位小数,适用于浮点数。
- float floatNum = 3.14f;
- printf("十进制浮点数: %f\n", floatNum); // 输出: 十进制浮点数: 3.140000
- // 4. %lf - 输出或输入双精度浮点数(double) // 原因: %lf 用于打印双精度浮点数,默认输出六位小数。
- double doubleNum = 3.141592653589793;
- printf("双精度浮点数: %lf\n", doubleNum); // 输出: 双精度浮点数: 3.141593
- // 5. %c - 输出或输入单个字符 // 原因: %c 用于打印单个字符。
- char character = 'A';
- printf("单个字符: %c\n", character); // 输出: 单个字符: A
- // 6. %s - 输出或输入字符串(字符数组) // 原因: %s 用于打印字符串,直到遇到空字符 '\0'。
- char str[] = "Hello, World!";
- printf("字符串: %s\n", str); // 输出: 字符串: Hello, World!
- // 7. %p - 输出指针的地址(内存地址) // 原因: %p 用于打印指针的地址,通常以十六进制格式输出。
- int *ptr = &signedInt;
- printf("指针地址: %p\n", (void *)ptr); // 输出: 指针地址: 0x7ffeedc3a5b4(地址会因运行而异)
- // 8. %o - 输出无符号八进制整数 // 原因: %o 用于打印无符号整数的八进制表示。
- printf("无符号八进制整数: %o\n", unsignedInt); // 输出: 无符号八进制整数: 50
- // 9. %x 或 %X - 输出无符号十六进制整数 // 原因: %x 用于打印小写十六进制,%X 用于打印大写十六进制。
- printf("无符号十六进制整数 (小写): %x\n", unsignedInt); // 输出: 无符号十六进制整数 (小写): 2a
- printf("无符号十六进制整数 (大写): %X\n", unsignedInt); // 输出: 无符号十六进制整数 (大写): 2A
- // 10. %e 或 %E - 以科学计数法输出浮点数 // 原因: %e 和 %E 用于以科学计数法格式打印浮点数。
- printf("科学计数法 (小写): %e\n", floatNum); // 输出: 科学计数法 (小写): 3.140000e+00
- printf("科学计数法 (大写): %E\n", floatNum); // 输出: 科学计数法 (大写): 3.140000E+00
- // 11. %g 或 %G - 根据数值的大小自动选择格式输出浮点数 // 原因: %g 和 %G 根据数值的大小选择使用 %f 或 %e 格式输出。
- printf("自动选择格式 (小写): %g\n", doubleNum); // 输出: 自动选择格式 (小写): 3.14159
- printf("自动选择格式 (大写): %G\n", doubleNum); // 输出: 自动选择格式 (大写): 3.14159
- return 0;
- }
复制代码
五. 选择语句
| 语句类型 | 适用场景 | 特点 |
|---|
| if | 单条件判断 | 无默认分支 | | if-else | 二选一逻辑 | 必须选择其一 | | if-else if-else | 多条件阶梯判断 | 按顺序检查条件 | | switch-case | 离散值匹配(如枚举、字符) | 效率高,需break防止穿透 |
输出
由于我坚信大家能看得懂,所以就不在此过多赘述!! - #include <stdio.h>
- int main() {
- //设置一些基础的变量。
- int score = 85;
- char grade = 'B';
- int num = 10;
- /* 1. 基础if语句:单条件判断 */
- if (score >= 60) {
- printf("1. 及格\n"); // 条件成立时执行
- }
- /* 2. if-else语句:二选一分支 */
- if (num % 2 == 0) {
- printf("2. 偶数\n");
- } else {
- printf("2. 奇数\n"); // 条件不成立时执行
- }
- /* 3. if-else if-else:多条件阶梯判断 */
- if (score >= 90) {
- printf("3. 优秀\n");
- } else if (score >= 80) {
- printf("3. 良好\n"); // 满足 score>=80 但 score<90
- } else if (score >= 60) {
- printf("3. 及格\n");
- } else {
- printf("3. 不及格\n"); // 默认分支
- }
- /* 4. switch-case:离散值匹配 */
- switch (grade) {
- case 'A':
- printf("4. 90-100分\n");
- break; // 必须用break阻止穿透
- case 'B':
- printf("4. 80-89分\n");
- break;
- case 'C':
- printf("4. 60-79分\n");
- break;
- default:
- printf("4. 不及格\n"); // 默认分支
- }
- return 0;
- }
复制代码
六. 循环语句
1. 循环类型
| 循环类型 | 执行顺序 | 适用场景 | 注意事项 |
|---|
| for | 先检查后执行 | 明确循环次数(如数组遍历) | 循环变量作用域需注意 | | while | 先检查后执行 | 条件未知但需前置检查(如读文件) | 避免忘记更新条件变量导致死循环 | | do-while | 先执行后检查 | 至少执行一次(如用户输入验证) | 结尾分号不可遗漏 |
2. 代码示例 - #include <stdio.h>
- int main() {
- /* 示例1: for循环(明确循环次数) */
- for (int i = 1; i <= 3; i++) {
- printf(" 第%d次循环\n", i); // 循环体执行3次
- }
- /* 示例2: while循环(条件驱动循环) */
- int count = 3;
- while (count > 0) {
- printf(" 剩余次数: %d\n", count);
- count--; // 修改条件变量避免死循环
- }
- /* 示例3: do-while循环(至少执行一次) */
- int input;
- do {
- printf(" 输入1继续,其他退出: ");
- scanf("%d", &input);
- } while (input >=1); // 先执行后判断
- /* 示例4: 循环控制语句(break/continue) */
- for (int j = 1; j <= 6; j++) {
- if (j == 3) {
- continue; // 跳过本次循环剩余代码,当j==3的时候,跳出本次循环,执行j==4,即输出语句不含有3。
- }
- if (j == 6) {
- break; // 提前终止整个循环,当j==6的时候,终止循环,即输出语句不含有6,及其以下语句。
- }
- printf(" 当前值: %d\n", j);
- }
- return 0;
- }
复制代码
输出:
3. 循环控制语句(break/continue)
| 语句 | 作用 | 示例场景 |
|---|
| break | 立即终止当前循环 | 搜索到目标后提前退出循环 | | continue | 跳过本次循环剩余代码,进入下一轮循环 | 过滤无效数据(如跳过负数处理) |
4. 运算符(符合运算符)此处不太明白,日后加强联系。
| 运算符 | 行为描述 | 典型应用场景 | 示例 | 结果 |
|---|
| i++ | 先用后加(后缀自增) | 循环变量更新 | | | | ++i | 先加后用(前缀自增) | 需要立即使用新值 | | | | i-- | 先用后减(后缀自减) | 递减计数器 | | | | --i | 先减后用(前缀自减) | 需要立即使用新值 | int i = 5; int j = i--; | |
| 运算符 | 含义 | 示例 | 等价表达式 |
|---|
| += | 加法赋值 | a += 5 | a = a + 5 | | -= | 减法赋值 | a -= 3 | a = a - 3 | | *= | 乘法赋值 | a *= 2 | a = a * 2 | | /= | 除法赋值 | a /= 4 | a = a / 4 | | %= | 取模赋值 | a %= 3 | a = a % 3 | | &= | 按位与赋值 | a &= 0x0F | a = a & 0x0F | | ^= | 按位异或赋值 | a ^= b | a = a ^ b | | <<= | 左移赋值 | a <<= 2 | a = a << 2 | | >>= | 右移赋值 | a >>= 1 | a = a >> 1 |
输出: - #include <stdio.h>
- int main() {
- int a = 10, b = 3, c = 5;
- /* 1. 基本运算符 */
- // 算术运算符
- printf("a + b = %d\n", a + b); // 加法:13
- printf("a %% b = %d\n", a % b); // 取模:1(余数)
- // 关系运算符
- printf("a > b? %d\n", a > b); // 1(true)
- // 逻辑运算符
- printf("!(a < 5) = %d\n", !(a < 5)); // 1(非操作)
- // 位运算符
- printf("a & b = %d\n", a & b); // 按位与:2(1010 & 0011 = 0010)
- printf("a << 2 = %d\n", a << 2); // 左移2位:40
- // 赋值运算符
- c = a; // 基本赋值,c变为10
- // 条件运算符(三目运算符)
- int max = (a > b) ? a : b; // max=10
- // 逗号运算符
- int d = (a++, b++, a + b); // d=15(a=11, b=4)
- /* 2. 复合运算符(组合赋值) */
- a += 5; // 等价于 a = a + 5 → 15
- b *= 2; // 等价于 b = b * 2 → 8
- c &= 0x0F; // 等价于 c = c & 0x0F → 10的二进制高4位清零 → 10
- // 验证结果
- printf("a=%d, b=%d, c=%d, d=%d, max=%d\n", a, b, c, d, max);
- return 0;
- }
复制代码
七. 函数
1.函数定义
即为Java中的方法,目的是为了提高代码复用率,可用性。
2.函数代码示例
代码中包含了六个函数,各有差异。细节都在注释之中了,仔细看,不是很难,比较难理解的是指针类型定义,这个我会在c语言的进阶版详细来说。
- #include <stdio.h>
- //1. 无返回值、无参数函数:用于执行特定任务,不返回数据,引用参数输出hello world 换行
- void greet(void) {
- printf("Hello World!\n");
- }
- // 2. 带参数和返回值的函数:接收两个整数,返回它们的和
- int add(int a, int b) { // 参数是值传递(拷贝)
- return a + b;
- }
- //3. 指针参数函数:通过地址修改外部变量 ,
- void swap(int *x, int *y) { // 使用指针实现"引用传递"效果,a=10 , b=20,输出结果应改为 *x等于20,*y=10;
- int temp = *x;//*x的值传给temp =10;
- *x = *y ;//*y把值传给*x,此时*x的值为20
- *y = temp;//temp(10)的值传给*y,temp的值为10。
- }
- // 4. 递归函数:计算阶乘,需设置终止条件
- int factorial(int n) {
- if (n < 0) return -1; // 错误检查,简单选择语句,即 使参数n不得小于0
- return (n <= 1) ? 1 : n * factorial(n - 1);//三运运算符,若n=1则返回1,n>1,则 n * factorial(n - 1),即为递归函数
- }
- /* 5. 函数指针类型定义:用于实现回调机制 */
- typedef int (*MathOperation)(int, int); // 定义函数指针类型,
- /* 6. 函数原型声明:必须在使用前声明(定义可在后面) */
- double divide(double a, double b); // 函数原型
- /* 函数实现:除法运算 */
- double divide(double a, double b) {
- if (b == 0) { // 参数有效性检查
- printf("错误:除数不能为0\n");
- return 0;
- }
- return a / b;
- }
- int main() {
- // 1.调用无参函数
- greet();
- // 2.调用函数a+b的结果,传参什么的,不过大多叙述。
- int result = add(3, 5); // 接收返回值
- printf("3 + 5 = %d\n", result);
- // 3.
- int a = 10, b = 20;
- printf("交换前:a=%d, b=%d\n", a, b);
- swap(&a, &b); // 传递地址修改实参
- printf("交换后:a=%d, b=%d\n", a, b);
- // 4. 输出结果即为 5*5-1
- printf("5的阶乘 = %d\n", factorial(5));//即给参数即可,算的是5的递归。
- //函数指针使用
- MathOperation operation = add; // 指向add函数,我能理解,即为定义指针函数,把add的作用调到operation中使用,可以简单理解。
- printf("函数指针运算:%d\n", operation(7, 8));
- //
- printf("10 / 3 = %.2f\n", divide(10.0, 3.0));
- return 0;
- }
复制代码
输出:
八. 数组
1. 数组的类型
1. 常规数组
2. 二维数组
3. 字符串数组与字符串
4. 指针数组
5. 动态数组(堆内存)
2. 数组的下标
此处的重点就是,记住下标第一个是0.
3. 数组的使用(遍历)
3.1.常规数组的使用与遍历
(1). 常规遍历-正序 - // 2. 常规遍历(正序)
- printf("arr1元素:");
- for (int i = 0; i < 5; i++) {
- printf("%d ", arr1[i]); // 通过下标访问,输出:1 2 3 0 0
- }
复制代码
(2). 常规遍历-倒叙 - // 3. 逆序遍历
- printf("\narr2逆序:");
- for (int i = sizeof(arr2)/sizeof(arr2[0]) - 1; i >= 0; i--) {
- printf("%d ", arr2[i]); // 输出:30 20 10
- }
复制代码
3.2. 二维数组的使用与(嵌套遍历) - int matrix[2][3] = {
- {1, 2, 3},
- {4, 5, 6}
- };
- printf("\n二维数组遍历:\n");
- for (int row = 0; row < 2; row++) {
- for (int col = 0; col < 3; col++) {
- printf("matrix[%d][%d]=%d ", row, col, matrix[row][col]);
- }
- printf("\n");
- }
复制代码
3.3. 字符串数组与字符串的使用与遍历 - char str1[] = {'H', 'e', 'l', 'l', 'o', '\0'}; // 手动添加结束符
- char str2[] = "World"; // 自动添加结束符'\0',长度=5+1=6
- printf("字符串遍历:");
- int i = 0;
- while (str1[i] != '\0') { // 遍历到结束符停止
- printf("%c", str1[i++]); // 输出:Hello
- }
- printf(" %s\n", str2); // 直接输出:World------%s 解释:输出或输入字符串(字符数组)。
复制代码
3.4. 指针数组的使用与遍历 - const char *words[] = {"Apple", "Banana", "Cherry"}; // 字符串指针数组
- printf("指针数组元素:\n");
- for (int i = 0; i < 3; i++) {
- printf("第%d个单词: %s\n", i, words[i]); // 通过指针访问字符串
- }
复制代码
3.5. 动态数组(堆内存)的使用与遍历 - int *dynamic_arr = malloc(5 * sizeof(int)); // 动态分配5个int空间
- if (dynamic_arr == NULL) {
- printf("内存分配失败!");
- return 1;
- }
- // 初始化动态数组
- for (int i = 0; i < 5; i++) {
- dynamic_arr[i] = i * 10; // 下标访问与普通数组一致
- }
- // 指针遍历动态数组
- printf("动态数组:");
- int *ptr = dynamic_arr;
- for (int i = 0; i < 5; i++) {
- printf("%d ", *ptr++); // 输出:0 10 20 30 40
- }
- free(dynamic_arr); // 必须释放内存!
复制代码
4. 数组的整体代码 - #include <stdio.h>
- #include <stdlib.h> // 用于动态数组
- int main() {
- /*=====================
- 一维数组基础-----这个好理解。
- ======================*/
- // 1. 定义与初始化
- int arr1[5] = {1, 2, 3}; // 长度为5,部分初始化,未赋值元素默认为0,即为剩下两个位置为0,0
- int arr2[] = {10, 20, 30}; // 自动推断长度(3个元素)
- // 2. 常规遍历(正序)
- printf("arr1元素:");
- for (int i = 0; i < 5; i++) {
- printf("%d ", arr1[i]); // 通过下标访问,输出:1 2 3 0 0
- }
- // 3. 逆序遍历
- printf("\narr2逆序:");
- for (int i = sizeof(arr2)/sizeof(arr2[0]) - 1; i >= 0; i--) {
- printf("%d ", arr2[i]); // 输出:30 20 10
- }
- /*=====================
- 二维数组与嵌套遍历
- ======================*/
- int matrix[2][3] = {
- {1, 2, 3},
- {4, 5, 6}
- };
- printf("\n二维数组遍历:\n");
- for (int row = 0; row < 2; row++) {
- for (int col = 0; col < 3; col++) {
- printf("matrix[%d][%d]=%d ", row, col, matrix[row][col]);
- }
- printf("\n");
- }
- /*=====================
- 字符数组与字符串
- ======================*/
- char str1[] = {'H', 'e', 'l', 'l', 'o', '\0'}; // 手动添加结束符
- char str2[] = "World"; // 自动添加结束符'\0',长度=5+1=6
- printf("字符串遍历:");
- int i = 0;
- while (str1[i] != '\0') { // 遍历到结束符停止
- printf("%c", str1[i++]); // 输出:Hello
- }
- printf(" %s\n", str2); // 直接输出:World------%s 解释:输出或输入字符串(字符数组)。
- /*=====================
- 指针数组与遍历
- ======================*/
- const char *words[] = {"Apple", "Banana", "Cherry"}; // 字符串指针数组
- printf("指针数组元素:\n");
- for (int i = 0; i < 3; i++) {
- printf("第%d个单词: %s\n", i, words[i]); // 通过指针访问字符串
- }
- /*=====================
- 动态数组(堆内存)
- ======================*/
- int *dynamic_arr = malloc(5 * sizeof(int)); // 动态分配5个int空间,开辟长度为5,类型为int的内存空间,
- if (dynamic_arr == NULL) {
- printf("内存分配失败!");
- return 1;
- }
- // 初始化动态数组
- for (int i = 0; i < 5; i++) {
- dynamic_arr[i] = i * 10; // 下标访问与普通数组一致
- }
- // 指针遍历动态数组
- printf("动态数组:");
- int *ptr = dynamic_arr;
- for (int i = 0; i < 5; i++) {
- printf("%d ", *ptr++); // 输出:0 10 20 30 40
- }
- free(dynamic_arr); // 必须释放内存!
- /*=====================
- 数组下标越界示例(危险!)
- ======================*/
- int arr[3] = {100, 200, 300};
- // printf("\n越界访问:%d", arr[5]); // 未定义行为,可能导致崩溃或输出垃圾值
- return 0;
- }
复制代码
输出:
九. 操作符
| 操作符 | 名称/作用 | 示例 |
|---|
| + | 加法 | a + b | | - | 减法/负号 | a - b, -x | | * | 乘法/指针解引用 | a * b, *ptr | | / | 除法 | a / b | | % | 取模(余数) | a % b | | ++ | 自增 | i++, ++i | | -- | 自减 | i--, --i | | > | 大于 | a > b | | < | 小于 | a < b | | == | 等于 | a == b | | != | 不等于 | a != b | | && | 逻辑与 | x && y | | ! | 逻辑非 | !x | | & | 按位与/取地址 | m & n, &var | | ^ | 按位异或 | m ^ n | | ~ | 按位非 | ~m | | << | 左移 | m << 2 | | >> | 右移 | n >> 1 | | = | 赋值 | c = 10 | | += | 复合赋值 | c += 5 | | ?: | 条件运算符 | (a>b)?a:b | | , | 逗号运算符 | d=(a++,b++) | | . | 结构体成员访问 | p.x | | -> | 结构体指针成员访问 | ptr->x | | sizeof | 获取类型/对象大小 | sizeof(int) |
- #include <stdio.h>
- int main() {
- /*================ 算术操作符 ================*/
- int a = 10, b = 3;
- printf("a + b = %d\n", a + b); // 加法:13
- printf("a - b = %d\n", a - b); // 减法:7
- printf("a * b = %d\n", a * b); // 乘法:30
- printf("a / b = %d\n", a / b); // 整除:3(截断小数)
- printf("a %% b = %d\n", a % b); // 取模:1(余数)
- /*================ 关系操作符 ================*/
- printf("a > b? %d\n", a > b); // 大于:1(true)
- printf("a == b? %d\n", a == b); // 等于:0(false)
- /*================ 逻辑操作符 ================*/
- int x = 1, y = 0;
- printf("x && y = %d\n", x && y); // 逻辑与:0
- printf("x || y = %d\n", x || y); // 逻辑或:1
- printf("!x = %d\n", !x); // 逻辑非:0
- /*================ 位操作符 ================*/
- unsigned char m = 0b1010, n = 0b1100;
- printf("m & n = %d\n", m & n); // 按位与:0b1000 (8)
- printf("m | n = %d\n", m | n); // 按位或:0b1110 (14)
- printf("m ^ n = %d\n", m ^ n); // 按位异或:0b0110 (6)
- printf("~m = %d\n", (unsigned char)~m); // 按位非:0b0101 (5)
- printf("m << 2 = %d\n", m << 2); // 左移:40 (0b101000)
- printf("n >> 1 = %d\n", n >> 1); // 右移:6 (0b0110)
- /*================ 赋值操作符 ================*/
- int c = 10;
- c += 5; // 等价 c = c + 5 → 15
- c %= 4; // 等价 c = c % 4 → 3
- printf("赋值操作后 c = %d\n", c);
- /*================ 其他操作符 ================*/
- // 条件运算符(三元运算符)
- int max = (a > b) ? a : b; // max = 10
- printf("较大值:%d\n", max);
- // 逗号运算符(返回最后一个表达式值)
- int d = (a++, b++, a + b); // a=11, b=4 → d=15
- printf("逗号运算结果:%d\n", d);
- // 地址操作符
- int *ptr = &a; // &取地址
- printf("a的地址:%p\n", ptr);
- printf("指针解引用:%d\n", *ptr); // *解引用
- // sizeof运算符
- printf("int字节数:%zu\n", sizeof(int)); // 通常为4
- // 结构体成员访问
- struct Point { int x; int y; } p = {5, 8};
- printf("结构体成员:%d\n", p.x); // .运算符访问成员
- return 0;
- }
复制代码
十. 常见关键字
| 关键字 | 类别 | 核心作用 |
|---|
| int/char | 基础类型 | 定义整数/字符类型 | | float/double | 浮点类型 | 定义单/双精度浮点数 | | _Bool | 布尔类型 | 定义true/false逻辑值 | | unsigned | 类型修饰符 | 声明无符号类型 | | const | 类型修饰符 | 定义不可修改的常量 | | struct | 复合类型 | 定义结构体数据类型 | | union | 复合类型 | 定义共享内存的联合体 | | enum | 复合类型 | 定义枚举常量集合 | | auto | 存储类别 | 自动存储期(默认) | | static | 存储类别 | 静态存储期/限制作用域 | | register | 存储类别 | 建议寄存器存储变量 | | extern | 存储类别 | 声明外部变量/函数 | | if/else | 流程控制 | 条件分支控制 | | for/while | 流程控制 | 循环结构控制 | | do | 流程控制 | 后置条件循环 | | switch | 流程控制 | 多条件分支结构 | | break | 流程控制 | 跳出循环/switch | | continue | 流程控制 | 跳过本次循环剩余代码 | | goto | 流程控制 | 无条件跳转(慎用) | | sizeof | 运算符 | 获取类型/对象内存大小 | | volatile | 类型修饰符 | 防止编译器优化访问 | | typedef | 类型定义 | 创建类型别名 | | void | 特殊类型 | 无类型/函数无返回值 | | return | 函数控制 | 从函数返回值 |
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdbool.h> // 包含_Bool的bool宏
- /*---------------- 数据类型关键字 ----------------*/
- int main() {
- // 基本类型
- int num = 10; // int: 32位整型
- char ch = 'A'; // char: 8位字符/小整型
- float pi = 3.14f; // float: 单精度浮点
- double salary = 9999.99;// double: 双精度浮点
- _Bool flag = true; // _Bool: 布尔类型(需要包含stdbool.h)
-
- // 类型修饰符
- unsigned int age = 30; // unsigned: 无符号整型
- long big_num = 100000L; // long: 扩展范围整型
- short small_num = 100; // short: 缩小范围整型
- const int MAX = 100; // const: 定义不可修改常量
- // 复合类型
- struct Person { // struct: 定义结构体类型
- char name[20];
- int age;
- };
- union Data { // union: 联合体(共享内存)
- int i;
- float f;
- };
- enum Color {RED, GREEN, BLUE}; // enum: 定义枚举常量
- /*---------------- 存储类别关键字 ----------------*/
- auto int local = 5; // auto: 自动存储期(默认可省略)
- static int counter = 0; // static: 静态存储期
- register int fast = 0; // register: 建议寄存器存储
- extern int external; // extern: 声明外部变量
- /*---------------- 流程控制关键字 ----------------*/
- // 条件分支
- if (num > 5) { // if: 条件判断
- printf("num>5\n");
- } else { // else: 否则分支
- printf("num<=5\n");
- }
- // 循环结构
- for (int i=0; i<3; i++) {// for: 确定次数循环
- printf("for:%d\n",i);
- continue; // continue: 跳过本次循环
- }
-
- while (num > 0) { // while: 条件循环
- if (num == 5) break;// break: 跳出循环
- num--;
- }
- do { // do: 后置条件循环
- printf("do-while\n");
- } while(0);
- // 多路分支
- switch(ch) { // switch: 多条件分支
- case 'A': // case: 分支匹配
- printf("A级\n");
- break;
- default: // default: 默认分支
- printf("未知等级\n");
- }
- /*---------------- 运算符相关关键字 ----------------*/
- int size = sizeof(int); // sizeof: 获取类型/对象字节数
- volatile int sensor; // volatile: 防止编译器优化
- typedef int Score; // typedef: 定义类型别名
- /*---------------- 特殊用途关键字 ----------------*/
- goto cleanup; // goto: 跳转到标签(慎用)
- cleanup:
- void *ptr = NULL; // void: 无类型指针/函数无返回值
- return 0; // return: 函数返回
- // restrict int *p; // restrict(C99): 指针独占访问优化
- // _Atomic int atom; // _Atomic(C11): 原子类型
- }
- /*---------------- 补充说明 ----------------*/
- // signed int num; // signed: 有符号数(默认可省略)
- // unsigned char byte; // 显式声明无符号字符
- // inline void func() {} // inline(C99): 内联函数优化
复制代码
十一. define 定义常量和宏
关键注释说明
| 宏类型 | 示例 | 作用 | 注意事项 |
|---|
| 简单常量 | #define PI 3.1415 | 定义全局常量 | 全大写命名,无分号结尾 | | 函数式宏 | #define SQUARE(x) ((x)*(x)) | 类似函数但无类型检查 | 参数必须用括号包裹,避免副作用 | | 多语句宏 | do { ... } while(0) | 安全封装多行代码 | 使用反斜杠\换行 | | 字符串化 (#) | #define STRINGIFY(x) #x | 将标识符转为字符串 | 用于调试输出 | | 连接符 (##) | #define CONCAT(a,b) a##b | 拼接标识符生成新变量/函数名 | 避免过度使用降低可读性 | | 可变参数宏 | #define LOG(fmt, ...) | 支持不定参数 | C99+标准支持 | | 条件编译宏 | #if DEBUG_MODE ... | 根据条件启用/禁用代码块 | 用于跨平台或调试代码 |
宏 vs 常量变量
| 特性 | 宏 (#define) | 常量变量 (const) |
|---|
| 类型检查 | 无 | 有 | | 内存占用 | 不占用内存 | 占用内存 | | 调试支持 | 展开后不可见 | 可追踪 | | 作用域 | 文件作用域(除非#undef) | 遵循变量作用域规则 | | 适用场景 | 简单替换、条件编译 | 需要类型安全或复杂计算的常量 |
代码示例: - #include <stdio.h>
- /*================== 定义常量 ==================*/
- // 1. 基础常量(全大写命名规范)
- #define PI 3.1415926 // 定义浮点常量
- #define MAX_SIZE 100 // 定义整型常量
- #define WELCOME_MSG "Hello" // 定义字符串常量
- /*================== 带参数的宏(函数式宏) ==================*/
- // 2. 简单运算宏(参数用括号包裹防止优先级问题)
- #define SQUARE(x) ((x) * (x)) // 计算平方
- #define MAX(a, b) ((a) > (b) ? (a) : (b)) // 返回较大值
- // 3. 多语句宏(用 do-while(0) 包裹保证语法安全)
- #define PRINT_SUM(a, b) do { \
- printf("%d + %d = %d\n", a, b, (a)+(b)); \
- } while(0)//do-while 循环执行一次后立即退出(0 是假条件)
- /*================== 高级宏技巧 ==================*/
- // 4. 字符串化操作符 (#)
- #define STRINGIFY(x) #x // 将参数转为字符串
- #define PRINT_VAR(x) printf(#x " = %d\n", x)
- // 5. 连接操作符 (##)
- #define CONCAT(a, b) a##b // 拼接标识符
- #define MAKE_VAR(name, num) name##num
- // 6. 可变参数宏(C99+)
- #define LOG(fmt, ...) printf("[LOG] " fmt, __VA_ARGS__)
- // 7. 条件编译相关宏
- #define DEBUG_MODE 1 // 调试模式开关
- #if DEBUG_MODE
- #define DEBUG_LOG(msg) printf("[DEBUG] %s\n", msg)
- #else
- #define DEBUG_LOG(msg)
- #endif
- int main() {
- // 使用基础常量
- printf("PI = %.2f\n", PI);
- printf("MAX_SIZE = %d\n", MAX_SIZE);
- printf("消息: %s\n", WELCOME_MSG);
- // 2.使用函数式宏
- int num = 5;
- printf("%d的平方 = %d\n", num, SQUARE(num));
- printf("较大值: %d\n", MAX(10, 20));
- PRINT_SUM(3, 4); // 输出:3 + 4 = 7
- // 高级宏演示
- int value = 100;
- PRINT_VAR(value); // 输出:value = 100
- printf("PI的字符串: %s\n", STRINGIFY(PI)); // 输出:PI的字符串: PI
- int CONCAT(var, 1) = 10; // 生成变量 var1
- int MAKE_VAR(temp_, 2) = 20; // 生成变量 temp_2
- printf("var1 = %d, temp_2 = %d\n", var1, temp_2);
- LOG("结果: %d, 消息: %s\n", 200, "成功"); // [LOG] 结果: 200, 消息: 成功
- DEBUG_LOG("调试信息"); // 根据DEBUG_MODE开关输出
- return 0;
- }
复制代码
输出:
十二. 指针(泛谈)
1. 什么是指针?
指针是一种存储变量内存地址的特殊变量。通过指针可以直接访问或操作内存中的数据,是C语言实现高效内存管理和灵活数据操作的核心机制。
2. 指针操作解析表
| 操作类型 | 代码示例 | 核心作用 | 注意事项 |
|---|
| 声明与初始化 | int *p = &var; | 获取变量地址 | 类型必须匹配 | | 解引用 | *p | 通过地址访问数据 | 确保指针已初始化 | | 指针算术 | p + i | 数组遍历/偏移访问 | 注意越界问题 | | 动态内存管理 | malloc/free | 堆内存分配与释放 | 必须成对使用,避免内存泄漏 | | 多级指针 | int **pp = &p; | 操作指针的指针 | 解引用层级需对应 | | 函数指针 | int (*fp)(int, int) | 动态调用函数 | 参数和返回值类型必须匹配 | | const修饰指针 | const int *p | 限制数据或指针的修改权限 | 根据const位置理解限制范围 | | 数组指针 | int (*ptr)[3] | 操作多维数组 | 列数必须匹配 |
3. 代码示例 - #include <stdio.h>
- #include <stdlib.h>
- // 函数指针示例
- int add(int a, int b) { return a + b; }
- int main() {
- /*================= 1. 基础指针操作 =================*/
- int num = 10;
- int *p1 = # // 声明指针并初始化为num的地址
- printf("1. num值: %d, 地址: %p\n", *p1, p1); // *解引用获取值
- /*================= 2. 指针算术运算 =================*/
- int arr[] = {10, 20, 30};
- int *p2 = arr; // 数组名即首元素地址
- printf("2. 数组遍历:");
- for (int i = 0; i < 3; i++) {
- printf(" %d", *(p2 + i)); // 指针加法等价于 arr[i]
- }
- printf("\n");
- /*================= 3. 动态内存管理 =================*/
- int *heap_ptr = malloc(5 * sizeof(int)); // 堆内存分配
- if (heap_ptr != NULL) {
- heap_ptr[0] = 100; // 通过指针操作动态数组
- free(heap_ptr); // 必须手动释放内存
- heap_ptr = NULL; // 置空防止野指针
- }
- /*================= 4. 多级指针 =================*/
- int **pp = &p1; // 二级指针:指向指针的指针
- printf("4. 多级指针值: %d\n", **pp);
- /*================= 5. 函数指针 =================*/
- int (*func_ptr)(int, int) = add; // 声明函数指针
- printf("5. 函数指针运算: %d\n", func_ptr(3, 5));
- /*================= 6. const与指针 =================*/
- const int *p3 = # // 指向常量的指针(值不可改)
- int *const p4 = # // 常量指针(地址不可改)
- *p4 = 20; // 允许修改值
- // p4 = NULL; // 错误!地址不可变
- /*================= 7. 指针与数组的关系 =================*/
- int matrix[2][3] = {{1,2,3}, {4,5,6}};
- int (*matrix_ptr)[3] = matrix; // 数组指针:指向二维数组的行
- printf("7. 二维数组第二行: %d\n", matrix_ptr[1][2]); // 输出6
- /*================= 8. 空指针与野指针 =================*/
- int *null_ptr = NULL; // 空指针(安全状态)
- int *wild_ptr; // 未初始化的野指针(危险!)
- // printf("%d", *wild_ptr); // 未定义行为(可能崩溃)
- return 0;
- }
复制代码
输出:
4. 总结
指针的核心概念总结
-
本质:指针是内存地址的容器,通过地址直接操作数据。 -
类型安全:指针类型决定了解引用时的内存解释方式(如int*与float*差异)。 -
内存管理:
-
常见风险:
-
野指针:未初始化或已释放的指针。 -
内存泄漏:忘记释放堆内存。 -
越界访问:指针算术超出合法范围。
5. 最佳实践建议
-
初始化原则:声明指针时立即赋值(有效地址或NULL)。 -
空指针检查:使用动态内存前验证 malloc 返回值。 -
const修饰:根据需求选择const int*(保护数据)或int* const(保护指针)。 -
工具辅助:使用静态分析工具(如Clang静态分析器)检测指针问题。
十三. 结构体(泛谈)
1. 什么是结构体?
结构体(struct)是C语言中用于组合多个不同类型变量的自定义复合数据类型,可将逻辑相关的数据封装为单一实体,增强代码可读性和可维护性。
2. 结构体操作解析表
| 操作类型 | 代码示例 | 核心作用 | 注意事项 |
|---|
| 结构体定义 | struct Student { ... }; | 创建自定义复合类型 | 成员可以是任意类型(包括其他结构体) | | 变量初始化 | struct Student stu = {...} | 声明并初始化结构体变量 | 字符串需用strcpy赋值 | | 成员访问 | stu.age | 通过.访问成员值 | 确保结构体变量已初始化 | | 指针访问 | stu_ptr->name | 通过指针间接访问成员 | 指针必须指向有效内存地址 | | 结构体数组 | struct Student class[3] | 存储多个结构体实例 | 数组索引从0开始 | | 结构体赋值 | stu3 = stu1 | 浅拷贝所有成员值 | 含指针成员时需深拷贝 | | 动态内存分配 | malloc(sizeof(...)) | 堆内存中创建结构体 | 必须手动free释放内存 | | 嵌套结构体 | Line line | 构建复杂数据结构 | 逐级访问成员(如line.start.x) | | 函数参数传递 | void func(struct Student) | 按值或按地址传递结构体 | 大结构体建议传指针减少拷贝开销 |
3. 代码示例 - #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- /*================= 1. 结构体定义与初始化 =================*/
- // 定义结构体类型
- struct Student {
- char name[50];
- int age;
- float score;
- };
- // 使用typedef简化结构体类型名
- typedef struct {
- int x;
- int y;
- } Point;
- int main() {
- /*============= 2. 结构体变量声明与初始化 =============*/
- // 声明并直接初始化
- struct Student stu1 = {"张三", 20, 85.5};
-
- // 先声明后赋值
- struct Student stu2;
- strcpy(stu2.name, "李四"); // 字符串需使用strcpy
- stu2.age = 22;
- stu2.score = 90.0;
- /*============= 3. 访问结构体成员 =============*/
- printf("学生1: %s, %d岁, 分数: %.1f\n",
- stu1.name, stu1.age, stu1.score); // 输出:张三,20岁,85.5
- /*============= 4. 结构体指针与箭头运算符 =============*/
- struct Student *stu_ptr = &stu1;
- printf("指针访问: %s\n", stu_ptr->name); // ->运算符访问成员
- /*============= 5. 结构体数组 =============*/
- struct Student class[3] = {
- {"王五", 19, 88.0},
- {"赵六", 21, 92.5},
- {0} // 剩余元素自动初始化为0
- };
- printf("数组遍历: %s的分数为%.1f\n", class[1].name, class[1].score); // 赵六:92.5
- /*============= 6. 结构体赋值与拷贝 =============*/
- struct Student stu3;
- stu3 = stu1; // 结构体直接赋值(浅拷贝)
- stu3.age = 23;
- printf("拷贝后年龄: %d vs %d\n", stu1.age, stu3.age); // 20 vs 23
- /*============= 7. 动态内存分配 =============*/
- struct Student *heap_stu = malloc(sizeof(struct Student));
- strcpy(heap_stu->name, "孙七");
- heap_stu->age = 24;
- heap_stu->score = 95.0;
- free(heap_stu); // 必须释放内存
- heap_stu = NULL;
- /*============= 8. 嵌套结构体 =============*/
- typedef struct {
- Point start;
- Point end;
- } Line;
- Line line = {{0, 0}, {10, 10}};
- printf("线段长度: %.2f\n",
- sqrt(pow(line.end.x - line.start.x, 2) +
- pow(line.end.y - line.start.y, 2))); // 14.14
- /*============= 9. 结构体作为函数参数 =============*/
- void print_student(struct Student s) {
- printf("函数参数传递: %s\n", s.name);
- }
- print_student(stu1); // 输出:张三
- return 0;
- }
复制代码
输出:
4. 总结
-
内存对齐:结构体大小可能大于成员总和(因内存对齐优化),可用sizeof(struct Student)查看。 -
深浅拷贝:直接赋值复制所有成员值(浅拷贝),若含指针需手动复制指向数据(深拷贝)。 -
灵活应用:
-
构建链表、树等数据结构 -
封装文件头信息(如BMP文件头) -
定义网络协议数据包格式 -
与联合体区别:结构体成员独立占用内存,联合体成员共享内存。
5. 最佳实践建议
-
命名规范:结构体类型名首字母大写(如Student)。 -
typedef简化:使用typedef避免重复写struct关键字。 -
传参优化:传递大结构体时使用指针(void func(struct Student *s))。 -
内存管理:动态分配的结构体指针使用后立即置空,防止野指针。
|