火龙信奥
  • 分享
  • 课程
  • 题库
  • 打卡
    • 代码对战
    • 快速对战
  • 题单
  • 团队
  • 荣誉墙
  • 商城
  • 登录 / 注册

第二章 C++ 数据类型、变量与复合结构

作者: 作者的头像   huolong , 时间:2026-08-20 18:06:49 , 所有人可见, 阅读  45

第二章 C++ 数据类型、变量与复合结构

在 C++ 语言中,数据类型是程序处理信息的基础。合理地选择数据类型、科学地组织变量与数组,以及理解底层指针和引用机制,是编写高效算法程序的基石。本章将系统讲解 C++ 的基本数据类型、变量与常量的规则、数组与字符串、指针与引用的概念,以及结构体的应用。


2.1 基本数据类型

C++ 提供了多种基本数据类型以满足不同的存储和计算需求。下表列出了常用的基本数据类型及其占用空间和数据范围:

名称 占用空间(字节) 常用别名 数据范围
int 4 signed, signed int, long, long int $-2,147,483,648 \sim 2,147,483,647$
unsigned int 4 unsigned, unsigned long $0 \sim 4,294,967,295$
char 1 char $-128 \sim 127$
unsigned char 1 unsigned char $0 \sim 255$
short 2 short int, signed short $-32,768 \sim 32,767$
unsigned short 2 unsigned short int $0 \sim 65,535$
long long 8 signed long long $-9,223,372,036,854,775,808 \sim 9,223,372,036,854,775,807$
unsigned long long 8 无 $0 \sim 18,446,744,073,709,551,615$
bool 1 无 true 或 false
float 4 无 $3.4\text{E}\pm38$(7位有效数字)
double 8 无 $1.7\text{E}\pm308$(15位有效数字)

C++11 新增:固定宽度整数类型

为了保证程序在不同平台、不同编译器下具有完全一致的位宽行为,C++11 在头文件 <cstdint> 中引入了固定宽度的整数类型:

类型 含义 范围
int8_t / uint8_t 8位有符号 / 无符号整数 $-128 \sim 127$ / $0 \sim 255$
int16_t / uint16_t 16位有符号 / 无符号整数 $-32,768 \sim 32,767$ / $0 \sim 65,535$
int32_t / uint32_t 32位有符号 / 无符号整数 同 int / unsigned int
int64_t / uint64_t 64位有符号 / 无符号整数 同 long long / unsigned long long

竞赛建议:在算法竞赛中,推荐使用 int64_t 替代传统的 long long。它的可读性更强,且能够从语言标准层面保证在任何平台下都是精确的 64 位宽,避免因平台差异导致意料之外的溢出错误。


2.2 变量与常量

1. 定义变量

  • 语法格式:变量类型 标识符;(例如:int i;)。
  • 未初始化警告:局部变量在定义时如果未进行显式初始化,其在内存中的初始值是不确定的(通常是残留的脏数据)。因此在声明变量后,建议立即初始化,如 int i = 0;。

2. 定义常量

  • 语法格式:const 变量类型 标识符 = 初始值;(例如:const int N = 90;)。常数的值在定义后不能被修改。

3. 合法的标识符规则

  • 标识符不能与 C++ 的关键字(如 int, double, const 等在集成开发环境中通常会变色的词语)冲突。
  • 标识符只能包含字母(大写和小写)、数字和下划线 _,且开头只能是字母或下划线,不能以数字开头。
  • 标识符必须先定义后使用。
  • 在同一作用域内,标识符不能重复定义。为了防止逻辑混淆,在不同作用域内也应尽量避免定义同名变量。
  • C++ 严格区分大小写,例如 Variable 和 variable 是两个完全不同的标识符。

4. 字面量后缀(C++11)与数字分隔符(C++14)

  • 整数字面量后缀:对于超出整型范围的字面量,应显式附加后缀。例如 123LL(代表 long long 类型字面量),123ULL(代表 unsigned long long 类型字面量)。
  • 二进制字面量(C++14):允许直接以 0b 或 0B 开头书写二进制常数,如 0b1010 等于十进制的 10。
  • 数字分隔符(C++14):允许在长数字字面量中使用单引号 ' 作为分隔符以提高代码可读性,编译器在编译时会自动忽略单引号。 cpp long long x = 123'456'789'012LL; // 极大地提高了长数字的可读性 int y = 0b1010'1010; // 二进制表示 10101010,等于十进制 170

2.3 数组与字符串

