C语言基础-变量常量和数据类型
变量常量和数据类型
常量
常量指的是在程序运行期间不会改变的值。
常量分类:
- 字面常量:直接写在代码中的值,如
10,'A',3.14 - const常量:
const int max = 100; // max不能再被赋新值
- define宏定义
#define PI 3.14159
const修饰的变量会占用内存;#define是预处理指令,不占用内存。
变量
变量是程序中用于存储数据的命名内存空间。变量的值可以在程序运行过程中改变。
变量定义格式:
数据类型 变量名;
数据类型 变量名 = 初始值;
int age = 25;
float price = 19.99;
char grade = 'A';
int r = 3; // 变量定义
int r; // 没有变量值的变量定义叫做声明
extern int r; // 添加了关键字extern

- 变量定义会开辟内存空间。变量声明不会开辟内存空间。
- 变量想要使用必须有定义。
- 当编译器编译程序时,在变量使用之前,必须看到变量的定义,如果没有,编译器会自动找寻一个变量声明提升为定义。
- 如果该变量的声明前有extern关键字,则无法提升为定义。
练习1:圆的面积与周长
#include<stdio.h>
#define PI 3.1415
int main(void) {
const int r = 3; //只读变量
float s = PI * r * r;
float l = 2 * PI * r;
printf("圆的周长为:%f\n", l);
printf("圆的面积为:%f\n", s);
return 0;
}

标识符
变量与常量的统称。
命名规则:
- 通常常量使用大写、变量使用小写。大小写严格区分。
- 只能使用字母、数字、下划线命名标识符。且,数字不能开头。
sizeof运算符
- sizeof不是函数,不需要包含任何头文件,用于获取某个类型或变量所占的内存字节数。
- sizeof其结果的类型是
size_t(无符号整数类型),而不是int类型。
#include <stdio.h>
int main(void) {
int i = 0;
short s = 0;
long l = 0;
long long ll = 0;
char c = 'A';
float f = 3.14f;
double d = 3.14;
long double ld = 3.14;
unsigned char uc = 0;
unsigned short us = 0;
unsigned int ui = 0;
unsigned long ul = 0;
unsigned long long ull = 0;
printf("sizeof(int) = %zu\n", sizeof(i));
printf("sizeof(short) = %zu\n", sizeof(s));
printf("sizeof(long) = %zu\n", sizeof(l));
printf("sizeof(long long) = %zu\n", sizeof(ll));
printf("sizeof(unsigned int) = %zu\n", sizeof(ui));
printf("sizeof(char) = %zu\n", sizeof(c));
printf("sizeof(float) = %zu", sizeof(f));
printf("sizeof(double) = %zu\n", sizeof(d));
printf("sizeof(long double) = %zu\n", sizeof(ld));
printf("sizeof(unsigned char) = %zu\n", sizeof(uc));
printf("sizeof(unsigned short) = %zu\n", sizeof(us));
printf("sizeof(unsigned int) = %zu\n", sizeof(ui));
printf("sizeof(unsigned long) = %zu\n", sizeof(ul));
printf("sizeof(unsigned long long) = %zu\n", sizeof(ull));
// 也可以直接对类型使用 sizeof
printf("sizeof(void *) = %zu\n", sizeof(void*)); // 指针类型(平台相关)
return 0;
}

整型
整型用于表示整数数值。
| 类型 | 占用字节 | 范围(32位系统) | 输出 |
|---|---|---|---|
int |
4 | -2,147,483,648 ~ 2,147,483,647 | %d |
short |
2 | -32,768 ~ 32,767 | %hd |
long |
4 | 同int(Windows) | %ld |
long long |
8 | -9 × 10¹⁸ ~ 9 × 10¹⁸ | %lld |
unsigned |
同上所有 | 只表示非负数,范围翻倍 | 上面+u |
字符型
字符型用于表示单个字符。
类型
#include <stdio.h>
#include <ctype.h> // 提供字符处理函数
int main(void) {
char ch = 'A';
char ch2 = 'z';
char numChar = '5';
// 输出字符本身
printf("字符 ch 的值为:%c\n", ch);
// 获取字符的 ASCII 值(整数)
printf("字符 ch 的 ASCII 值为:%d\n", ch);
// 通过 ASCII 值创建字符
char ch_from_ascii = 66; // ASCII 66 是 'B'
printf("ASCII 值 66 对应的字符是:%c\n", ch_from_ascii);
// 判断字符类型
printf("ch2 是否是字母?%s\n", isalpha(ch2) ? "是" : "否");
printf("numChar 是否是数字字符?%s\n", isdigit(numChar) ? "是" : "否");
// 转换大小写
printf("ch2 的大写是:%c\n", toupper(ch2));
printf("ch 的小写是:%c\n", tolower(ch));
// 使用 sizeof 查看 char 类型大小
printf("char 类型所占字节数:%zu\n", sizeof(char));
printf("ch 变量所占字节数:%zu\n", sizeof(ch));
return 0;
}

