Menu

C++ 变量:声明、初始化与作用域详解

C++ 中的变量是怎么工作的:用类型声明变量、赋值与初始化的区别、花括号初始化、命名规则,以及决定变量在哪里诞生和销毁的作用域。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

什么是变量

变量是一块有名字的内存,用来保存一个值。在 C++ 中,每个变量都有一个固定的类型,在声明时选定,之后永远不会改变。这就是 C++ 静态类型的含义:编译器在程序运行之前就知道每个变量的类型,并拒绝编译那些存入错误类型值的代码。

一个声明由三部分组成:类型、名字,以及(几乎总会有的)初始值。

int age = 30; 可以读作“创建一个名为 age 的 int,并把 30 放进去”。分号结束这条语句,正如注释那一页所说,每条语句都是这样。注意 isActive 打印出来是 1:bool 默认显示为 1/0,数据类型那一页会在此基础上展开。

初始化与赋值

这两者看起来相似,却是不同的操作,而它们的区别是本页最重要的概念之一。

初始化是在声明时给变量它的第一个值。赋值是改变一个已经存在的变量的值。

你可以先声明而不初始化,稍后再赋值,但要小心:

这段代码能正常工作,是因为我们在读取 score 之前给它赋了值。危险的写法是先读取它,下一节就讲这个。

未初始化变量的陷阱

这是 C++ 的经典陷阱,在很多其他语言里并不存在。读取一个已声明却从未赋值的局部变量,是未定义行为:变量里装着那块内存中碰巧留下的任意字节。

int score;             // not initialized
std::cout << score;    // undefined behavior - prints garbage, or "works", or crashes

编译器会照样把它编译出来。它可能打印 0,可能打印 32766,每次运行或每台机器上的表现都可能不同,这让这类 bug 极难追查。有两种防御方法:

  • 始终在声明时初始化。 int score = 0; 没有任何成本,却能彻底消除这个问题。
  • 打开警告。 用 -Wall -Wextra 编译,编译器会在很多未初始化读取造成麻烦之前就把它们标出来。

对每个局部变量都优先采用第一种方法:在创建它的那一刻就给它一个合理的初始值。

初始化的写法:=、() 和 {}

C++ 提供了几种写初始值的方式。它们大多做的是同一件事,但花括号初始化多了一项值得了解的安全特性。

选择 {} 的理由是它会拒绝窄化转换,也就是那些会悄悄丢失数据的赋值。用 = 时,小数值会被悄悄截断;用 {} 时,编译器会阻止你:

int x = 3.9;    // compiles - x silently becomes 3 (the .9 is thrown away)
int y{3.9};     // compile error - narrowing from double to int is not allowed

在编译时就发现这种问题,正是你想要的。现代 C++ 的一个常见习惯是默认使用 {} 来获得这项额外检查,只有在拷贝初始化读起来更自然时才退回到 =。

命名规则与约定

C++ 强制规定了几条硬性规则,大家再在此之上加一些约定。规则是:名字可以包含字母、数字和 _;不能以数字开头;不能是保留关键字(比如 int 或 return);而且区分大小写(age 和 Age 是两个不同的变量)。避免使用以下划线加大写字母开头、或者包含两个连续下划线的名字,它们是留给编译器和标准库实现的。

大多数 C++ 代码遵循的约定:

  • 变量使用 snake_case 或 camelCase,选一种并保持一致:item_count 或 itemCount。
  • 名字应当描述它的值:用 count 而不是 c;用 user_email 而不是 x。

清晰的名字不是装饰,而是将来的你读懂代码的方式。total_price = item_count * price_per_item 一看就懂,而 t = c * p 永远做不到。

变量的作用域

变量只存在于声明它的那个代码块({ ... })之内,在右花括号处被销毁。这就是它的作用域。在循环或 if 块里声明的变量,在块外是看不见的:

i 和 square 都属于这个循环,循环结束时就消失了;total 是在外层块中声明的,所以它会保留下来。内层块还可以遮蔽外层块的名字:一个同名的新变量会暂时隐藏外层的那个,直到内层块结束。这经常让人困惑,所以避免在嵌套的作用域中重复使用同一个名字。

实践中的要点:在需要某个变量的最小代码块里声明它,并在那里初始化。作用域越小,争夺你注意力的名字就越少,在远离赋值位置的地方读取值的机会也越少。

下一步:数据类型

本页的每个变量都以一个类型开头:int、double、string、bool。下一页会详细讲解 C++ 的数据类型:基本类型及其大小、有符号与无符号整数、float 与 double、char,以及如何为任务选择合适的类型。

常见问题

C++ 中如何声明变量?

先写类型,再写名字,然后可以选择给它一个值:int age = 30;。类型(int)在变量的整个生命周期内都是固定的;名字(age)是你引用它的方式。你也可以不给值直接声明,比如 int age;,但对局部变量来说,这会让它在你赋值之前一直装着垃圾值,所以请始终在声明时就初始化。

C++ 中初始化和赋值有什么区别?

初始化是在声明时给变量它的第一个值:int x = 5; 或 int x{5};。赋值是改变一个已经存在的变量的值:x = 7;。这个区别很重要,因为读取一个已声明但从未初始化的局部变量,是未定义行为。

C++ 中使用未初始化的变量会怎样?

读取未初始化的局部变量的值是未定义行为:变量里装着那块内存中原有的任意字节,所以程序可能打印一个随机数、碰巧能运行,也可能崩溃。编译器不会阻止你(不过很多编译器在 -Wall 下会警告),解决办法是声明局部变量时总是给它一个值。

Coddy 编程语言插图

和 Coddy 一起学编程

开始