第二章——C程序设计的初步知识 第三节——C语言基本数据类型及其定义规则

第二章——C程序设计的初步知识 第三节——C语言基本数据类型及其定义规则

亓翎_Re-Tikara Lv5

一、常量

  在生活中,我们常常会有一些不需要去更改的数值,比如说一天,我们知道他就是 24 小时,比如说 1 小时,他就绝对是 60 分钟,诸如此类的。像这种在程序运行时不会改变的值,我们称作常量。

1. 定义

  程序运行时其值不能改变的量(即常数)

2. 分类

- 符号常量

  由用户用标识符所定义的常量,称为符号常量

  • 格式:#define 符号常量   常量
1
2
#define PI 3.14159
//定义一个PI常量为3.14159
  • 一般使用大写字母作为常量名
  • 是宏定义预处理命令,不是C语句

- 直接常量

  • 整型常量  
  • 实型常量  
  • 字符常量 
  • 字符串常量
       接下来我们分别来讲一下直接常量

二、整型常量(整常数)

1. 形式

  一般来说,整型常量我们有如下三种类型,分别对应不同的进制

  • 十进制整数:由数字0~9和正负号表示.如 123,-456,0
  • 八进制整数:由数字0开头,后跟数字0~7表示.如0123,011
  • 十六进制整数:由0x开头,后跟09,af,A~F表示. 如0x123,0Xff

   对于进制之间的相互转换此处不做教学,兴趣的可以自己学学。
   其中十进制整数有正负,而八进制,十六进制没有正负,仅能以正数存在。

2. 类型

  • 根据其值所在范围确定其数据类型
  • 在整常量后加字母l或L,认为它是long int 型常量

补充:整数在内存中的存储形式

   基于在计算机中,存储数据均是以 0 和 1 的二进制作为基准。此时,存储正数很好理解,比如 5,以八位二进制为主,表示为0000 0101,但是负数就比较有意思了,比如-5,就不能单纯的表示为**-0000 0101**。

  此时我们引入两个新的概念, 有符号整数 和 无符号整数

  • 有符号整数:最高位是符号位(0是正,1是负)。
  • 无符号整数:所有位都是数值,没有符号位。

  然后,我们表示负数的话,需要基于有符号整数,再度引入一个概念叫做补码。

  • 补码的简单规则
    1. 先写出正数的二进制(比如5是 0000 0101)。
    2. 全部取反(0变1,1变0)→ 1111 1010
    3. 加1 → 1111 1011。这就是-5 的补码

  这样设计的好处就是,可以直接用加法电路计算正负数,计算机处理起来更方便。
   此时再来分别看一下 有符号整数 和 无符号整数

  • 正数:比如 5 → 0000 0101
  • 负数:比如 -5 → 1111 1011
  • 最大正数(无符号):所有位都是 1 → 1111 1111 → 255(因为 2⁸ -1 = 255)。
  • 最大负数(有符号,最高位0 是正,1 是负):最高位是 1,其他为 0 → 1000 0000 → -128(因为 2⁷ = 128)。
       总结就是,计算机用0和1的开关组合表示数字,正数直接存,负数用“补码”存,内存里存的是二进制,具体数值要看类型(有符号/无符号)和位数。所以比较有意思的就是,在不指明类型的情况下,有可能-1=65535.

三、C语言基本数据类型及其定义规则

1. 常量

在生活中,我们常常会有一些不需要去更改的数值,比如说一天有24小时,1小时等于60分钟。在程序中这些固定值就叫做常量。

1
2
3
4
5
6
#define PI 3.14159  // 符号常量示例

int main() {
printf("圆周率大约是:%.5f\n", PI);
return 0;
}

1.1 分类

  • 符号常量:用#define定义的常量,如上面的PI
  • 字面常量:直接写出的数字或字符,如1233.14'A'

1.2 注意事项

  • 常量一旦定义就不能修改
  • 建议使用全大写字母命名常量(约定俗成)

2. 变量

与常量相对的是变量,它的值可以在程序运行过程中改变。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int age = 20;        // 整型变量
float price = 9.99f; // 单精度浮点数
char grade = 'B'; // 字符型变量

