C语言基础:数据类型、输入输出与垃圾字符回收详解
变量
程序运行过程中会发生变化
格式:
存储类型 数据类型 变量名
auto
存储类型:决定变量存放的位置(auto 表示默认存储在栈区,由系统自动管理)
数据类型:决定开辟的空间大小
变量名:遵循标识符命名规则
类型 | 含义 | 字节大小 | 取值范围(了解) |
char | 字符型 | 1 | -128~127 |
int | 整型 | 4 | 约 -21 亿 ~ 21 亿 |
short | 短整型 | 2 | -32768~32767 |
long | 长整型 | 32位:4 64位:8 | 依赖平台(32/64位不同) |
float | 单精度浮点型 | 4 | 有效位数:6-7 |
double | 双精度浮点型 | 8 | 有效位数:15-16 |
使用sizeof运算符可以查看任意类型在当前平台下占用的字节数:
printf("%ld\n", sizeof(char));以上代码输出char类型在当前平台下的字节大小(通常为 1)。
局部变量和全局变量的区别:
对比维度 | 局部变量 | 全局变量 |
定义位置 | 函数体内部 | 函数体外部 |
初值 | 未初始化,值为随机数 | 未初始化,值为0 |
存储位置 | 栈区 | 全局区 |
作用域 | 当前函数体内 | 整个程序 |
生命周期 | 随函数共存亡 | 随整个程序共存亡 |
简单记忆:局部变量在函数内部定义,函数结束即销毁;全局变量在函数外部定义,整个程序运行期间一直存在。
虚拟内存空间
32位系统:4G 虚拟地址空间;64位系统:264 虚拟地址空间
栈区:系统自动开辟、回收(存放局部变量、函数参数)
堆区:程序员手动开辟、回收(通过 malloc/free 管理)
全局区 / 静态区:存放全局变量和静态变量,进一步分为:
.bss 段:未初始化或初始化为 0 的全局/静态变量
.data 段:已初始化且初值非 0 的全局/静态变量
常量区:存放字符串常量等只读数据
常量
程序运行过程中不会发生变化
整型常量
支持四种进制表示:二进制(0b 前缀)、八进制(0 前缀)、十进制(默认)、十六进制(0x 前缀)
浮点型常量
float 和 double 类型,如 3.14(默认 double 类型),3.14f 表示 float 类型
字符常量
由单引号包裹单个元素
char a = 'a';
a 是变量
'a' 是字符常量
\ 转义字符:如 \n(换行)、\t(制表)、\\(反斜杠本身)
字符串常量
由双引号包裹,以 '\0' 为结束标志
"hello" ----> 实际存储为 "hello\0",占 6 个字节
标识常量(宏定义)
宏定义:在预处理阶段进行文本替换
格式:
#define 宏名 常量值或表达式或函数
宏名:一般用大写,为了和普通变量区分开
特点:先进行单纯的替换,最后再计算(注意运算优先级,建议加括号保护)
#include<stdio.h> #define N 3 #define M N+1 #define NUM N+M/2 int main(int argc, char const *argv[]) { printf("%d\n", NUM); return 0; }宏示例:求两个数中的最大值
#include<stdio.h> #define MAX a>b?a:b int main(int argc, char const *argv[]) { int a = 33; int b = 56; printf("%d\n", MAX); a = 67; b = 89; printf("%d\n", MAX); return 0; }宏函数(带参数的宏)
#include<stdio.h> #define MAX(a, b) a>b?a:b int main(int argc, char const *argv[]) { printf("%d\n", MAX(12, 35)); printf("%d\n", MAX(87, 59)); return 0; }宏函数与普通函数的区别:宏函数在预处理阶段直接展开,没有函数调用开销,但不做类型检查,且多次参数求值可能带来副作用。
输入输出
按字符输入输出
getchar
int getchar(void);// int a;
功能:从终端(标准输入)读取一个字符
参数: void(无参数)
返回值:读取字符的 ASCII 码值(int 类型),失败返回 EOF
putchar
int putchar(int c);
功能:向终端(标准输出)输出一个字符
参数:要输出的字符的 ASCII 码值
返回值:成功输出时返回该字符的 ASCII 码值,失败返回 EOF
#include<stdio.h> int main(int argc, char const *argv[]) { // getchar(); int n = getchar(); putchar(n); putchar(10); return 0; }利用 getchar 吸收多余的换行符:
当使用 scanf 读取字符后,缓冲区中可能残留换行符,可以在下一次 scanf 前调用 getchar() 将其吸收掉:
char a = 0; scanf("%c", &a); getchar(); printf("%d\n", a); char b = 0; scanf("%c", &b); printf("%d\n", b);按格式输入输出
printf
int printf(const char *format, ...);
功能:按指定格式向终端输出内容
参数:第一个参数是格式字符串,后面是不定长的输出项
返回值:成功输出元素的个数
常用格式占位符:
%d:int 整型
%c:char 字符型
%f:float 单精度浮点型
%lf:double 双精度浮点型
%s:字符串
%p:指针地址
%o / %#o:八进制(加 # 显示前导 0)
%x / %#x:十六进制(加 # 显示前导 0x)
精度控制:.n 保留 n 位小数,四舍五入
%f 默认输出小数点后六位
float a = 123.456; printf("%f\n", a); // 输出 123.456000 printf("%.1f\n", a); // 输出 123.5位宽控制:m 位宽,位宽值大于数据实际宽度时才会在左侧补空格对齐
#include<stdio.h> int main(int argc, char const *argv[]) { int a = 2; int b = 22; int c = 222; printf("%3d\n", a); // 输出 " 2" printf("%3d\n", b); // 输出 " 22" printf("%d\n", c); // 输出 "222" return 0; }scanf
int scanf(const char *format, ...);
功能:按指定格式从终端读取输入
参数:格式字符串 + 变量地址列表(需使用 & 取地址)
返回值:成功赋值的输入项个数;第一个元素赋值失败时返回 0
#include<stdio.h> int main(int argc, char const *argv[]) { int a = 0; int b = 0; printf("please input a b:\n"); scanf("%d %d", &a, &b); printf("%d %d\n", a, b); return 0; }需要注意:scanf 中%d、%f、%s会自动跳过前置空白字符(空格、换行、制表符),但%c不会自动跳过。如果想深入了解%c的特殊行为以及如何处理缓冲区中的垃圾字符,请继续阅读下一章「垃圾字符回收」。
垃圾字符回收
一、核心问题:%c会读取空白垃圾字符
1. 空白字符包含哪些
空格 、制表符\t、换行符\n,都属于空白垃圾字符,%c会原封不动读入变量。
2. 案例 1:连续两个%c无空格
char a = 0; char b = 0; scanf("%c%c", &a, &b); printf("%d %d\n", a, b);输入场景演示
场景 1:输入x y(x + 空格 + y,回车)
- 第一个
%c读取字符x→ 存入 a - 第二个
%c直接读取中间的空格→ 存入 b - 输出结果:
120 32(x 的 ASCII=120,空格 ASCII=32)
场景 2:输入x按回车,再输入y
- a =
x(ASCII=120) - b = 换行符
\n(ASCII=10) - 输出:
120 10
结论:
%c不会自动跳过空格、回车,缓冲区中残留的空白字符会被直接当成有效字符读取,产生意料之外的垃圾值。
二、空白回收方案:格式串前加空格%c
语法规则
在%c转换符前面写一个空格 ,作用:自动跳过缓冲区中一个或多个连续空白字符(空格、\t、\n全部清空)。
对比代码
char a = 0; scanf("%c", &a); // 无前置空格,会读回车/空格 printf("%d\n", a); char b = 0; scanf(" %c", &b); // 前置空格,自动清空前面所有空白垃圾 printf("%d\n", b);连续双字符修正写法
如果要连续读取两个字符、忽略中间空白,给每个%c前加空格:
scanf(" %c %c", &a, &b);此时输入a 回车 b、a 空格 b、a\tb都会正常读取 a、b,空白全部被过滤。
三、易混淆知识点:%d/%f和%c的区别
%d、%f、%s:默认自动跳过前置空白,不用手动加空格;%c:不会自动跳过空白,缓冲区残留的回车 / 空格会直接读入,必须手动加空格过滤垃圾字符。
小结:遇到%c读字符前如果前面有输入残留,优先使用%c(前置空格)方案;如果只是想丢弃单个回车,也可以用getchar()吸收。