C语言入门必备基础知识大全
Linux C语言程序设计
- 基础:数据类型,数据表现形式,运算符,表达式,语句, 三大设计结构
- 核心:数组,函数,指针
- 进阶:构造类型,预处理,库文件,标准文件流,位运算
- 高级:数据结构,算法,工程管理,工程调试,版本控制
- 编译过程:预处理、编译、汇编、链接
- printf 格式化列表:要求用一组 " " 括起来 格式化列表中可出现的内容:
- 普通字符:格式化列表中的普通字符是原样输出的,
- 转义字符:(\加上特定的字符) \n \t \0 \r
- 格式化符:(%加上特定的字符) %c %d %e %s
C语言编码风格
- 常用缩进书写格式
- 有足够的注释
- 有合适的空行
- {}对齐
- 函数体内采用分层缩进和模块化的书写方式
- 不把多条语句写在程序的同一行上
- 命名:变量或函数命名要尽可能包含更多含义,但不能太长,可采用_和缩写来命名
c语言数据类型
- 基本数据类型 数值类型:int,float,double 字符类型:char
- 构造数据类型 数组、结构体(struct)、联合体(union)、枚举(enum)
- 指针类型
- 空值类型:void
- 定义类型:typedef
进制转换
- 16转8: 先转换为二进制,从后往前看,每4位一组,每组转换成8进制,然后拼接起来。
- 8进制转16: 先转换为二进制,每3位一组,每组转换成16进制,然后拼接起来。
数据表现形式
常量
- 整型常量
- 浮点型常量
- 字符型常量
- 符号常量
变量
不同类型数据在内存中的储存方式
- 在内存中是以补码方式存储的
- 原码: 将最高位作为符号位(0 表示正,1表示负),其他位代表了数据绝对值
- 反码: 如果是正数,反码就是原码,如果是负数,符号位不变,其余为按位取反
- 补码: 如果是正数,补码就是原码,如果是负数, 反码 + 1
- 格式控制符
int 整型:%d short 整型:%hd, h代表half,即一半的存储字节 long 整型:%ld long long 整型:%lld 显示不同进制的前缀: %#o、 %#x
- (在内存中以二进制小数表示)以指数方式存储,数据分为指数与小数部分 符号位(1) + 指数位(8 /11) + 尾数(23 / 52)
- 浮点型数据先分别将整数部分与小数部分转换为二进制数据。
- 将转换后的数据以科学计数法的方式表示
- 根据数据的正负,确认最高位(符号位),然后将指数 + 指数偏移量(127 / 1023) 转换为8位数据 / 11位数据,将科学计数法表示的尾数部分(.以后)放置在最后的 23/52位
注意: 浮点型数据在内存中存储的是一个近似值,因为有些浮点型数据无法用有限数位来表示。
- 面试题: 浮点型数据和 0 怎么比较?
定义一个极小的正数作为误差阈值(通常称为 Epsilon),只要浮点数的绝对值小于这个阈值,我们就认为它“等于” 0。
- 字符型数据:
在内存中并不是存储字符本身,而是存储字符的ASCII码值。
不同数据类型混合运算
类型转换方式:
- 隐式类型转换: 编译系统自动转化: 转换规则: 低优先级类型 —> 高优先级转换
- 强制类型转化: 程序员自行完成转换。 转换语法:(目标类型) 待转换数据/表达式;
printf 格式说明符
整型格式
浮点型格式
字符与字符串格式
特殊用途格式
宽度与精度
- %md:指定最小宽度为 m,不足用空格填充。
- %.nf:浮点数保留 n 位小数。
- %.ns:字符串最多输出前 n 个字符。
C 语言 I/O 缓冲区的“进与出”
查漏补缺
表达式优先级问题
整型运算核心
数据的底层存储法则(截断与溢出) 在计算机底层,数据最终都是以二进制比特序列的形式存在的。 截断机制:当把一个较大范围的数据类型(如 32 位整数)赋值给较小范围的类型(如 8 位字符)时,编译器会直接截取低位二进制存入,高位部分被丢弃。 溢出与重解释:如果截断后的二进制序列超出了目标类型的表达范围,计算机会按照目标类型的编码规则(有符号数通常采用补码)重新解释这段二进制,从而得到一个“面目全非”的数值。
运算与传参的预处理法则(整型提升) 为了保证 CPU 的运算效率,C 语言规定在表达式运算或作为可变参数(如 printf 的参数)传递时,所有小于 int 长度的整型(如 char、short)都必须先转换为 int(或 unsigned int)。这个转换过程称为“整型提升”,其核心规则如下: 有符号数的符号扩展:如果原类型是有符号的且值为负数(最高位是 1),提升时高位全部补 1;如果值为正数,高位全部补 0。这保证了提升前后的数值大小不变。 无符号数的零扩展:如果原类型是无符号的,无论数值是多少,提升时高位一律补 0。
格式化输出的解释法则(格式符决定视角) printf 等输出函数本身并不知道传入变量的真实类型,它完全依赖程序员提供的“格式控制符”来解读内存中的二进制数据。 有符号解读(如 %d):函数会将传入二进制序列的最高位视为符号位。如果是 1,则将其视为负数(补码)进行解析。 无符号解读(如 %u):函数会忽略符号位的概念,将传入的所有二进制位全部视为数值位,直接计算其代表的正整数大小。
混合运算的类型对齐法则(算术转换) 当不同长度的整数参与同一个数学运算时,编译器会按照“向长度更长、精度更高的类型对齐”的原则,自动将短类型转换为长类型,再进行计算。
- 有符号与无符号的碰撞:当有符号整数与无符号整数进行运算时,如果它们的长度相同(例如都是 32 位),有符号数会被强制转换为无符号数参与运算。这意味着原本的负数会被当作一个极大的正数来处理,这往往是导致逻辑判断出现严重偏差的隐形陷阱。
#include <stdio.h>
int main() {
// 初始化变量
signed char a = -1;
unsigned char b = 255;
short c = -1;
// 运算与输出
printf("1. %d\n", a + b); // 254
printf("2. %u\n", (char)b); // 2^32 - 1
printf("3. %d\n", (unsigned short)c); // 65535
// 0000 0000 0000 0001
// 1111 1111 1111 1110
// 1111 1111 1111 1111 2^16-1 = 65535
return 0;
}- 存储看补码,运算看提升,输出看格式。
数组
一维数组常见的陷阱
- 索引越界
- 牢记数组下标从 0 开始,到 长度-1 结束。
- 循环条件严格使用 i < 数组长度。
- 数组传参时的“长度消失”
- 数组传参时,数组名会自动转换成指针,指针的 sizeof 为指针的大小,而不是数组的长度。
- 在传递的时候需要额外增加一个长度的变量
- 返回局部数组的地址
- 函数返回局部变量的地址,函数结束后,局部变量会被回收,返回的地址会指向一个不存在的地址。
- 解决方法:
- 创建一个全局变量,将局部变量的值赋给全局变量,返回全局变量的地址。
- 使用 malloc 实现动态内存分配,返回分配的指针,注意释放内存。
- 数组名是地址常量,不能整体赋值
- 使用 memcpy 等内存拷贝函数。
- 字符数组使用 strcpy 函数。
- 未初始化导致“垃圾值”
- 定义时立即初始化。
int arr[10] = {0};可以将所有元素初始化为0。 - 全局数组或静态数组(static)会被自动初始化为0。
二维数组常见陷阱
- 定义时不能省略列数
- 在定义并初始化二维数组时,可以省略行数,但绝对不能省略列数。编译器需要知道列数来计算内存偏移,以正确访问元素。
函数传参时,列数必须明确
混淆行指针与二级指针
行指针是什么? 列指针是什么?
字符数组与字符串的专属陷阱
- 忘记字符串结束符 \0
- 在字符数组中,字符串的结束符必须用 \0 来结束。如果没有结束符,那么字符串将无法被正确处理。
- 缓冲区溢出
- 字符数组的大小必须足够大,以容纳字符串。如果字符串的长度超过字符数组的大小,那么字符串将无法被正确处理。
- 注意: strncpy 在截断时不会自动添加 \0,需要手动添加。
常用字符数组函数
- strcpy:将源字符串复制到目标字符串。
- strncpy:将源字符串复制到目标字符串,最多复制 n 个字符。
- strcat:将源字符串追加到目标字符串的末尾。
- strncat:将源字符串追加到目标字符串的末尾,最多追加 n 个字符。
- strlen:返回字符串的长度。
- strcmp:比较两个字符串。
- strncmp:比较两个字符串,最多比较 n 个字符。
- strstr:在源字符串中查找子字符串。
- strchr:在源字符串中查找字符。
- strrchr:在源字符串中从末尾开始查找字符。
- strspn: 该函数返回 str1 中第一个不在字符串 str2 中出现的字符下标。
- strcpan: 该函数返回 str1 开头连续都不含字符串 str2 中字符的字符数。