Skip to main content

第二课:数据类型和变量

(一)令牌(Token)

在 C 语言中,令牌(Token) 是程序的基本组成单位。编译器通过对源代码进行词法分析,将代码分解成一个个的令牌,进而转化成汇编语言。

C 语言的令牌主要包括以下几种类型:

1. 关键字(Keywords)

  • 关键字是 C 语言预定义的、有特殊意义的单词,不能用作标识符。
  • 例如:int, return, if, while, for 等。

2. 标识符(Identifiers)

  • 标识符是程序中变量、函数、数组等的名字。
  • 命名规则:
    • 第一个字符必须是字母(A-Z 或 a-z)或下划线 _
    • 后续字符可以是字母、数字或下划线;
    • 不能以数字开头
    • 区分大小写,如 Varvar 是不同的标识符。

3. 常量(Constants)

  • 常量是固定值,在程序执行期间不会改变。
  • 类型包括:
    • 整型常量(如 10, -5
    • 浮点型常量(如 3.14, -0.5
    • 字符常量(如 'a', '\n'
    • 枚举常量

4. 字符串字面量(String Literals)

  • 由双引号 " 括起来的字符序列,例如 "Hello World"
  • 字符串末尾会自动添加一个空字符 \0 以标识字符串结束。

5. 运算符(Operators)

  • 运算符用于执行各种操作,如算术、逻辑、比较等。

  • 算术运算符+, -, *, /, %

  • 关系运算符==, !=, >, <, >=, <=

  • 逻辑运算符&&, ||, !

  • 位运算符&, |, ^, ~, <<, >>

  • 赋值运算符=, +=, -=, *=, /=, %=

  • 其他运算符sizeof, ?:, &, *, ->, .

6. 分隔符(Separators)

  • 用于分隔代码的不同部分。
  • 例如:
    • ;:每个语句必须以分号结束,表示一个逻辑实体的结束;
    • {}:表示代码块;
    • ():用于函数参数或优先级;
    • []:用于数组下标。

(二)数据类型

变量的类型决定了变量存储所占用的空间,以及如何解释存储的位模式。

C 语言中的数据类型主要可以分为以下几种:

1. 基本数据类型

  • 整型(int)
  • 字符型(char)
  • 浮点型(float)
  • 双精度浮点型(double)

这些都是算术类型,用于表示数值。

整数类型对照表

类型存储大小值范围
char1 字节-128 到 127 或 0 到 255
unsigned char1 字节0 到 255
signed char1 字节-128 到 127
int2 或 4 字节-32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647
unsigned int2 或 4 字节0 到 65,535 或 0 到 4,294,967,295
short2 字节-32,768 到 32,767
unsigned short2 字节0 到 65,535
long4 字节-2,147,483,648 到 2,147,483,647
unsigned long4 字节0 到 4,294,967,295

浮点类型对照表

类型存储大小值范围精度
float4 字节1.2E-38 到 3.4E+386 位有效位
double8 字节2.3E-308 到 1.7E+30815 位有效位
long double16 字节3.4E-4932 到 1.1E+493219 位有效位

2. 枚举类型

枚举类型也是一种算术类型,用于定义只能赋予特定离散整数值的变量。


3. void 类型

  • void 表示没有值的数据类型,常用于函数的返回类型或指针类型。

  • void 类型的使用场景

序号用法描述
1函数返回为空不返回任何值的函数使用 void 作为返回类型,例如:void exit(int status);
2函数参数为空不接受任何参数的函数参数类型为 void,例如:int rand(void);
3指针指向 voidvoid * 表示通用指针,可以指向任何类型的数据。例如:void *malloc(size_t size);

4. 派生类型

  • 数组类型(如 int arr[10];
  • 指针类型(如 int *p;
  • 结构体类型(如 struct Student {...};

5. 类型转换

隐式类型转换

int i = 10;
float f = 3.14;
double d = i + f; // 隐式将int类型转换为double类型

显式类型转换

double d = 3.14159;

int i = (int)d; // 显式将double类型转换为int类型

(三)变量类型

变量是程序中可操作的存储空间的名称。
在 C 语言中,每个变量都有特定的类型,类型决定了变量的存储大小和布局。
变量的命名必须以字母或下划线 _ 开头,区分大小写

1. 基本变量类型

类型描述
char表示单个字符(如 'a''1''@' 等),占 1 字节(8 位),不是字符串
int整型,通常占 4 字节,取值范围为 -2147483648 到 2147483647。
float单精度浮点型,占 4 字节。格式为:1 位符号 + 8 位指数 + 23 位尾数。例如:0 01111100 010000... 表示 0.15625。
double双精度浮点型,占 8 字节。格式为:1 位符号 + 11 位指数 + 52 位尾数。
void表示“无类型”,通常用于函数返回值或指针类型。

2. 变量定义,声明与初始化

变量的定义例如:

float salary;         // 定义一个 float 类型变量 salary
int i, j, k;          // 同时定义多个 int 类型变量

salary = 20;          // 给变量 salary 赋初值 20

或者直接变量初始化例如:

int x = 10;           // 定义并初始化整型变量 x
char ch = 'A';        // 定义并初始化字符型变量 ch
int d = 3, f = 5;     // 定义并初始化多个整型变量
extern int d;         // 声明一个外部变量

在 C 语言中,变量的声明(Declaration)变量的定义(Definition) 是不同的概念。

比较项声明(Declaration)定义(Definition)
作用告诉编译器变量的名称和类型声明并分配内存,可能包括初始化
是否分配内存
是否可重复可以重复声明(类型一致)只能定义一次
关键字可使用 extern 引用其他文件中定义的变量不需要 extern
示例extern int x; // 声明变量 x,但不分配内存int x; // 定义变量 x,分配内存
int x = 10; // 定义并初始化变量 x
#include <stdio.h>

int x;        // 定义全局变量 x
int y;        // 定义全局变量 y

int addtwonum()
{
    extern int x, y;  // 声明 x 和 y 是外部变量(全局变量)
    x = 1;
    y = 2;
    return x + y;
}

int main()
{
    int result;
    result = addtwonum();  // 调用函数
    printf("result 为: %d", result);
    return 0;
}

程序说明:

  • xy 在函数外部被定义,因此是全局变量
  • addtwonum 函数中使用 extern 声明它们,表示这两个变量已经在别处定义。
  • 函数中为 xy 赋值并返回它们的和。
  • 主函数中调用 addtwonum 并输出结果。

3. 字符数组与字符串

在 C 语言中,字符串是以 \0 结尾的字符数组。例如:

char name[] = "OK";  // 实际存储为 'O', 'K', '\0'

4. 变量的初始化规则

  • 局部变量:没有默认值,未初始化的局部变量值未定义(可能是任意的垃圾值)。
  • 全局变量和静态变量:默认初始化为 0;
    • 字符型默认值为 '\0'
    • 指针默认值为 NULL

5. 变量类型对比

静态变量 vs 非静态变量,局部变量 vs 全局变量

类型定义位置生命周期存储位置作用域
全局变量函数外程序启动到终止全局存储区所有文件(若非 static)
局部变量函数内部函数调用期间仅限函数内部
静态变量使用 static 修饰程序启动到终止静态存储区局部静态:函数内
全局静态:文件内
非静态变量static 修饰局部:函数调用期间
全局:程序启动到终止
局部:栈
全局:全局存储区
局部:函数内
全局:整个程序

总结

  • 静态 vs 非静态:是否在内存中持续存在(生命周期)。
  • 全局 vs 局部:变量可以被访问的范围(作用域)。

跨文件调用变量

在 C 语言中,extern 关键字用于在一个源文件中引用另一个源文件中定义的变量

这允许多个源文件共享同一个全局变量或函数,是组织大型项目时的一种常见方式。

示例一:

file1.c

int myGlobalVar = 100;  // 定义并初始化全局变量

file2.c

#include <stdio.h>
extern int myGlobalVar;  // 声明外部定义的变量

int main() {
    printf("Value of myGlobalVar: %d\n", myGlobalVar);  // 使用变量
    return 0;
}

编译命令:

gcc file1.c file2.c -o test.exe

示例 2:函数与变量跨文件调用

file1.c

#include <stdio.h>

extern int x;  // 声明外部变量
extern int y;

int addtwonum() {
    return x + y;
}

file2.c

#include <stdio.h>

int x = 1;  // 定义全局变量 x
int y = 2;  // 定义全局变量 y

int addtwonum();  // 声明外部函数

int main(void) {
    int result;
    result = addtwonum();  // 调用跨文件函数
    printf("result 为: %d\n", result);
    return 0;
}

编译命令:

gcc file1.c file2.c -o test.exe

总结

  • 使用 extern 可以实现 跨文件访问变量或函数
  • 变量只能在一个文件中定义一次,但可在多个文件中声明;
  • 编译时需要将所有相关源文件一起传入编译器,否则链接器会找不到定义。
如果你希望我将所有模块整合成一份完整的 `.md` 笔记文档或生成目录结构,也可以告诉我!