C语言知识总结
C语言核心知识总结(共8章 · 含代码示例+详细解释)
适合入门/复习:覆盖C语言核心语法,每章附可直接运行的代码+易错点解析
第1章 数据类型与基本运算
核心知识点(补充代码示例)
#include <stdio.h>
int main() {
// 1. 基本数据类型定义与初始化
int a = 10; // 整型(4字节,范围:-2^31 ~ 2^31-1)
short b = 20; // 短整型(2字节)
long c = 100000L; // 长整型(4/8字节,加L标识长常量)
float d = 3.14f; // 单精度浮点(4字节,加f避免默认double)
double e = 3.1415926;// 双精度浮点(8字节,默认浮点型)
char f = 'A'; // 字符型(1字节,存储ASCII码)
const int g = 5; // const修饰的只读常量(不可修改)
// 2. 运算符示例
int x = 7, y = 3;
printf("算术运算:7/3 = %d,7%%3 = %d\n", x/y, x%y); // 整除2,取余1
printf("自增运算:x++ = %d,++x = %d\n", x++, ++x); // 7,9(x最终为9)
printf("逻辑运算:(1>0)&&(2<1) = %d\n", (1>0)&&(2<1)); // 0(假,逻辑与短路)
// 3. 类型转换
int h = (int)3.99; // 强制转换:截断小数,h=3(非四舍五入)
double i = 5 + 3.2; // 隐式转换:int→double,结果8.2
printf("强制转换:%d,隐式转换:%.1f\n", h, i);
return 0;
}
代码解释 & 易错点
const常量:定义后无法修改(如g=6;编译报错),建议用于固定值(如π);- 算术运算:
/对整数是整除(7/3=2),%仅适用于整数(负数取余符号随被除数); - 类型转换:隐式转换自动提升精度(低→高),强制转换可能丢失精度(如3.99→3);
- 自增运算:
x++(后自增,先取值再+1)、++x(先+1再取值)。
第2章 顺序结构
核心知识点(补充代码示例)
#include <stdio.h>
int main() {
// 1. 顺序执行:输入→计算→输出
int num1, num2, sum;
printf("请输入两个整数(空格分隔):");
scanf("%d %d", &num1, &num2); // &取地址符,整型/字符型必须加
sum = num1 + num2;
printf("两数之和:%d\n", sum);
// 2. 字符输入输出(处理回车符)
char ch;
printf("请输入一个字符:");
getchar(); // 吸收scanf残留的回车符
ch = getchar(); // 读取单个字符
putchar(ch);
printf(" (ASCII码:%d)\n", (int)ch); // 强制转换看ASCII码
return 0;
}
代码解释 & 易错点
scanf注意:格式符与变量类型匹配(%d→int,%c→char),字符串数组名无需加&;- 回车符问题:
scanf读取后会残留回车,后续getchar会读取到空字符,需用getchar()吸收; putchar:仅输出单个字符,等价于printf("%c", ch)。
第3章 分支结构
核心知识点(补充代码示例)
#include <stdio.h>
int main() {
int score;
printf("输入成绩(0-100):");
scanf("%d", &score);
// 1. if多分支(范围判断)
if (score > 100 || score < 0) {
printf("输入无效!\n");
} else if (score >= 90) {
printf("优秀\n");
} else if (score >= 60) {
printf("及格\n");
} else {
printf("不及格\n");
}
// 2. switch语句(整型/字符型表达式)
char grade = 'B';
switch (grade) {
case 'A': printf("90-100\n"); break; // break跳出,避免穿透
case 'B': printf("80-89\n"); break;
case 'C': printf("70-79\n"); break;
default: printf("不及格\n"); // 无匹配时执行
}
// 3. 三目运算符(简化if-else)
int num1 = 10, num2 = 20;
int max = (num1 > num2) ? num1 : num2;
printf("最大值:%d\n", max);
return 0;
}
代码解释 & 易错点
switch限制:表达式只能是整型/字符型(不能是浮点型),case后必须是常量(如10、’A’);break作用:缺少则会“穿透”(执行当前case后继续执行下一个case);- 三目运算符:
条件 ? 真值 : 假值,适合简单赋值,不可替代复杂分支。
第4章 循环结构
核心知识点(补充代码示例)
#include <stdio.h>
int main() {
int i, sum = 0;
// 1. for循环(已知次数,最常用)
for (i = 1; i <= 10; i++) {
if (i == 5) continue; // 跳过i=5的后续代码(sum仍加5)
sum += i;
// if (i == 8) break; // 跳出循环,sum=1+2+...+7=28
}
printf("1-10累加和(跳过i=5):%d\n", sum); // 50(55-5)
// 2. while循环(未知次数)
int n = 0;
while (n < 5) {
printf("n = %d\n", n);
n++; // 必须更新,否则死循环
}
// 3. do-while循环(至少执行一次)
int m = 5;
do {
printf("m = %d\n", m);
m++;
} while (m < 5); // 条件假,但仍执行1次
return 0;
}
代码解释 & 易错点
for循环:初始化只执行1次,条件为真执行循环体,更新语句在循环体后执行;- 死循环:
while(1)是常用死循环,需用break退出; continuevsbreak:continue跳过本次循环,break跳出本层循环;do-while:适合“输入验证”(如必须输入合法值才退出)。
第5章 数组
核心知识点(补充代码示例)
#include <stdio.h>
#include <string.h> // 字符串函数头文件
int main() {
// 1. 一维数组
int arr[5] = {1,2,3,4,5}; // 初始化,大小可省略(自动推导)
printf("一维数组:");
for (int i = 0; i < 5; i++) {
printf("%d ", arr[i]); // 下标从0开始,arr[4]是最后一个元素
}
printf("\n");
// 2. 二维数组(2行3列)
int arr2[2][3] = {{1,2,3}, {4,5,6}};
printf("二维数组:\n");
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", arr2[i][j]);
}
printf("\n");
}
// 3. 字符数组(字符串)
char str[20] = "hello"; // 自动加'\0'结束符
printf("字符串长度:%zu\n", strlen(str)); // 5(不含'\0')
printf("数组大小:%zu\n", sizeof(str)); // 20(数组总字节数)
strcpy(str, "world"); // 字符串复制,覆盖原内容(需确保数组足够大)
printf("复制后:%s\n", str); // world
return 0;
}
代码解释 & 易错点
- 数组越界:
arr[5]超出一维数组范围(下标0-4),会导致程序崩溃/数据错乱; - 字符串结束符:
'\0'是字符串标志,手动赋值需加(如char str[] = {'h','e','\0'}); strlenvssizeof:strlen算有效长度,sizeof算数组总字节数;- 二维数组:
arr2[i][j]等价于*(*(arr2+i)+j),按行存储。
第6章 字符串
核心知识点(补充代码示例)
#include <stdio.h>
#include <string.h>
int main() {
char str1[20] = "hello";
char str2[20] = "world";
// 1. 字符串拼接(确保str1足够大)
strcat(str1, str2); // str1 = "helloworld"
printf("拼接后:%s\n", str1);
// 2. 字符串比较(按ASCII码逐字符比)
int cmp = strcmp("apple", "banana");
if (cmp < 0) {
printf("apple < banana\n"); // a(97) < b(98),返回负数
} else if (cmp > 0) {
printf("apple > banana\n");
} else {
printf("相等\n");
}
// 3. 查找字符/子串
char *p1 = strchr(str1, 'w'); // 查找'w'首次出现位置
char *p2 = strstr(str1, "or"); // 查找子串"or"首次出现位置
printf("字符w位置:%s\n", p1 ? p1 : "未找到"); // 防止NULL指针
printf("子串or位置:%s\n", p2 ? p2 : "未找到");
// 4. 安全输入(替代gets,避免溢出)
char input[50];
printf("输入字符串(可含空格):");
fgets(input, sizeof(input), stdin); // 限制长度
input[strcspn(input, "\n")] = '\0'; // 去除fgets读取的回车符
printf("输入内容:%s\n", input);
return 0;
}
代码解释 & 易错点
strcmp返回值:负数(前<后)、0(相等)、正数(前>后),不可用if(str1 == str2)比较字符串;strchr/strstr:未找到返回NULL,直接打印会崩溃,需先判断;fgets优化:会读取回车符,用strcspn去除末尾的\n;- 字符串函数安全:
strcpy/strcat需确保目标数组足够大,否则溢出。
第7章 指针(C语言核心)
核心知识点(补充代码示例)
#include <stdio.h>
#include <string.h>
// 函数传指针示例:交换两个数(传址调用)
void swap(int *a, int *b) {
if (a == NULL || b == NULL) return; // 防止空指针
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
// 1. 指针与变量
int num = 10;
int *p = # // p存储num的地址
printf("num值:%d,地址:%p\n", num, &num);
printf("p指向的值:%d,p自身地址:%p\n", *p, &p);
*p = 20; // 解引用修改num值,num变为20
printf("修改后num:%d\n", num);
// 2. 指针与数组
int arr[5] = {1,2,3,4,5};
int *p_arr = arr; // 数组名=首元素地址(等价于&arr[0])
printf("数组遍历(指针):");
for (int i = 0; i < 5; i++) {
printf("%d ", *(p_arr + i)); // 等价于arr[i]
}
printf("\n");
// 3. 指针与字符串
const char *str = "hello"; // 字符串常量(只读,加const避免误修改)
printf("字符串首字符:%c\n", *str);
// str[0] = 'H'; // 错误:字符串常量不可修改
char str_arr[] = "hello"; // 字符数组(可修改)
char *p_str = str_arr;
*p_str = 'H';
printf("修改后:%s\n", str_arr); // Hello
// 4. 函数传指针
int x = 5, y = 10;
swap(&x, &y); // 传地址,实参会被修改
printf("交换后:x=%d, y=%d\n", x, y); // 10,5
// 5. 二级指针(进阶)
int **pp = &p; // pp指向指针p的地址
printf("二级指针取值:%d\n", **pp); // 20(等价于*p=num=20)
return 0;
}
代码解释 & 易错点
- 指针核心:
&取地址,*解引用(访问指向的值),指针大小固定(32位=4字节,64位=8字节); - 数组与指针:
arr[i]等价于*(arr+i),指针遍历数组更高效(少一次下标计算); - 字符串常量:
const char *str = "hello"指向只读内存区,修改会崩溃; - 空指针:函数传参需判断
NULL,避免解引用空指针导致程序崩溃; - 传址调用:函数修改主函数变量的唯一方式(值传递仅拷贝)。
第8章 函数与结构体
核心知识点(补充代码示例)
#include <stdio.h>
#include <string.h>
// 1. 函数声明(原型):定义在main后需先声明
int add(int a, int b);
// 递归函数:求阶乘(n! = n*(n-1)!)
int factorial(int n) {
if (n < 0) return -1; // 异常处理:负数无阶乘
if (n == 0 || n == 1) return 1; // 基线条件(终止递归)
return n * factorial(n-1); // 递归调用(逐步拆解)
}
// 2. 结构体定义(自定义数据类型)
struct Student {
char name[20]; // 姓名
int age; // 年龄
float score; // 成绩
};
int main() {
// 函数调用
int res = add(3,5);
printf("3+5=%d\n", res);
int n = 5;
int fac = factorial(n);
if (fac == -1) {
printf("输入无效!\n");
} else {
printf("%d的阶乘:%d\n", n, fac); // 120
}
// 结构体使用
struct Student s1;
strcpy(s1.name, "张三"); // 字符串成员需用strcpy赋值
s1.age = 18;
s1.score = 90.5;
printf("学生1:%s,%d岁,%.1f分\n", s1.name, s1.age, s1.score);
// 结构体指针(常用,节省内存)
struct Student *p_s = &s1;
printf("指针访问:%s,%d岁\n", p_s->name, p_s->age); // -> 替代(*p_s).name
// 结构体数组
struct Student s_arr[2] = {
{"李四", 19, 88.0},
{"王五", 20, 95.5}
};
printf("结构体数组:\n");
for (int i = 0; i < 2; i++) {
printf("学生%d:%s,%.1f分\n", i+2, s_arr[i].name, s_arr[i].score);
}
return 0;
}
// 函数定义
int add(int a, int b) {
return a + b;
}
代码解释 & 易错点
- 递归函数:必须有基线条件(如
n==1),否则栈溢出;递归深度不宜过大(如n>1000); - 结构体赋值:字符串成员不能直接
=(如s1.name = "张三"报错),需用strcpy; - 结构体指针:
->是指针访问成员的简写,大型结构体用指针更高效(避免拷贝); - 函数声明:若函数定义在
main之后,必须先声明原型,否则编译报错。