int main() {
printf("年龄:%d\n", age);
printf("价格:%.2f\n", price);
printf("等级:%c\n", grade);

// 修改变量值
age = 21;
printf("明年年龄:%d\n", age);

return 0;
}

2.1 数据类型

C语言中有以下常用基本数据类型:

类型 关键字 典型用途
整型 int 存储整数
浮点型 float/double 存储小数
字符型 char 存储单个字符
无值型 void 表示没有类型

2.2 变量命名规则

  • 只能使用字母、数字和下划线
  • 首字符不能是数字
  • 区分大小写(ageAge不同)
  • 不能使用关键字(如intreturn等)
  • 推荐使用有意义的名字(如studentName而不是x

四、实型常量(实数或浮点数)

1. 表示形式

  • 十进制数形式:(必须有小数点) 如0.123, .123, 123.0, 0.0, 123.
  • 指数形式:(e或E之前必须有数字;指数必须为整数)如12.3e3 ,123E2, 1.23e-4

2. 实型常量的类型

  在 C 语言中,象是类似于 3.14,0.5 之类的数字,默认会是 double 的类型,如下

1
2
3
4
double d = 3.14; 
// 3.14 默认是 double 类型,直接赋值给 double 变量没问题。
float f = 3.14;
// 错误!虽然可以编译,但 3.14 是 double,赋值给 float 可能导致精度丢失,编译器会警告。

  如果我们想明确为 float 型,则需如下定义:

1
2
3
4
float f = 3.14f; 
//正确!`3.14f` 明确指定为 float 类型。
float g = 0.5F;
//同样有效,`F` 和 `f` 效果相同。

  对于如何选择 double 和 float,需要看自己的精度需求

特性 double(默认) float(加 f/F
内存占用 通常 8 字节 通常 4 字节
精度 高(15-17 位有效数字) 低(6-9 位有效数字)

注意:

在内存中,实数一律是以指数形式存储的

  实数(如 3.141.23e-4)在内存中以二进制科学计数法的形式存储,即 符号位 + 指数部分 + 尾数部分。这种设计可以同时满足以下需求:

  • 表示极大或极小的数值(如天文数字或量子物理中的微小值)。
  • 节省存储空间(通过指数压缩数值范围)。
  • 平衡精度与范围(通过尾数位数控制有效数字)。

  以 3.14159 为例,分步说明其如何被转换为二进制指数形式并存储:

1. 转换为二进制科学计数法

  • 十进制转换为二进制
      3.14159 的二进制表示为 11.001001000011111101101...(精确二进制可能无限长,但内存中只能存储有限位数)。

  • 规范化
      将二进制数写成 1.xxxxx… × 2^指数 的形式:
    11.001001000011111101101…_{2} =1.1001001000011111101101… _{2} ×2^{2}

      规范化的目的是统一格式,确保尾数部分的二进制数在 [1.0, 2.0) 范围内(二进制中最高位隐含为 1,无需存储)。

2. 分配内存空间(以 float 为例)

  • float 类型:占 4字节(32位),按以下结构存储:
1
符号位(1位) | 阶码(8位) | 尾数(23位)
  • 符号位(Sign)
    0 表示正数,1 表示负数。例如 3.14159 是正数 → 符号位为 0

  • 阶码(Exponent,指数部分)
      将指数 2 加上一个偏移量(float 的偏移量是 127),得到 2 + 127 = 129,然后转换为二进制:


    129_{10} =10000001_{2}
      这样,指数部分存储为 `10000001`。
  • 尾数(Mantissa,有效数字部分)
      规范化后的二进制数 1.1001001000011111101101... 中,去掉前面隐含的 1,只存储小数点后的部分:

    1001001000011111101101…→取前23位→10010010000111111011011

  尾数部分存储为 10010010000111111011011

3. 最终内存存储形式

  将符号位、阶码和尾数按顺序拼接:

1
0(符号)∥10000001(阶码)∥10010010000111111011011(尾数)0(符号)∥10000001(阶码)∥10010010000111111011011(尾数)

  二进制表示

1
0100000011001001000011111101101101000000110010010000111111011011

  十六进制表示(方便阅读):

40490FD7_{16}


计算机中存储整数不会出现误差,存储实型数往往存入误差。

   从上面的存储方式可以看出来,小数的存储方式极其复杂,并且会有一定的舍弃,这一部分舍弃也就造就了计算机在存储实型数据是会产生误差,而我们的整数因为只需要将其转换后便可以存储,所以并不会出现误差。


三、字符常量

1. 定义

  用单引号括起来的单个普通字符或转义字符,例如 'a''C''7''\n' 等等,包括大小写字母,数字,标点符号,特殊符号,转义字符。

2. 字符常量的值

  该字符的ASCII码值,在 ASCII 表中,每个字符都有对应的码值。在内存中以 ASCII码 的形式存储为整数(占1个字节)。例如 'A' 的ASCII码是 65'0' 是 48

3. 转义字符

  反斜线后面跟一个字符或一个代码值表示,用来表示一些不可见或特殊功能的字符。部分举例如下:

转义字符 含义 ASCII码
'\n' 换行符 10
'\t' 制表符(横向跳格) 9
'\r' 回车符 13
'\b' 退格符 8
'\'' 单引号 ' 39
‘"‘` 双引号 " 34
'\\' 反斜杠 \ 92
'\0' 空字符(结束符) 0

4. 非法的字符常量

使用双引号

  ”A” 是字符串常量,不是字符常量。

多个字符

  ’AB’ 是非法的,单引号内只能有一个字符。

缺少反斜杠的转义字符

  如果想表示单引号 ',必须写成 '\'',而不是 '(否则单引号会被当作结束符)。


四、字符串常量

1. 定义

  用双引号(“”)括起来的字符序列,在之前的字符常量中,仅能表示单个字符,而字符串常量能表示好多,比如”ASDFGHJK”

2.存储

  每个字符串尾自动加一个 \0 作为字符串结束标志
   以存储一个 Hello 为例

H E L L O \0
0 0 0 0 0 0

   同时,字符串常量也可以是空串,即什么都没有 " ",在内存中为

\0
0

   字符串常量和字符常量也是有区别的,比如字符常量 'a' 和字符串常量 "a",在内存中如下:

字符常量 字符串常量
a a \0

五、变量

  变量,看名字就可以看出来,他与常量不同,是可以变化的一个值。

1. 概念

  其值可以改变的量

2. 特点

  1. 变量初始化:在C语言中,变量是需要初始化的,因为变量在存储时,需要从内存单元中获取到值,而我们的内存并不会真正意义上的“删除”,只是去“覆盖”,所以说当我们的变量没有初始化时,那从内存中读到的值就可能是一些无用的杂乱数据,会对程序的运行造成影响,因此我们需要对变量进行初始化。

  2. 变量的使用:变量的使用必须遵守“先定义后使用” 的规则,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 定义方法一
int main01()
{
int a , b , c;
a = 10;
b = 20;
c = a + b;
printf("%d + %d = %d", a , b , c);
return 0;
}

//定义方法二
int main02()
{
int a = 10;
int b = 20;
int c = a + b;
printf("%d + %d = %d", a , b , c);
return 0;
}

  两种写法的区别是分不分行,作用上相同,我个人是习惯,如果变量在三个一下,使用第二种写法,如果变量超过三个,使用第一种写法。(前提是变量类型相同)

3. 变量定义的位置

  变量一般来说按照如上写法写,但是不排除有特殊情况(比如临时需要一个)


六、整型变量

  • 整型变量,即整数类型变量,占字节数随机器的不同而不同,一般只占一个机器字(机器字:即CPU一次能处理的二进制数据的位数,由寄存器和数据总线宽度决定。如32位CPU,则机器字一次只能处理32位的二进制数据,换算之后也就是四字节,同样的,64位CPU,则机器字一次只能处理64位的二进制数据,换算之后也就是8字节)。
  • 在整型变量中,共有三种数据类型,分别是:short、int、long。三者在范围上:short ≤ int ≤ long。
  • 在不同机器上,三个类型范围有所不同。具体的我们可以使用sizeof函数来查看。sizeof函数用于获取 数据类型变量 在内存中所占的 字节数。

七、实型变量

  说完整形,我们来说说实型变量(我习惯叫浮点型,从python那边带过来的习惯了,有时候我可能会把实型叫浮点型,但是为了避免混淆,还是叫实型变量)。实型变量共有两种类型,分别是:float、double

  • float:float类型占4个字节,float类型在计算机中占4个字节,float类型在计算机中的存储方式就是小数点后保留6位,也就是6位小数。
  • double:double类型占8个字节,double类型在计算机中占8个字节,double类型在计算机中的存储方式就是小数点后保留15位,也就是15位小数。

  两种类型输出如下:

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>

int main()
{
float a = 10.123456789;
double b = 10.123456789;
printf("float a = %f\n", a);
printf("double b = %f\n", b);
return 0;
}

  输出结果为:

1
2
float a = 10.123457
double b = 10.123457

  这时候有个疑问就是,为什么输出的float a = 10.123457,而double b也是等于10.123457呢?在此我们还要注意一个就是我们在使用printf函数时,要考虑到占位符%f的精度,默认情况下,%f的精度只有6位小数,我们要是想输出高i精度的数据,则需要对占位符进行修改,比如:%.10f,这种类型的格式化输出表示输出10位小数。


八、字符型变量

  字符变量,也叫字符型变量,在C语言中,字符变量通常存储的是字符的ASCII码值,而ASCII码值是数字,只占一个字节,所以其实可以与int数据之间进行运算,举例如下:

1
2
3
4
5
6
7
#include <stdio.h>
int main()
{
char a = 'a';
printf("a = %d\n", a);
return 0;
}

  输出结果为:

1
a = 97

  基于此,我们便有以下玩法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>

int main()
{
char a = 'a';
int b = 30;
int x = a + b;
int y = ‘!’ + b + a;
printf("a = %d\n", a);
printf("b = %d\n", b);
printf("x = %d\n", x);
printf("y = %d\n", y);
return 0;
}

  输出结果为:

1
2
3
4
a = 97
b = 30
x = 127
y = 160

  不仅仅是与int型相加,更好玩的还有float和double型相加。我们逐个来讲:

char与float相加


1. 类型转换规则

charfloat 进行运算时,会发生以下隐式类型转换:

  1. charint
    char 是整数类型(1字节),先被提升为 int(4字节)。
  2. intfloat
    提升后的 int 再被转换为 float(4字节),因为 float 的优先级高于 int
  3. 运算结果类型为 float
    最终运算结果以 float 类型存储。

2. 具体示例

示例 1:charfloat 相加

1
2
3
4
5
6
7
8
9
#include <stdio.h>

int main() {
char c = 'A'; // ASCII码值为 65
float f = 3.14f;
float result = c + f; // char -> int -> float
printf("Result: %f\n", result); // 输出 68.140000
return 0;
}

解释

  • c 的 ASCII 码值为 65。
  • 65 被提升为 int 类型(65),再转换为 float 类型(65.0)。
  • 65.0 + 3.14 = 68.14,结果存储为 float 类型。

示例 2:有符号 char 的负值

1
2
3
4
5
6
7
8
9
#include <stdio.h>

int main() {
signed char c = -10; // 有符号 char 的负值
float f = 2.5f;
float result = c + f; // -10 -> -10.0 -> -7.5
printf("Result: %f\n", result); // 输出 -7.500000
return 0;
}

解释

  • 有符号 char-10 被提升为 int 类型(-10),再转换为 float 类型(-10.0)。
  • -10.0 + 2.5 = -7.5,结果为 float 类型。

3. 注意事项

(1) 无符号 char 的转换

如果 charunsigned char,其范围为 0~255,转换规则类似:

1
2
3
4
unsigned char c = 200;  // 无符号 char 的最大值为 255
float f = 50.5f;
float result = c + f; // 200 -> 200.0 -> 250.5
printf("Result: %f\n", result); // 输出 250.500000

(2) 精度问题

虽然 char 的范围较小(-128127 或 0255),但 float 的精度足够表示这些整数,因此不会出现精度损失。但如果 char 的值通过复杂计算得到,需注意浮点数的舍入误差:

1
2
3
4
5
float a = 0.1f;
float b = 0.2f;
char c = 'A'; // 65
float result = a + b + c; // 0.3 + 65.0 = 65.3
printf("Result: %f\n", result); // 输出 65.300003(可能因舍入误差略有偏差)

(3) 避免直接比较

由于浮点数的精度问题,不建议直接用 == 比较 charfloat 的值:

1
2
3
4
5
char c = 'A';  // 65
float f = 65.0f;
if (c == f) { // 实际比较的是 65.0f == 65.0f,结果为真
printf("Equal\n");
}

但注意:如果 f 是通过计算得到的近似值(如 f = 65.000001),比较结果可能为假。


4. 显式类型转换

为了代码的清晰性,可以显式转换 charfloat

1
2
3
4
char c = 'B';  // ASCII码值为 66
float f = 1.5f;
float result = (float)c + f; // 66.0 + 1.5 = 67.5
printf("Result: %f\n", result); // 输出 67.500000

char与double相加


1. 类型转换规则

chardouble 相加时,会发生以下隐式类型转换:

  1. charint
    char 是整数类型(1字节),先被提升为 int(4字节)。
  2. intdouble
    提升后的 int 再被转换为 double(8字节),因为 double 的优先级高于 int
  3. 运算结果类型为 double
    最终运算结果以 double 类型存储。

2. 示例代码

示例 1:基本运算

1
2
3
4
5
6
7
8
9
#include <stdio.h>

int main() {
char c = 'A'; // ASCII码值为 65
double d = 3.14;
double result = c + d; // char -> int -> double
printf("Result: %f\n", result); // 输出 68.140000
return 0;
}

解释

  • c 的 ASCII 码值为 65。
  • 65 被提升为 int 类型(65),再转换为 double 类型(65.0)。
  • 65.0 + 3.14 = 68.14,结果存储为 double 类型。

示例 2:无符号 char 的负值

1
2
3
4
5
6
7
8
9
#include <stdio.h>

int main() {
signed char c = -10; // 有符号 char 的负值
double d = 2.5;
double result = c + d; // -10 -> -10.0 -> -7.5
printf("Result: %f\n", result); // 输出 -7.500000
return 0;
}

解释

  • 有符号 char-10 被提升为 int 类型(-10),再转换为 double 类型(-10.0)。
  • -10.0 + 2.5 = -7.5,结果为 double 类型。

3. 注意事项

(1) 无符号 char 的转换

如果 charunsigned char,其范围为 0~255,转换规则类似:

1
2
3
4
unsigned char c = 200;  // 无符号 char 的最大值为 255
double d = 50.5;
double result = c + d; // 200 -> 200.0 -> 250.5
printf("Result: %f\n", result); // 输出 250.500000

(2) 精度问题

虽然 char 的范围较小(-128127 或 0255),但 double 的精度足够表示这些整数,因此不会出现精度损失。但如果 char 的值通过复杂计算得到,需注意浮点数的舍入误差:

1
2
3
4
5
double a = 0.1;
double b = 0.2;
char c = 'A'; // 65
double result = a + b + c; // 0.3 + 65.0 = 65.3
printf("Result: %f\n", result); // 输出 65.300003(可能因舍入误差略有偏差)

(3) 避免直接比较

由于浮点数的精度问题,不建议直接用 == 比较 chardouble 的值:

1
2
3
4
5
char c = 'A';  // 65
double d = 65.0;
if (c == d) { // 实际比较的是 65.0 == 65.0,结果为真
printf("Equal\n");
}

但注意:如果 d 是通过计算得到的近似值(如 d = 65.000001),比较结果可能为假。


4. 显式类型转换

为了代码的清晰性,可以显式转换 chardouble

1
2
3
4
char c = 'B';  // ASCII码值为 66
double d = 1.5;
double result = (double)c + d; // 66.0 + 1.5 = 67.5
printf("Result: %f\n", result); // 输出 67.500000

总结

  虽然说这种玩法好玩,但实际开发中,应该避免这种做法。,因为要考虑到精度,还有遵循隐式类型转换规则,其实也是个很麻烦的东西。


  需要注意的是,没有字符串变量,用字符数组存放一整串的那种

  • 标题: 第二章——C程序设计的初步知识 第三节——C语言基本数据类型及其定义规则
  • 作者: 亓翎_Re-Tikara
  • 创建于 : 2026-02-07 02:21:28
  • 更新于 : 2026-02-10 02:48:46
  • 链接: https://re-tikara.fun/Blog/posts/fcadce25/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。