1. 一维数组

  • 定义:int a[10];。该数组在内存中占用了 10 个连续的 int 空间,下标范围为 0 ~ 9。访问第 6 个元素时使用 a[5]。

2. 二维数组

  • 定义:int b[5][3];。该数组共有 $5 \times 3 = 15$ 个元素,在内存中按行连续存放。元素依次为 b[0][0]、b[0][1]、b[0][2]、b[1][0] $\dots$ 直到 b[4][2]。访问具体元素时需要使用两个方括号,如 b[2][1]。更长维度的多维数组定义与此类推。

3. 数组名与元素寻址

  • 数组名本质上是一个常量指针,指向该数组首个元素的内存地址。例如 a 代表 &a[0],b 代表 &b[0][0]。
  • 多维数组的本质是“数组的数组”。因此,二维数组 b 的第一维 b[0] 实际上是包含 3 个一维元素的子数组的数组名,即 b[0] 相当于 &b[0][0]。
  • 数组在内存中是连续排布的。因此可以利用指针偏移量进行寻址运算:a + 3 对应 &a[3],*(b + 1) 对应 b[1][0],*(*(b + 3) + 2) 对应 b[3][2]。

    竞赛建议:在算法竞赛中,应尽量避免复杂的指针算法寻址,直接使用下标(如 b[3][2])可以使代码更清晰,降低调试难度。

4. 字符串

  • 在 C 风格语法中,字符串在物理上表现为 char 类型的数组。
  • 零终止符 \0:字符串的最后一个字符必须是空字符 '\0'(ASCII 值为 0)。如果缺少 \0,在使用标准输出或字符串处理函数时,程序会因为无法定位边界而发生内存越界和未定义行为。
  • 整体操作限制:原生数组(包括 char 字符数组)不能直接进行整体赋值或比较(例如不能写 a = b 或 if (a == b))。若需要进行这些操作,应使用 <cstring> 中的 memcpy 和 memcmp 库函数。如果是字符串,则应使用 strcpy 和 strcmp。

数组越界警告: * C++ 数组下标必须从 0 开始。int a[10] 的最大合法索引是 a[9],不存在 a[10]。 * C++ 在运行期不进行任何数组越界检查。如果发生数组越界访问,可能会静默修改相邻变量的值,或导致程序发生不可预知的崩溃。


2.4 指针

指针是用于保存另一个变量内存地址的特殊变量。

1. 基本操作符

  • 取地址运算符 &:返回一个变量在内存中的起始地址。由于数组名本身在表达式中会自动转换为指向首元素的指针,因此对数组名无须使用 & 运算符。
  • 指针运算符 *:作用于指针变量,用于获取或修改该地址所指向的内存空间中的实际值。

2. 定义与初始化

  • 定义语法:int *p;。在同一行声明多个指针时,每个变量名前都必须加上 * 标识(例如 int *p1, *p2;)。
  • 野指针警告:如果指针 p 在定义后没有被显式初始化,它会指向一个随机的未知内存空间。对这种“野指针”进行解引用写入操作,极易引发程序崩溃。在不用时应将其初始化为 nullptr(C++11 标准)或 NULL。
