数据类型与变量
目录
一、数据类型:C 语言的 “数据容器”(选对容器才不浪费空间)
二、sizeof 操作符:测量 “容器” 的大小(避免装不下数据)
三、signed 与 unsigned:控制 “正负号” 的开关
八、单目操作符:只需一个 “容器” 的操作(自增自减是重点)
十、scanf 与 printf:数据的 “输入输出门窗”(新手最易踩坑)
场景:多组输入(比如计算多组 a+b),输入到 Ctrl+Z 结束;
✨ 引言:
为什么这部分知识点是 C 语言的 “核心骨架”?
学 C 语言就像盖房子 —— 数据类型是 “砖瓦”(存储数据的容器),操作符是 “工具”(处理数据的手段),输入输出是 “门窗”(与外界交互的通道)。这三部分直接决定你能否写出 “能跑、没错、好用” 的代码,也是新手最容易踩坑的地方!
一、数据类型:C 语言的 “数据容器”(选对容器才不浪费空间)
数据类型就像不同规格的 “收纳盒”—— 装整数用 “整型盒”,装小数用 “浮点盒”,装单个字符用 “字符盒”,盒子的大小(长度)决定了能装多少东西(取值范围)。
1. 数据类型分类(一张表分清)
| 类型大类 | 具体类型 | 核心用途 | 新手优先级 |
|---|---|---|---|
| 内置类型(自带) | 字符型(char、signed char 等) | 存单个字符(如 'a')、小范围整数 | ⭐⭐⭐⭐⭐ |
| 整型(int、long、long long) | 存整数(如年龄、分数、数量) | ⭐⭐⭐⭐⭐ | |
| 浮点型(float、double) | 存小数(如身高、体重、金额) | ⭐⭐⭐⭐⭐ | |
| 布尔型(_Bool、bool) | 存 “真 / 假”(如判断条件) | ⭐⭐⭐⭐ | |
| 自定义类型(后续学) | 数组、结构体、枚举等 | 存复杂数据(如学生信息、商品列表) | ⭐⭐⭐ |
2. 内置类型详解(避坑 + 实用场景)
(1)字符型:最小的 “迷你容器”(占 1 字节)
| 类型 | 取值范围 | 实用场景 | 注意事项 |
|---|---|---|---|
char | -128~127 | 存字符(如'a'、'5') | 默认带符号(signed char) |
signed char | -128~127 | 需存负的小整数(如 - 10) | 明确声明带符号 |
unsigned char | 0~255 | 仅存非负小整数(如 200) | 最大值是 signed char 的 2 倍 |
💡 小技巧:字符型本质是 “1 字节整数”,存字符时实际存的是 ASCII 码(比如'a'=97),所以能做算术运算(如'a' + 32 = 'A')。
(2)整型:整数的 “专属容器”(按大小分 4 类)
新手记住 “优先用 int,超范围用 long long” 就够了!
| 类型 | 内存长度(32 位 / 64 位) | 取值范围 | 日常场景 |
|---|---|---|---|
short | 2 字节(固定) | -32768~32767 | 存小整数(如年龄 18) |
int | 4 字节(固定) | -21 亿~21 亿 | 首选(分数、数量等) |
long | 4 字节 / 8 字节 | 随系统 | 跨平台兼容时用 |
long long | 8 字节(固定) | -9e18~9e18 | 存超大整数(如人口数) |
| 无符号版本 | 同对应有符号类型 | 0~ 最大值(翻倍) | 仅存非负(如索引、金额) |
(3)浮点型:小数的 “精度容器”(按精度分 3 类)
| 类型 | 精度(有效数字) | 内存长度 | 新手选择 | 避坑点 |
|---|---|---|---|---|
float | 6~7 位 | 4 字节 | 内存紧张时用(如嵌入式) | 赋值必须加f(3.14f) |
double | 15~17 位 | 8 字节 | 首选(精度高、兼容性好) | 默认小数类型(3.14是 double) |
long double | 18~19 位 | 8/10 字节 | 科学计算(如航天数据) | 不同编译器支持差异大 |
(4)布尔型:真 / 假的 “迷你容器”(C99 新增)
专门用来表示 “是 / 否”,需包含头文件#include <stdbool.h>:
#include <stdio.h>
#include <stdbool.h> // 必须加这个头文件
int main()
{
bool is_ok = true; // true=1(真)
_Bool is_error = false; // false=0(假)
if (is_ok) {
printf("执行成功\n"); // 会执行
}
if (5) { // C语言规则:非0即真(-1、3.14都算真)
printf("非0为真\n"); // 会执行
}
return 0;
}
3. 类型选择原则(新手不纠结)
- 整数:先试
int,不够用就换long long; - 小数:直接用
double,别用float(精度不够易出错); - 字符:用
char,存非负小整数用unsigned char; - 真假:用
bool(记得加头文件)。
二、sizeof 操作符:测量 “容器” 的大小(避免装不下数据)
每种容器(数据类型)的大小不同,用sizeof能精确测量,单位是字节(Byte) —— 这直接决定了数据能不能装下(比如用short存 200000 会溢出)。
1. sizeof 的 3 种用法(简单到不用记)
#include <stdio.h>
int main()
{
int a = 10;
// 用法1:测变量
printf("变量a的大小:%zd\n", sizeof(a)); // 4字节
// 用法2:测变量(省略括号)
printf("变量a的大小:%zd\n", sizeof a); // 4字节(等价于用法1)
// 用法3:测类型(必须加括号)
printf("int类型的大小:%zd\n", sizeof(int)); // 4字节
return 0;
}
2. 内存单位换算(通俗解释)
- 比特位(bit):最小单位,只能存 0 或 1(比如 “开关”);
- 字节(Byte):基本单位,1Byte = 8bit(能存 1 个字符,比如
'a'); - 常用单位:1KB=1024Byte(约 1000 个字符)、1MB=1024KB(约 100 万个字符)。
3. 常见类型大小(32 位 / 64 位系统通用)
#include <stdio.h>
#include <stdbool.h>
int main()
{
printf("bool:%zd字节\n", sizeof(bool)); // 1
printf("char:%zd字节\n", sizeof(char)); // 1(固定)
printf("short:%zd字节\n", sizeof(short)); // 2(固定)
printf("int:%zd字节\n", sizeof(int)); // 4(固定)
printf("long:%zd字节\n", sizeof(long)); // 4(32位)/8(64位)
printf("long long:%zd字节\n", sizeof(long long));// 8(固定)
printf("float:%zd字节\n", sizeof(float)); // 4(固定)
printf("double:%zd字节\n", sizeof(double)); // 8(固定)
return 0;
}
4. 核心陷阱:sizeof 不执行表达式!
这是新手最容易踩的坑 ——sizeof里的表达式只是 “摆样子”,不会真的计算:
#include <stdio.h>
int main()
{
int a = 10;
short s = 4;
// 关键点:s = a+5 不会执行,只测s的类型(short=2字节)
printf("sizeof结果:%zd\n", sizeof(s = a + 5)); // 输出2
printf("s的实际值:%d\n", s); // 输出4(s没被修改!)
return 0;
}
5. 输出格式:用 % zd(跨平台兼容)
sizeof的返回值是 “无符号整数”(类型名size_t),打印时用%zd最稳妥,避免用%d(某些编译器会报错)。
三、signed 与 unsigned:控制 “正负号” 的开关
signed和unsigned是 “类型开关”,只对整型(含 char)有效,控制容器是否能装负数。
1. 核心区别(一张表看懂)
| 开关 | 含义 | 取值范围特点 | 打印占位符 |
|---|---|---|---|
signed | 带符号(默认) | 能存正数、负数、0(如 - 10、20) | %d(int) |
unsigned | 无符号 | 只能存 0 和正数(如 0、20) | %u(int) |
2. 实用示例(含溢出陷阱)
#include <stdio.h>
int main()
{
signed int a = -20; // 等价于int a=-20(默认带符号)
unsigned int b = 20; // 无符号,不能存负数
printf("signed int:%d\n", a); // 输出-20(正常显示负数)
printf("unsigned int:%u\n", b); // 输出20(正常显示)
// 陷阱:给unsigned变量赋负数,会溢出(结果是超大正数)
unsigned int c = -1;
printf("unsigned存负数:%u\n", c); // 输出4294967295(32位系统)
return 0;
}
3. 适用场景
- 用
unsigned的唯一好处:相同长度下,最大值翻倍(比如unsigned int最大是 4294967295,是signed int的 2 倍); - 什么时候用?存非负数据(如年龄、索引、金额),避免负数干扰(比如年龄不可能是 - 5)。
四、数据类型的取值范围:别让数据 “装不下”
每个容器都有 “容量上限”,超出就会 “溢出”(数据错乱),比如用short存 32768(超出最大值 32767),结果会变成 - 32768。
1. 整型取值范围:查 limits.h 头文件
C 语言提供了现成的 “容量表”,包含在limits.h里,不用死记硬背:
#include <stdio.h>
#include <limits.h> // 必须包含
int main()
{
printf("int最小值:%d\n", INT_MIN); // -2147483648
printf("int最大值:%d\n", INT_MAX); // 2147483647
printf("unsigned int最大值:%u\n", UINT_MAX); // 4294967295
printf("long long最大值:%lld\n", LLONG_MAX); // 9223372036854775807
return 0;
}
2. 浮点型取值范围:查 float.h 头文件
浮点型的 “容量表” 在float.h里,日常用得少,需要时查阅:
#include <stdio.h>
#include <float.h> // 必须包含
int main()
{
printf("float最大值:%f\n", FLT_MAX); // 约3.4e38
printf("double最大值:%lf\n", DBL_MAX); // 约1.8e308
return 0;
}
3. 溢出的危害(新手必看)
溢出是 “隐形 bug”,编译时不报错,运行时结果错乱:
#include <stdio.h>
#include <limits.h>
int main()
{
int a = INT_MAX; // a=2147483647(int最大值)
a = a + 1; // 溢出!超出容量
printf("a+1的结果:%d\n", a); // 输出-2147483648(数据错乱)
return 0;
}
五、变量:“容器” 的具体实例(给容器起名字)
变量是 “数据类型的具体容器”—— 比如int age = 18;,int是 “容器类型”,age是 “容器名字”,18是 “容器里的东西”。
1. 变量的创建与初始化(避免垃圾数据)
#include <stdio.h>
// 全局变量:定义在main函数外,未初始化默认0
int global_var;
int main()
{
// 局部变量:定义在main函数内,必须初始化(否则是随机垃圾数据)
int local_var = 0; // 推荐:创建时赋值(初始化)
int bad_var; // 不推荐:未初始化,值是随机的
// printf("%d\n", bad_var); // 报错(VS编译不通过)
printf("全局变量:%d\n", global_var); // 输出0(默认初始化)
printf("局部变量:%d\n", local_var); // 输出0(手动初始化)
return 0;
}
2. 变量命名规则(别踩命名坑)
- 允许用:字母、数字、下划线(
_); - 禁止用:数字开头(如
123var)、关键字(如int int)、中文 / 特殊字符(如变量1); - 区分大小写:
age和Age是两个不同变量; - 推荐风格:用下划线分隔(如
student_name),别用驼峰(studentName,C 语言不推荐)。
3. 全局变量 vs 局部变量(核心区别)
| 特性 | 全局变量(main 外) | 局部变量(main 内 / 代码块内) |
|---|---|---|
| 作用域 | 整个程序(所有文件) | 仅定义的代码块内(出块失效) |
| 生命周期 | 程序运行全程 | 代码块执行时创建,执行完销毁 |
| 未初始化值 | 默认 0 | 随机垃圾数据 |
| 内存区域 | 静态区 | 栈区 |
4. 同名变量:局部覆盖全局
如果全局和局部变量同名,优先用局部的(“就近原则”):
#include <stdio.h>
int a = 100; // 全局变量a
int main()
{
int a = 10; // 局部变量a(与全局同名)
printf("%d\n", a); // 输出10(局部优先)
{
int a = 5; // 代码块内的局部变量a
printf("%d\n", a); // 输出5(更就近)
}
printf("%d\n", a); // 输出10(回到main的局部变量)
return 0;
}
六、算术操作符:数据的 “运算工具”(加减乘除取余)
算术操作符是处理数据的 “扳手、螺丝刀”,核心有+、-、*、/、%,重点避坑整数除法和取模!
1. +(加)、-(减):简单无坑
和数学里的加减完全一致,支持整数、浮点数:
#include <stdio.h>
int main()
{
int m = 3 + 5; // 8
float f = 3.5 - 1.2; // 2.3
printf("%d %f\n", m, f);
return 0;
}
2. *(乘):别写 × 符号
C 语言里没有×,用*表示乘法:
#include <stdio.h>
int main()
{
printf("%d\n", 5 * 5); // 25(5×5)
printf("%f\n", 3.14 * 2); // 6.280000
return 0;
}
3. /(除):整数除法是坑王!
核心规则(记牢!):
- 两个操作数都是整数 → 整数除法(只取商,丢弃小数,不是四舍五入);
- 至少一个是浮点数 → 浮点除法(得到小数)。
示例(含实际应用):
#include <stdio.h>
int main()
{
printf("6/4(整数除):%d\n", 6 / 4); // 1(丢弃0.5)
printf("6/4.0(浮点除):%f\n", 6 / 4.0); // 1.500000
// 实际场景:计算5分对应的百分比(满分20)
int score = 5;
// 错误:score/20是整数除(0),结果0
// int percent = (score / 20) * 100;
// 正确:score/20.0是浮点除(0.25),结果25
int percent = (score / 20.0) * 100;
printf("百分比:%d%%\n", percent); // 25%(%%打印%)
return 0;
}
4. %(取模 / 取余):仅支持整数
核心规则:
- 作用:求两个整数整除后的余数;
- 限制:操作数必须是整数(不能是浮点数);
- 正负号:结果的正负由 “第一个操作数” 决定。
示例:
#include <stdio.h>
int main()
{
printf("6%%4:%d\n", 6 % 4); // 2(6÷4商1余2)
printf("11%%5:%d\n", 11 % 5); // 1
printf("-11%%5:%d\n", -11 % 5); // -1(第一个数负)
printf("11%%-5:%d\n", 11 % -5); // 1(第一个数正)
// printf("6%%4.0:%d\n", 6 % 4.0); // 错误:取模只能是整数
return 0;
}
七、赋值操作符:给 “容器” 装东西(别和等于号搞混)
赋值操作符是 “装东西的动作”,核心有=(简单赋值)和+=/-=等(复合赋值),重点:=不是 “等于”,是 “赋值”!
1. =(简单赋值):从右向左装
#include <stdio.h>
int main()
{
int a = 10; // 初始化:创建容器a,装10
a = 20; // 赋值:把20装进a(覆盖原来的10)
printf("%d\n", a); // 输出20
// 连续赋值(不推荐,可读性差)
int b, c;
c = b = a + 3; // 先算b = 23,再算c = 23
printf("%d %d\n", b, c); // 23 23
// 推荐:拆开写
b = a + 3;
c = b;
return 0;
}
2. 复合赋值:简化操作(高效不易错)
a += 3 等价于 a = a + 3,其他同理,代码更简洁:
#include <stdio.h>
int main()
{
int a = 5;
a += 3; // a = a+3 → 8
printf("%d\n", a);
a -= 2; // a = a-2 → 6
printf("%d\n", a);
a *= 4; // a = a*4 → 24
printf("%d\n", a);
a /= 3; // a = a/3 → 8
printf("%d\n", a);
a %= 5; // a = a%5 → 3
printf("%d\n", a);
return 0;
}
八、单目操作符:只需一个 “容器” 的操作(自增自减是重点)
单目操作符只需一个操作数(比如 “给 a 加 1”),核心是++(自增)、--(自减),新手最容易搞混前置和后置!
1. ++(自增)、--(自减):容器自身 ±1
核心口诀(记死!):
- 前置
++/--:先变后用(先改值,再用改后的值); - 后置
++/--:先用后变(先用原值,再改值)。
前置++示例:
#include <stdio.h>
int main()
{
int a = 10;
int b = ++a; // 先a+1=11,再把11赋给b
printf("a=%d, b=%d\n", a, b); // a=11, b=11
return 0;
}
后置++示例:
#include <stdio.h>
int main()
{
int a = 10;
int b = a++; // 先把10赋给b,再a+1=11
printf("a=%d, b=%d\n", a, b); // a=11, b=10
return 0;
}
避坑:别在一个表达式里多次用++/--
比如a++ + ++a,结果未定义(不同编译器答案不同),尽量单独一行用(如a++;)。
2. +(正号)、-(负号):简单无坑
+:无意义(默认正数),可省略;-:改变正负号(如-a是 a 的负数);
#include <stdio.h>
int main()
{
int a = 10;
int b = -a; // -10
int c = -(-a); // 10(双重负号抵消)
printf("%d %d\n", b, c); // -10 10
return 0;
}
九、强制类型转换:强制换 “容器”(慎用!)
强制类型转换是 “把一个容器里的东西,硬塞进另一个容器”,语法(目标类型) 表达式,可能会 “挤坏东西”(损失精度)。
1. 示例(含风险):
#include <stdio.h>
int main()
{
// 浮点数转整数:丢弃小数(不是四舍五入)
int a = (int)3.14; // 3
int b = (int)5.9; // 5(不是6!)
// 整数转浮点数:安全(无精度损失)
float c = (float)10; // 10.000000
// 大范围转小范围:溢出(数据错乱)
short d = (short)32768; // short最大32767,溢出后是-32768
printf("%d %d %f %d\n", a, b, c, d);
return 0;
}
2. 避坑:括号别漏!
(double)3/2 和 (double)(3/2) 完全不同:
#include <stdio.h>
int main()
{
// 3先转double(3.0),再除以2 → 1.5
printf("%lf\n", (double)3/2); // 1.500000
// 3/2先整数除(1),再转double → 1.0
printf("%lf\n", (double)(3/2)); // 1.000000
return 0;
}
十、scanf 与 printf:数据的 “输入输出门窗”(新手最易踩坑)
printf是 “从容器里拿东西给别人看”(输出),scanf是 “从别人那拿东西装进容器”(输入),核心是 “占位符” 要和数据类型匹配!
1. printf:格式化输出(按规矩展示)
核心语法:
printf("格式字符串", 输出项1, 输出项2, ...);
- 格式字符串:含 “普通字符”(直接显示)和 “占位符”(替换成输出项);
- 常用占位符(记牢 80% 场景够用):
| 占位符 | 作用 | 示例代码 | 输出结果 |
|---|---|---|---|
%d | 打印有符号整数 | printf("%d", 100); | 100 |
%u | 打印无符号整数 | printf("%u", 100); | 100 |
%c | 打印单个字符 | printf("%c", 'a'); | a |
%s | 打印字符串 | printf("%s", "张三"); | 张三 |
%f | 打印 float 浮点数 | printf("%f", 3.14f); | 3.140000 |
%lf | 打印 double 浮点数 | printf("%lf", 3.14); | 3.140000 |
%.2f | 保留 2 位小数(四舍五入) | printf("%.2f", 3.1415); | 3.14 |
%zd | 打印 sizeof 返回值 | printf("%zd", sizeof(int)); | 4 |
实用技巧(格式化输出更美观):
#include <stdio.h>
int main()
{
// 1. 限定宽度(右对齐,不足补空格)
printf("%5d\n", 123); // 123(前面补2空格)
// 2. 左对齐
printf("%-5dhehe\n", 123); // 123 hehe(后面补2空格)
// 3. 显示正负号
printf("%+d\n", 10); // +10
printf("%+d\n", -10); // -10
// 4. 输出部分字符串(前3个字符)
printf("%.3s\n", "hello world"); // hel
return 0;
}
2. scanf:格式化输入(按规矩接收)
核心语法:
scanf("格式字符串", &变量1, &变量2, ...);
- 关键坑:变量前必须加
&(取地址符),告诉 scanf “东西装到哪”; - 数组例外:数组名本身是地址,不用加
&; - VS 使用前置:文件第一行加
#define _CRT_SECURE_NO_WARNINGS 1(屏蔽安全警告)。
常用占位符(与 printf 对应):
| 占位符 | 作用 | 代码示例 |
|---|---|---|
%d | 读取有符号整数 | scanf("%d", &a); |
%u | 读取无符号整数 | scanf("%u", &b); |
%c | 读取单个字符 | scanf(" %c", &ch); |
%s | 读取字符串(遇空白停止) | scanf("%10s", arr); |
%f | 读取 float 浮点数 | scanf("%f", &f); |
%lf | 读取 double 浮点数 | scanf("%lf", &d); |
高频避坑(新手必看!):
坑 1:% c 读取空白字符
问题:scanf("%c")会读入前一个输入的换行 / 空格,导致读不到想要的字符;解决方案:在%c前加空格(scanf(" %c", &ch));
#include <stdio.h>
int main()
{
int a;
char ch;
scanf("%d", &a); // 输入10后按回车(缓冲区残留换行)
// scanf("%c", &ch); // 读入换行,输出空
scanf(" %c", &ch); // 忽略空白,正确读入
printf("a=%d, ch=%c\n", a, ch);
return 0;
}
坑 2:% s 读取含空格的字符串
问题:输入 “hello world”,scanf("%s", arr)只读到 “hello”;解决方案:用%[^\n]读取到换行前的所有字符,同时指定长度避免溢出;
#include <stdio.h>
int main()
{
char name[20];
// 读取含空格的字符串,最多19个字符(留1个存\0)
scanf("%19[^\n]", name);
printf("你输入的是:%s\n", name); // 输入"张三 18",输出完整内容
return 0;
}
坑 3:赋值忽略符*(跳过不需要的输入)
场景:读取日期 “2025-11-15”,跳过 “-”;
#include <stdio.h>
int main()
{
int year, month, day;
// %*c跳过一个字符(这里是'-')
scanf("%d%*c%d%*c%d", &year, &month, &day);
printf("%d年%d月%d日\n", year, month, day); // 输入2025-11-15,输出正确
return 0;
}
坑 4:用返回值判断输入是否有效
场景:多组输入(比如计算多组 a+b),输入到 Ctrl+Z 结束;
#include <stdio.h>
int main()
{
int a, b;
// 成功读取2个整数才执行计算
while (scanf("%d %d", &a, &b) == 2)
{
printf("a+b=%d\n", a + b);
}
return 0;
}
📝 总结
这篇博客覆盖了 C 语言核心语法的 “三大支柱”:数据类型(容器)、操作符(工具)、输入输出(交互)”。
如果这篇博客帮你避开了几个坑,欢迎点赞收藏!
C语言数据类型与变量详解
1754

被折叠的 条评论
为什么被折叠?



