在C++中,不仅仅是变量才有名字,枚举(enumeration),函数(function),类(class),模板(template)等物件都有名字。在使用任何一个名字之前,必须要先对该名字表示的物件进行声明(declaration)或者定义(definition)。声明和定义这两个词的经常容易被混淆,有时用一个名字只需要该名字的声明就可以了,有时用一个名字需要该名字的定义。并且不同类型的物件(变量,枚举,函数,等等)的声明和定义的概念也不太相同,本系列文章中会对他们进行明确的区分。C++中的定义和声明的区别是符合我们的对事物理解的经验和直觉的,所以绝大多数情况下并不需要特别记忆。
对于变量来说,声明一个变量即是相当于告诉编译器两个信息:变量名和其相对应的变量类型。定义一个变量即使相当于告诉编译器变量名和变量类型,同时让编译器在内存中预留该变量的存储空间(因为预留空间是编译器自动完成的,所以编译器自然也自动知道了该变量的首地址)。可以看到定义一个变量得同时让这个变量在内存中也真正存在了,而声明一个变量名却没有这个要求。变量的定义也是一个声明(简单来说,就是声明再加上在内存中预留空间),但是变量的声明不一定是定义。
我们可以用下面的语法来定义(也是声明)一个变量。
变量类型说明符 变量名1, 变量名2, ... , 变量名3;
其中变量类型说明符的作用是告诉编译器该变量的类型。我们上节提到过的基本数据类型的变量类型说明符如下表,
基本数据类型 | 变量类型说明符 |
char | char |
unsigned char | unsigned char |
signed char | signed char |
char16_t | char16_t |
char32_t | char32_t |
wchar_t | wchar_t |
unsigned short int | unsigned short, unsigned short int |
short int | signed short, signed short int, short, short int |
unsigned int | unsigned int, unsigned |
int | signed int, signed, int |
unsigned long int | unsigned long int, unsigned long |
long int | signed long int, signed long, long int, long |
unsigned long long int | unsigned long long int, unsigned long long |
long long int | signed long long int, signed long long, long long int, long long |
bool | bool |
float | float |
double | double |
long double | long double |
要注意的是,整型可能对应多种类型说明符,他们之间是完全等价的。主要原因是signed和int可以忽略不写。例如short int类型的类型说明符就是signed short int,int忽略不写就变成signed short,signed忽略不写就变成short int,signed和int都忽略不写就变成short。下面的程序定义了(也是声明)6个int类型的变量,变量名为var1,var2,...,var6。
int var1;
signed var2;
signed int var3;
int var4, var5, var6;
变量名(适用于其他的名字,包括函数名,类名,模板名等)是我们自己取的,但要注意有以下规则。第一,变量名的首字母必须为26个英文字母的大小写外加下划线,其他字母必须为26个英文字母的大小写,加下划线以及数字。举例如下。
错误的变量名:2012population, coordinates?
第二,变量名不可以是下列C++中预留的关键词。我们已经见过其中的一些关键词,例如signed,unsigned,int,double等。其他关键词我们会在后面的章节讲解。
alignas continue friend register true
alignof decltype goto reinterpret_cast try
asm default if return& typedef
auto delete inline short typeid
bool double int signed typename
break do long sizeof union
case dynamic_cast mutable static unsigned
catch else namespace static_assert using
char enum new static_cast virtual
char16_t explicit noexcept struct void
char32_t export nullptr switch volatile
class extern operator template wchar_t
const false private this while
constexpr float protected thread_local
const_cast for public throw
第三,C++标准规定,所有以2个下划线开头的名字,以及1个下划线加上1个大写字母开头的名字,例如__range,__Range或者_Range你的程序中都不可以用,因为要为标准库预留。所有以1个下划线开头并且第二个字符并不是下划线,也不是大写字母的名字,例如_range,在你的程序中不可以用在全局名字空间(对变量来说,在全局名字空间的变量也叫做全局变量)。全局变量和全局名字空间我们稍后再提。如果你就是用了这些名字,编译器可能不会报错,但是你的程序的可移植性就变差了,因为换到另外一个编译器,就可能和另外一个编辑器的库实现由名字冲突。
第四,在C++中,名字是大小写敏感的。即大写字母的名字和小写字母的名字是不同的名字。例如,age,Age,AGE是3个不同的名字。
第五,本系列文章并不打算对如何命名作出规范。事实上每一个公司,每一个项目都可能有不同的命名规范。但是最基本的一条原则要牢记:我们给变量取的名字一定要是描述性的(也适用于其他物件的名字)。意思就是说,无论是你自己,还是维护你代码的人,一看到一个名字就要知道这个变量是做什么的。一些名字例如average_salary,maxMathScore都是好名字。不要为了自己方便而取一些类似于a,b,c之类的名字,这样的代码的维护性是很差的。
小知识:其实C++中,不仅仅是26个英文字母的大小写,0-9的数字和下划线可以用在名字上,Unicode也可以用在名字上。例如,你可以,
int \u5e74\u9f84;
如果你的编译器支持当地语言到Unicode的转化,上面的名字也可以这样写,
int 年龄;
如果阅读你的代码的人懂汉语,而你英文实在不是很好,用汉语起名字也未尝不是一种选择,当然前提是你的编译器要支持。