指针操作实例:
int a = 0, b = 1;
int c[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int *p;         // 定义一个整型指针变量

p = &a;         // 让 p 指向变量 a 的地址
(*p) = 3;       // 相当于对 a 赋值,a = 3
(*p) = b;       // 相当于 a = b,此时 a 的值变为 1
// p = b;       // 非法:不能把 int 类型的值直接赋给指针变量 int*
p = &b;         // 让 p 重新指向 b 的地址,此后 p 与 a 失去关联
p = c + 6;      // 指针偏移,使 p 指向数组元素 c[6]
cout << *p;     // 输出 p 指向的变量值,即输出 c[6] 的值(7)
p++;            // 指针向后移动一个整型单位(4字节),此时指向 c[7]
p = nullptr;    // 将 p 置为空指针,不指向任何有效地址
// cout << *p;  // 警告:对空指针进行解引用会导致程序崩溃!

2.5 引用

引用是已存在变量的别名,在语法上充当了原变量的代言人。

int &p = f[0][2]; // p 成为 f[0][2] 的别名

此后,无论是修改 p 的值还是修改 f[0][2] 的值,操作的都是同一个内存单元。

引用的优势与应用:

  • 简化代码与提高可读性:对于在程序中需要频繁读写的多维数组元素(如 f[0][2]),使用引用可以避免冗长的下标书写。
  • 函数参数传递:在函数形参中使用引用类型(如 void update(int &x)),可以在函数内部直接修改外部实参的值,而无须像指针那样进行繁琐的取地址与解引用操作。

引用与指针的区别:

  1. 初始化要求:引用在创建时必须被立即初始化,且必须绑定到一个合法的内存单元上;而指针可以先定义,后续再进行赋值。
  2. 绑定关系唯一性:引用一旦初始化完成,它与原变量的绑定关系就不可更改,对其重新赋值只是改变原变量的值;而指针在生命周期内可以随时指向不同的内存地址。

2.6 结构体

结构体(struct)用于将多个不同类型的数据项组合成一个有机的整体。

struct pack {
    int value;  // 成员变量:价值
    int weight; // 成员变量:重量
};
  • 定义变量:在 C++ 中定义结构体变量时,可直接写成 pack p;(无须像 C 语言那样强制补写关键字 struct)。
  • 访问成员:
    • 通过结构体对象本身访问:使用成员访问符 .(例如 p.value)。
    • 通过结构体指针访问:使用指向符 ->(例如 (&p)->value)。
  • 面向对象特性:在 C++ 中,结构体表现出与类(class)几乎一致的特性。结构体内部可以拥有自己的构造函数、成员函数,并且可以进行运算符重载(这在自定义 sort 的比较规则时极其有用)。
  • 递归定义限制:结构体内部不允许包含其自身类型的成员变量(因为这会导致编译器无法计算其空间大小),但允许包含指向其自身类型的指针成员(如 pack *next;,这在链表结构中非常经典)。

2.7 课后推荐练习题

为了加深对数据范围、数组、结构体以及引用指针等基础知识的理解,建议在平台中练习以下精选题目:

  • 题目编号:59 —— 类型转换与溢出
    • 考点提示:本题中计算阶乘的值会随着输入迅速膨胀。通过此题,您可以深刻理解 int 与 long long 的数值范围限制,并练习如何避免算术溢出。
  • 题目编号:95 —— 交换数值
    • 考点提示:编写一个交换函数。要求利用引用传递(&)作为函数参数,从而实现在函数体内部修改外部变量的值,掌握引用的核心用法。
  • 题目编号:136 —— 成绩排序
    • 考点提示:本题需要存储每个学生的学号和多科成绩。非常适合练习利用 struct 结构体打包多属性数据,并编写自定义排序函数。
  • 题目编号:237 —— 寻找最小值
    • 考点提示:基本的一维数组练习题。通过数组存储输入序列,并使用循环遍历整个数组来寻找最小值及其在数组中的下标。
  • 题目编号:245 —— 二维数组模拟
    • 考点提示:经典的二维数组生成与填充题。需要定义合适大小的二维数组,并通过嵌套循环按照“回”字形规律进行逻辑判断和填充。
  • 题目编号:1536 —— 删除链表节点
    • 考点提示:指针与链表操作。给定链表中某个节点的指针,要求在不遍历链表的情况下完成对该节点的删除,深刻体会指针在动态数据结构中的精妙应用。

—— 本文来自火龙信奥(义乌睿码科技):义乌青少年信息学奥赛与编程教育平台,专注 CSP-J/S、NOIP、GESP 竞赛培训,线上线下融合教学,助力编程升学。网址:hlcoding.com

关于火龙

  • 关于我们
  • 学员获奖
  • 预约试听
  • ACM课程
  • CSP课程
  • 学习指南

帮助中心

  • 用户协议
  • 打字练习
  • 在线画图
  • DevC++下载
  • CSP报名
  • GESP官网

推荐课程

  • C++零基础入门(可试看)
  • C++进阶提升
  • GESP考级辅导
  • GESP打卡
  • CSP-J/S打卡

公众号

火龙信奥公众号二维码

地址:义乌市北门街188号新天地商厦二楼2F 邮箱:wdlok305@126.com

© 2017-2026 义乌市睿码科技有限公司版权所有 浙ICP备2021013995号

火龙信奥
请输入登录信息


请完成安全验证
验证码底图 滑块
向右拖动滑块完成验证
请输入用户名 / 绑定的手机号码



请输入注册信息(手机号验证码注册)





验证码5分钟有效,60秒内不可重复获取,每日最多3次

微信登录

微信登录二维码

正在生成二维码...

账号已过期,请续期。
去续期

绑定手机号

📱

为了更好地保护您的账号安全,享受完整的平台服务

请您尽快绑定手机号码