注意
char本质是一个整型,占 1 字节;- 字符用单引号括起,如
'x',而不是"x"(后者是字符串)。
转义字符
转义字符是在字符或字符串中表示特殊意义的字符,写法以反斜杠 \ 开头,用来表示一些不能直接输入的字符,如换行、制表、双引号等。
常用转义字符一览表:
| 转义字符 | 描述 | ASCII码(十进制) |
|---|---|---|
\n |
换行符 | 10 |
\r |
回车符 | 13 |
\t |
水平制表符(Tab) | 9 |
\b |
退格符 | 8 |
\a |
响铃(警报) | 7 |
\\ |
反斜杠(\) | 92 |
\' |
单引号(') | 39 |
\" |
双引号(") | 34 |
\? |
问号 | 63 |
\0 |
空字符(null) | 0 |
- 字符
'\\'实际上是表示反斜杠这个字符; - 字符串
"\0"会使字符串提前结束; - 响铃符
\a在大多数现代系统中不会发出声音,但在某些终端(或启用了声音的IDE)中仍可能发出“哔”声。
实型(浮点型)
用于表示小数(实数)。
| 类型 | 占用空间(通常) | 有效位数(十进制) | 输出 |
|---|---|---|---|
float |
4 字节 | 6~7 位 | f |
double |
8 字节 | 15~16 位 | lf |
long double |
16 字节(有的平台为12) | 18~21 位 | L |
示例
#include <stdio.h>
#include <float.h> // 提供浮点数相关的宏常量
int main(void) {
float f = 3.1415926f;
double d = 3.14159265358979;
long double ld = 3.1415926535897932384626L;
// 基本输出
printf("float 类型变量 f = %f\n", f);
printf("double 类型变量 d = %f\n", d);
printf("long double 类型变量 ld = %Lf\n", ld);
// 控制小数点精度
printf("\n精度控制输出:\n");
printf("f 保留2位:%.2f\n", f);
printf("d 保留6位:%.6f\n", d);
printf("ld 保留10位:%.10Lf\n", ld);
// 科学计数法输出
printf("\n科学计数法:\n");
printf("f = %e\n", f);
printf("d = %e\n", d);
// 查看类型占用空间
printf("\nsizeof(float) = %zu 字节\n", sizeof(float));
printf("sizeof(double) = %zu 字节\n", sizeof(double));
printf("sizeof(long double) = %zu 字节\n", sizeof(long double));
// 浮点数范围和精度
printf("\nfloat 的最小值:%e\n", FLT_MIN);
printf("float 的最大值:%e\n", FLT_MAX);
printf("float 的有效位数:%d 位\n", FLT_DIG);
printf("\ndouble 的最小值:%e\n", DBL_MIN);
printf("double 的最大值:%e\n", DBL_MAX);
printf("double 的有效位数:%d 位\n", DBL_DIG);
printf("\nlong double 的最小值:%Le\n", LDBL_MIN);
printf("long double 的最大值:%Le\n", LDBL_MAX);
printf("long double 的有效位数:%d 位\n", LDBL_DIG);
return 0;
}

进制
C语言中整数常量的进制表示
| 表示方式 | 前缀 | 示例 | 实际含义(十进制) |
|---|---|---|---|
| 二进制 | 0b / 0B |
0b1010 |
10(C99或GCC扩展) |
| 八进制 | 0 |
012 |
10 |
| 十进制 | 无 | 10 |
10 |
| 十六进制 | 0x / 0X |
0xA |
10 |
标准的C并不支持二进制字面量(如 `0b1010`),但 **GCC 和 Clang 支持**。MSVC 则不支持。
二进制(C99标准及以上)
int a = 0b1010; // 等价于10(需要编译器支持)
八进制
int b = 012; // 以0开头,等价于10(十进制)
十六进制
int c = 0x1A; // 等价于26(十进制)
进制转换
十进制 ➡ 其他进制
十进制 ➡ 二进制:不断除以 2,记录余数,倒序排列
示例: 10 ➡ 二进制
10 ÷ 2 = 5 余 0
5 ÷ 2 = 2 余 1
2 ÷ 2 = 1 余 0
1 ÷ 2 = 0 余 1
倒序写:1010(即 0b1010)
十进制 ➡ 八进制:不断除以 8,记录余数
10 ÷ 8 = 1 余 2;1 ÷ 8 = 0 余 1 → 012
十进制 ➡ 十六进制:不断除以 16
10 ÷ 16 = 0 余 10 → 0xA
其他进制 ➡ 十进制
二进制 0b1010 =
1×2³ + 0×2² + 1×2¹ + 0×2⁰ = 8 + 0 + 2 + 0 = 10
八进制 012 =
1×8¹ + 2×8⁰ = 8 + 2 = 10
十六进制 0xA =
A = 10 → 10×1 = 10
C语言中进制转换的示例代码
#include <stdio.h>
int main(void) {
int dec = 255;
printf("十进制:%d\n", dec);
printf("八进制:%o\n", dec);
printf("十六进制(小写):%x\n", dec);
printf("十六进制(大写):%X\n", dec);
return 0;
}
计算机中数值的存储方式
1. 原码
原码是直接按数值的绝对值转换为二进制,最高位为符号位:
- 正数:符号位为0;
- 负数:符号位为1。
2. 反码
- 正数:与原码相同;
- 负数:符号位不变,其余位按位取反。
3. 补码(计算机内部存储使用的方式)
- 正数:与原码相同;
- 负数:在反码基础上 +1。
示例
(以8位为例,-5的表示):
十进制: -5
原码: 10000101
反码: 11111010
补码: 11111011
💡 为什么用补码?
- 统一了加减运算的电路设计;
- 避免“负零”的问题;
- 补码的加法溢出特性正好用于环绕存储。
数据溢出
数据溢出是指:一个变量存储的数值超出了它类型所能表示的最大或最小范围时,发生的数值错误或异常行为。
数据溢出是导致 程序逻辑错误、安全漏洞(如缓冲区溢出)等的常见原因。
整数类型溢出
整型变量只能表示一定范围的整数,超出后会发生“循环”,通常按 补码规则 重新从最小值开始。
#include <stdio.h>
#include <limits.h>
int main(void) {
int a = INT_MAX;
printf("a = %d\n", a);
a += 1; // 整型溢出
printf("a + 1 = %d(已溢出)\n", a);
unsigned int b = UINT_MAX;
printf("b = %u\n", b);
b += 1;
printf("b + 1 = %u(已回绕)\n", b);
return 0;
}

浮点类型溢出和精度误差
- 浮点型有非常大的表示范围;
- 超过最大值会变为 正无穷 (
+inf); - 过小的非零值可能变为 0.0(称为“下溢”);
- 不能准确表示某些小数(如 0.1),存在精度误差。
#include <stdio.h>
#include <float.h>
int main(void) {
float f = 3.4e38f; // 超过 float 最大值(~3.402823e+38)
f *= 10;
printf("f = %e\n", f); // 输出 inf
double d = 0.1 + 0.2;
if (d == 0.3)
printf("相等\n");
else
printf("不相等(浮点误差)\n");
return 0;
}

类型限制符
类型限制符用于进一步控制变量的属性,比如读写权限、存储方式或生命周期。
| 限制符 | 说明 |
|---|---|
const |
表示变量只读,值不能被修改 |
volatile |
表示变量可能随时变化(多线程/硬件),防止编译器优化代码 |
static |
表示变量具有静态存储期,局部静态变量生命周期延续至程序结束 |
extern |
声明一个在其他文件中定义的全局变量 |
register |
提示编译器将变量存储在寄存器中(已废弃) |
字符串格式化输出输入
字符串格式化输出:printf
printf 是最常用的输出函数之一,可以按格式将变量值输出到终端。
基本格式:
printf("格式字符串", 参数1, 参数2, ...);
常用格式说明符
| 类型 | 格式符 | 示例值 |
|---|---|---|
| 整数(十进制) | %d / %i |
42 |
| 无符号整数 | %u |
42 |
| 八进制 | %o |
052 |
| 十六进制 | %x / %X |
0x2a / 0X2A |
| 字符 | %c |
'A' |
| 字符串 | %s |
"hello" |
| 浮点数 | %f |
3.141593 |
| 科学计数法 | %e / %E |
3.14e+00 |
| 指针 | %p |
地址 |
size_t 类型 |
%zu |
sizeof 返回值 |
精度与宽度控制
printf("%6.2f", 3.1415); // 总宽度6,小数点后保留2位,输出 " 3.14"
printf("%-10s", "abc"); // 左对齐,占10字符宽度,输出 "abc "
字符串格式化输入:scanf
scanf 从标准输入中读取格式化数据,并存储到变量中。
基本格式
scanf("格式字符串", &变量1, &变量2, ...);
常用格式说明符
| 输入类型 | 格式符 | 示例 |
|---|---|---|
| 整数 | %d |
int i; scanf("%d", &i); |
| 字符 | %c |
char c; scanf("%c", &c); |
| 字符串 | %s |
char str[20]; scanf("%s", str); |
| 浮点数 | %f |
float f; scanf("%f", &f); |
| 双精度 | %lf |
double d; scanf("%lf", &d); |
注意事项
- 读取字符串时不加
&(char str[20]; scanf("%s", str);) %s会读取非空白字符直到空格、回车或Tabscanf读取字符后 不会跳过换行符,需用空格或getchar()处理
printf vs putchar:输出函数对比
| 项目 | printf |
putchar |
|---|---|---|
| 功能 | 格式化输出,支持多种类型 | 仅输出一个字符 |
| 参数类型 | 字符串格式 + 可变参数 | 单个字符(char 或其值) |
| 返回值 | 输出的字符数 | 成功返回输出的字符,失败返回 EOF |
| 使用场景 | 多变量输出、复杂格式 | 简单输出单个字符或循环输出 |
| 所属头文件 | <stdio.h> |
<stdio.h> |
示例
#include <stdio.h>
int main(void) {
char ch = 'A';
// printf 输出字符串和多个变量
printf("Hello, number = %d, char = %c\n", 42, ch);
// putchar 输出单个字符
putchar('H');
putchar('i');
putchar('\n');
// 输出字符串的每个字符
char str[] = "C语言";
for (int i = 0; str[i] != '\0'; i++) {
putchar(str[i]);
}
putchar('\n');
return 0;
}
scanf vs getchar:输入函数对比
| 项目 | scanf |
getchar |
|---|---|---|
| 功能 | 格式化输入,支持多种类型 | 读取一个字符(包括回车) |
| 参数类型 | 格式字符串 + 参数地址 | 无参数 |
| 返回值 | 成功读取的变量数量 | 成功返回读取的字符,失败返回 EOF |
| 注意事项 | 自动跳过空白(除 %c 外) |
会读取换行符等所有字符 |
| 使用场景 | 多变量输入、格式化读取 | 一次一个字符读取、控制台暂停 |
| 所属头文件 | <stdio.h> |
<stdio.h> |
示例
#include <stdio.h>
int main(void) {
int age;
char ch;
// scanf 格式化读取整数
printf("请输入年龄:");
scanf("%d", &age);
printf("你输入的年龄是:%d\n", age);
// 读取多余换行符(因为 scanf 不读取 '\n')
getchar(); // 吃掉回车
// getchar 读取一个字符
printf("请输入一个字符:");
ch = getchar();
printf("你输入的字符是:%c\n", ch);
return 0;
}
总结对比表
| 功能 | 推荐函数 | 原因说明 |
|---|---|---|
| 格式化多类型输出 | printf |
输出格式灵活,功能强 |
| 输出单字符 | putchar |
简洁快速,适合循环输出 |
| 格式化输入 | scanf |
适合读取整型、浮点型、字符串等 |
| 单字符读取 | getchar |
精准读取每个字符,包括空格、换行等 |
| 读取控制台暂停 | getchar |
常用于输入后暂停(防止窗口自动关闭) |
- 想读取一整行字符串,推荐使用
fgets()替代scanf("%s"),防止缓冲区溢出; - 多次使用
scanf()后紧跟getchar()是常用的“清理输入缓冲区”技巧。
本文来自博客园,作者:ffff5,转载请注明原文链接:https://www.cnblogs.com/ffff5/p/18882748

浙公网安备 33010602011771号