第3章 使用变量和常量
变量让程序员能够将数据临时存储一段时间,而常量让程序员能够定义不允许修改的东西。
在本章中,您将学习:
• 如何使用C++11关键字auto和constexpr;
• 如何声明和定义变量与常量;
• 如何给变量赋值以及操纵这些值;
• 如何将变量的值显示到屏幕上。
3.1 什么是变量
在探索编程语言为何需要使用变量前,先来看看计算机的组成及其工作原理。
所有计算机、智能手机及其他可编程设备都包含微处理器和一定数量的临时存储空间,这种临时存储器被称为随机存取存储器(RAM)。另外,很多设备还让您能够将数据永久性地存储到硬盘等存储设备中。微处理器负责执行应用程序,在此过程中,它从 RAM 中获取要执行的应用程序以及相关联的数据,这包括显示到屏幕上的数据以及用户输入的数据。
RAM类似于宿舍里成排存物柜的存储区域,每个存物柜都有编号,即地址。要访问特定的内存单元,如内存单元578,需要使用指令要求处理器从这里获取值或将值写入到这里。
下面的示例将帮助您明白变量是什么。假设您要编写一个程序,它将用户提供的两个数字相乘。用户被要求依次提供被乘数和乘数,而您需要存储它们,以便以后将它们相乘。您还可能需要存储乘法运算的结果,供以后使用,这取决于您要使用这个结果做什么。如果显式地指定用于存储这些数字的内存单元的地址(如578),既慢又容易出错,因为这需要避免不小心覆盖原有的数据,以后还需避免覆盖您存储的数据。
使用 C++等语言编程时,您只需定义用于存储这些值的变量。定义变量非常简单,其语法如下:
或
变量类型向编译器指出了变量可存储的数据的性质,编译器将为变量预留必要的空间。变量名由程序员选择,它替代了变量值在内存中的存储地址,但更友好。除非给变量赋初值,否则无法确保相应内存单元的内容是什么,这对程序可能不利。因此,初始化虽然是可选的,但对变量初始化通常是一个不错的编程习惯。程序清单 3.1 将用户提供的两个数字相乘,演示了如何在程序中声明、初始化和使用变量。
程序清单3.1 使用变量存储数字及其相乘的结果
输出:
分析:
这个应用程序要求用户输入两个数字,将它们相乘并显示结果。应用程序要使用用户输入的数字,必须将其存储到内存中。第9和13行声明了变量FirstNumber和SecondNumber,用于临时存储用户输入的整数。第10和14行使用std::cin获取用户输入,并将其存储到两个整型变量中。第21行的cout语句用于将结果显示到控制台。
下面进一步分析其中的一个变量声明:
这行代码声明了一个变量,其类型为int(表示整型),名称为FirstNumber,并将该变量的初始值设置为零。
使用汇编语言编程时,需要显式地要求处理器将被乘数存储到特定的位置,如 578,而 C++让您能够使用更友好的概念(如变量 FirstNumber)来访问内存单元,以检索和存储数据。将变量 FirstNumber关联到内存单元的工作由编译器负责,它还负责为您完成相关的簿记工作(book Keeping)。
这样,程序员就可使用对人类友好的名称,把将变量关联到地址以及创建 RAM 访问指令的工作留给编译器去做。
为编写易于理解和维护的代码,给变量指定合适的名称很重要。
变量名可包含数字和字母,但不能以数字打头。变量名不能包含空格和算术运算符(+、-等)。要拉长变量名,可使用下划线。
另外,变量名不能是保留的关键字,例如,将变量命名为return将导致程序无法通过编译。
在程序清单3.1中,变量FirstNumber、SecondNumber和MultiplicationResult属于同一种类型——都是整型,但在三行中分别声明。如果您愿意,可简化这三个变量的声明,在一行代码中完成,如下所示:
正如您看到的,在C++中,可同时声明多个类型相同的变量,还可在函数开头声明变量。然而,需要时再声明变量通常是更好的选择,因为这让代码更容易理解——变量的声明离使用它的地方不远时,别人更容易了解变量的类型。
存储在变量中的数据被存储在内存中。计算机关闭或应用程序终止时,这样的数据将丢失,除非程序员显式地将其存储到硬盘等永久性存储介质中。
将数据存储到磁盘文件将在第27章讨论。
常规变量的作用域很明确,只能在作用域域内使用它们,如果您在作用域外使用它们,编译器将无法识别,导致程序无法通过编译。在作用域外面,变量是未定义的实体,编译器对其一无所知。
为让读者更好地理解变量的作用域,程序清单3.2将程序清单3.1所示的程序重新组织成了一个函数——MultiplyNumbers(),它将两个数字相乘并返回结果。
程序清单3.2 使用变量存储数字及其相乘的结果
输出:
分析:
程序清单 3.2 的功能与程序清单 3.1 相同,输出也相同。唯一的差别在于,将工作交给了函数MultiplyNumbers()去完成,并在 main()中调用它。请注意,不能在函数 MultiplyNumbers()外面使用变量FirstNumber和SecondNumber。如果您取消对main()中第28或29行的注释,将出现编译错误,而错误很可能是标识符未声明(undeclared identifier)。
这是因为变量FirstNumber和SecondNumber的作用域为局部,被限定在声明它的函数内,这里为MultiplyNumbers()。局部变量只能在这样的范围内使用,即从声明它的语句开始到当前函数的末尾。标识函数结束的花括号(})也限定了函数内部声明的变量的作用域。函数结束后,将销毁所有局部变量,并归还它们占用的内存。
编译时,在 MultiplyNumbers()内部声明的变量在该函数结束时不再存在,如果在 main()中使用它们,程序将无法通过编译,因为在main()中这些变量未声明。
如果您在main()声明另一组同名变量,就不能指望它们的值与您在MultiplyNumbers()中赋给同名变量的值相同。
编译器将 main()中声明的变量视为独立的实体,即便它们与另一个函数中声明的变量同名,因为这些变量的作用域不同。
在程序清单3.2中,如果变量是在函数MultiplyNumbers()外部而不是内部声明的,则在函数main()和 MultiplyNumbers()中都可使用它们。程序清单 3.3 演示了全局变量,它们是程序中作用域最大的变量。
程序清单3.3 使用全局变量
输出:
分析:
程序清单 3.3 在两个函数中显示了乘法运算的结果,而变量 FirstNumber、SecondNumber 和MultiplicationResult 都不是在这两个函数内部声明的。这些变量为全局变量,因为声明它们的第 5~7行不在任何函数内部。注意到第23和36行使用了这些变量并显示它们的值。尤其要注意的是,虽然MultiplicationResult的值是在MultiplyNumbers()中指定的,但仍可在main()中使用它。
不分青红皂白地使用全局变量通常是一种糟糕的编程习惯。
全局变量可在任何函数中赋值,因此其值可能出乎意料,在不同函数模块由小组中的不同程序员编写时尤其如此。
要像程序清单 3.3 那样在 main()中获取乘法运算的结果,一种更妥善的方式是,让MultiplyNumbers()将结果返回给main()。
3.2 编译器支持的常见C++变量类型
在本书前面的大多数示例中,定义的变量类型都是 int(整型),然而 C++编译器支持很多基本变量类型,可供程序员选择。选择正确的变量类型犹如根据要做的工作选择正确的工具一样重要!十字螺钉与普通螺帽不匹,同样,无符号整型变量也不能用于存储负值!表 3.1 列出了各种变量类型及其可存储的数据的特征。要编写高效而可靠的C++程序,这些信息非常重要。
表3.1 变量类型
接下来的几小节将更详细地介绍一些重要的类型。
C++提供了一种专为存储布尔值true和false而创建的类型,其中true和false都是保留的C++关键字。对于取值为ON或OFF、有或没有、可用或不可用等设置和标记,非常适合使用这种类型的变量来存储。
下面是一个声明并初始化布尔变量的例子:
下面是一个结果为布尔值的表达式:
char变量用于存储单个字符,下面是一个声明示例:
请注意,表示内存空间容量大小的单位是位和字节。位的取值为 0 或 1,而字节可以包含字符的数字表示。因此像前面的示例那样使用字符数据时,编译器将把字符转换为可存储到内存中的数字表示。美国信息交换标准(ASCII)对拉丁字符A~Z、a~z、数字0~9、一些特殊键击(如DEL)和一些特殊字符(如空格)的数字表示进行了标准化。
如果您查看附录E的ASCII码表,赋给变量UserInput的字符Y的ASCII码为89,因此编译器将在分配给UserInput的内存空间中存储89。
符号表示正或负。您在计算机中使用的所有数字都以位和字节的方式存储在内存中。1 字节的内存单元包含8位,每位都要么为0,要么为1(即存储这两个值之一),因此1字节的内存单元可以有28(即256)个不同的取值。同样,16位的内存单元可以有216(65536)个不同的取值。
如果这些取值是无符号的(即为正数),则1个字节的可能取值为 0~255,而2 个字节的可能取值为 0~65535。从表 3.1可知,类型 unsigned short的取值范围为 0~65535,因为它占用 16位内存。因此,使用位和字节表示正值非常容易,如图3.1所示。
在这种空间中如何表示负数呢?一种方式是将 1 位用作符号位,指出其他位包含的值是正还是负,如图3.2所示。符号位必须是最高有效位(most-sigificant-bit,MSB),因为最低有效位(least-significant-bit)需要用于表示小于2的数字。当MSB包含符号信息时,假定0表示正,1表示负,而其他位包含绝对值。
图3.1 占用16位内存的unsignedshort变量
图3.2 占用16位内存的signedshort变量
因此,占用 8 位的有符号数的取值范围为−127~127,而占用 16 位的有符号数的取值范围为−32768~32768。如果查看表3.1,将发现类型short的取值范围为−32768~32768。
3.2.4 有符号整型 short、int、long和 long long
这些类型的长度各不相同,因此取值范围也各不相同。int可能是使用得最多的类型,在大多数编译器中,其长度都是32位。应根据变量可能存储的最大值给它指定合适的类型。
声明有符号类型的变量非常简单,如下所示:
3.2.5 无符号整型unsigned short、unsigned int、unsigned long和unsigned long long
不同于相应的有符号类型,无符号整型变量不能包含符号信息,因此,它们的最大取值为相应有符号类型的两倍。
声明无符号类型变量也很简单,如下所示:
如果预期变量的取值不会为负数,就应将其类型声明为无符号的。因此,如果您要存储苹果的数量,不要使用 int变量,而应使用unsigned int变量,后者的最大取值为前者的两倍。
对于银行应用程序中用于存储账户余额的变量,将其类型声明为无符号的就可能不合适。
您可能在学校学过,浮点数就是实数,可以是正,也可以是负,还可以包含小数值。因此,如果要使用C++变量存储pi(22/7)的值,就应将其声明为浮点类型。
声明浮点类型变量的方式与程序清单3.1中声明int变量的方式相同。要声明一个可存储小数值的float变量,可像下面这样做:
要声明双精度浮点数(double)变量,可像下面这样做:
表 3.1列出的数据类型通常被称为 POD(Plain Old Data)。POD还包含聚合数据类型(结构、枚举、共用体和类)。
3.3 使用sizeof确定变量的长度
变量长度指的是:程序员声明变量时,编译器将预留多少内存,用于存储赋给该变量的数据。变量的长度随类型而异,C++提供了一个方便的运算符——sizeof,可用于确定变量的长度(单位为字节)或类型。
sizeof的用法非常简单。要确定int变量的长度,可调用sizeof并给它传递参数int:
程序清单3.4演示了如何获悉各种标准C++变量类型的长度。
程序清单3.4 获悉标准C++变量类型的长度
输出:
分析:
程序清单3.4的输出指出了各种类型的长度(单位为字节),这是针对我使用的平台(编译器、操作系统和硬件)而言的。具体地说,这是在64位系统中以32位模式(使用32位编译器进行编译)运行该程序得到的结果。如果使用64位编译器进行编译,结果可能不同。我之所以使用32位编译器,是因为这样该应用程序在32位和64位系统上都能运行。输出表明,无符号类型和相应的有符号类型的长度相同,唯一的差别在于,后者的MSB包含符号信息。
输出中的长度单位为字节。给对象分配内存时,其长度将是一个重要参数,在程序员动态地给对象分配内存时尤其如此。
C++11
使用auto——编译器的类型推断功能
在有些情况下,根据赋给变量的初值,很容易知道其类型。例如,如果将变量的初值设置成了 true,就可推断其类型为bool。在C++11中,可不显式地指定变量的类型,而使用关键字auto:
这将指定变量Flag的类型的任务留给了编译器。编译器检查赋给变量的初值的性质,再确定将变量声明为什么类型最合适。就这里而言,显然初始值true最适合存储到类型为bool的变量中。因此,编译器认为变量Flag的最佳类型为bool,并在内部将Flag的类型视为bool,程序清单3.5证明了这一点。
程序清单3.5 使用关键字auto依靠编译器的类型推断功能
输出:
分析:
在第 6和 7行声明变量Flag和Number时,没有将其类型分别指定为 bool和 long long,而使用了关键字 auto。这让编译器去决定变量的类型,而编译器将根据初始值来确定合适的类型。接下来,使用sideof来检查编译器选择的类型是否符合预期,从程序清单3.5的输出可知,确实符合预期。
使用auto时必须对变量进行初始化,因为编译器需要根据初始值来确定变量的类型。如果将变量的类型声明为auto,却不对其进行初始化,将出现编译错误。
乍一看,auto 并非什么了不起的功能,但在变量类型非常复杂时,它可让编程容易得多。假设您使用std::vector声明了一个名为MyNumbers的动态整数数组:
要访问或遍历该数组中的元素并显示它们,可使用如下代码:
std::vector和for循环都还没有介绍,因此即便上述代码看起来像天书,也不用担心。其功能如下:对于矢量中的每个元素——从begin()开始,到end()前面的一个元素结束,都使用cout显示其值。第1行代码非常复杂,它声明变量Iterator,并将其初始值设置为begin()返回的值。这个变量的类型为vector<int>::const_iterator,这对程序员来说,学习和书写起来都非常复杂。程序员无需熟记这一点,而可依赖于begin()的返回类型,将上述for循环简化为如下所示:
注意到现在第1行有多紧凑。编译器检查Iterator的初始值——begin()返回的值,并将该变量的类型设置为该返回值的类型。这简化了C++编码工作,在大量使用模板时尤其如此。
3.4 使用typedef替换变量类型
C++允许您将变量类型替换为您认为方便的名称,为此可使用关键字 typedef。在下面的示例中,程序员想给 unsigned int指定一个更具描述性的名称——STRICTLY_POSITIVE_INTEGER:
编译时,第 1行告诉编译器,STRICTLY_POSITIVE_INTEGER就是 unsigned int。以后编译器再遇到已定义的类型 STRICTLY_POSITIVE_INTEGER时,就会将它替换为 unsigned int并继续编译。
涉及语法烦琐的复杂类型,如使用模板的类型时,typedef(类型替换)特别方便。
3.5 什么是常量
假设您要编写一个程序,计算圆的面积和周长,其公式如下:
在这些公式中,Pi 为常量,其值为 22/7。您希望在整个程序中,Pi 的值都不变;您也不希望无意间将错误的值赋给Pi,如错误地复制/粘贴或查找/替换。C++让您能够将Pi定义为声明后就不能修改的常量,换句话说,定义常量后,就不能修改它的值。在 C++中,给常量赋值会导致编译错误。
因此,在 C++中,常量类似于变量,只是不能修改。与变量一样,常量也占用内存空间,并使用名称标识为其预留的空间的地址,但不能覆盖该空间的内容。在C++中,常量可以是:
• 字面常量;
• 使用关键字const声明的常量;
• 使用关键字constexpr声明的常量表达式(C++11新增的);
• 使用关键字enum声明的枚举常量;
• 使用#define定义的常量(已摒弃,不推荐)。
再来看一下程序清单3.1——将两个数相乘的简单程序。其中声明了一个名为FirstNumber的int变量:
将这个int变量的初始值设置成了零。这个零是代码的一部分,将编译到应用程序中,且不可修改,因此称为字面常量。字面常量可以是任何类型:布尔型、整型、字符串等。在您编写的第一个 C++程序(程序清单 1.1)中,您使用了如下代码显示Hello World:
Hello World就是一个字符串字面常量。
从实用和编程的角度看,最重要的C++常量类型是在变量类型前使用关键字const声明的。通用的声明方式类似于下面这样:
来看一个简单的应用程序,它显示常量Pi的值,如程序清单3.6所示。
程序清单3.6 声明一个名为Pi的常量
输出:
分析:
请注意常量Pi的声明(第7行)。这里使用了关键字const来告诉编译器,Pi是一个类型为double的常量。第11行试图给一个常量赋值,如果取消对该行的注释,将出现编译错误,指出不能给常量赋值。因此,常量是一种确保某些数据不能修改的强大方式。
如果变量的值不应改变,就应将其声明为常量,这是一种良好的编程习惯。通过使用关键字const,程序员可确保数据不变,避免应用程序无意间修改该常量。
在多位程序员合作开发时,这特别有用。
声明在编译期间长度固定的静态数组时,常量很有用。程序清单 4.2 提供了一个示例,演示了如何使用int常量指定数组长度。
C++11
在C++11之前,C++就支持常量表达式的概念,只是没有关键字constexpr。在程序清单3.5中, 22.0/7 是一个常量表达式,C++11 之前的编译器也支持它。然而,C++11 之前的编译器不允许定义在编译阶段计算的函数。在C++11中,可以编写下面这样的代码:
还可将GetPi()与另一个常量一起使用,如下所示:
乍一看,const和constexpr之间的差别很小,但从编译器和应用程序的角度看,关键字Constexpy提供了优化应用程序的可能性。对于第二条语句,如果使用const,将在运行阶段执行计算,但使用遵守C++11的编译器时,将在编译阶段计算该表达式的值,这提高了应用程序的运行速度。
编写本书时,Microsoft Visual C++学习版还不支持关键字 constexpr,但GNU的g++编译器支持。
在有些情况下,变量只能有一组特定的取值。例如,彩虹不能包含青绿色,指南针的方位不能为“左”。在这些情况下,需要定义这样一种变量,即其可能取值由您指定。为此,可使用关键字enum来声明枚举常量。
例如,下面的枚举常量包含彩虹的颜色:
下面的枚举常量包含基本方位:
可使用枚举常量来指定变量的类型,这样声明的变量只能取指定的值。因此,如果要声明一个变量,用于存储彩虹的颜色,可以像下面这样做:
上述代码声明了常量MyWorldColor,其类型为RainbowColors。这个枚举常量只能取RainbowColors中指定的值,而不能取其他值。
声明枚举常量时,编译器将把枚举值(Voilet 等)转换为整数,每个枚举值都比前一个大1。您可以指定起始值,如果没有指定,编译器认为起始值为0,因此North的值为0。如果愿意,还可通过初始化显式地给每个枚举量指定值。
程序清单3.7演示了如何使用枚举常量来存储4个基本方位,并对第一个方位进行了初始化。
程序清单3.7 使用枚举值指示基本方位
输出:
分析:
这里将4个基本方位定义为枚举常量,并将第一个常量(North)的值设置为25(第6行),这自动将随后的常量分别设置为26、27和28,如输出所示。第20行创建了一个类型为CardinalDirections的变量,并将其初始值设置为South。第21行显示该变量时,编译器显示的是South对应的整数值——26。
您可能该看看程序清单6.4和程序清单6.5,它们使用enum列举一个星期的各天,并使用条件处理指出用户选择的那天是根据哪颗星星命名的。
首先也是最重要的是,编写新程序时,不要使用这种常量。这里介绍使用#define定义常量,只是为了帮助您理解一些旧程序,它们使用下面的语法定义常量:
这是一个预处理器宏,让预处理器将随后出现的所有 Pi 都替换为 3.14286。预处理器将进行文本替换,而不是智能替换。编译器既不知道也不关心常量的类型。
使用#define定义常量的做法已被摒弃,因此不应采用这种做法。
3.6 给变量和常量命名
给变量命名的方式有很多,还有很多不同的约定。有些程序员喜欢在变量名开头用几个字符指出变量的类型,例如:
其中b是程序员添加的前缀,指出变量的类型为bool。这种表示法称为匈牙利表示法,最初由微软发明并倡议。然而,C++是一种强类型安全语言,编译器根据类型定义而不是名称前缀来获悉变量的类型。因此,当前强烈推荐程序员不要采用匈牙利表示法。变量名必须易于理解,哪怕这会导致变量名更长些。在这个示例中,假定该布尔变量表示车前灯开关,下面的变量名将更好些:
这两个变量名都比下面的变量名更好:
应不惜一切代价避免使用非描述性变量名。
3.7 不能用作常量或变量名的关键字
有些单词被C++保留,不能用作变量名。对C++编译器来说,这些关键字有特殊含义。关键字包括if、while、for、main等。表3.2和附录B列出了C++定义的关键字。您的编译器可能还保留了其他单词,有关完整的关键字列表,请参阅编译器手册。
表3.2 C++关键字
续表
3.8 总结
在本章中,您了解到内存用于临时存储变量和常量的值。您了解到变量的长度取决于其类型,并可使用运算符sizeof来确定。您学习了各种变量类型,如bool、int等,知道它们用于存储不同类型的数据。选择正确的变量类型至关重要,如果选择的类型太短,可能导致回绕(wrapping)错误或溢出。您学习了C++11新增的关键字auto,它让编译器根据变量的初始值确定其类型。
您还学习了各种常量,其中最重要的是使用关键字const和enum定义的常量。
3.9 问与答
问:既然可以使用常规变量代替常量,为何还要定义常量?
答:通过声明常量(尤其是使用关键字const时),可告诉编译器,其值是固定的,不允许修改。这样,编译器将确保不给常量赋值,即便另一位程序员接手了您的工作,不小心试图覆盖常量的值。因此,在知道变量的值不应改变时,应将其声明为常量,这是一个不错的编程习惯,可提高应用程序的质量。
问:为何应给变量赋初值?
答:如果不初始化,就无法知道变量包含的初始值。在这种情况下,初始值将是给变量预留的内存单元的内容。下面的语句使得创建变量MyFavoriteNumber后,就将指定的初始值0写入到为该变量预留的内存单元:
有时候,需要根据变量的值(通常是核实它不为零)做条件处理,如果不对变量进行初始化,这样的逻辑将不可靠,因为未赋值或初始化的变量包含的内容是随机的。
问:C++为何提供变量类型 short int、int和 long int?为何不始终使用取值范围最大的变量类型呢?
答:C++用于编写各种应用程序,其中很多运行在计算能力和内存资源都很有限的设备上。例如,老式手机的计算能力和内存都有限。在这种情况下,程序员通过选择合适的变量类型,可节省内存并提高速度。如果编写的是常规台式机或高端智能手机程序,选择不同整型带来的性能提升或内存节省将很小,有时甚至可以忽略不计。
问:为何不应频繁地使用全局变量?全局变量在应用程序的任何地方都可用,可避免在函数之间传递值,从而节省一些时间,这种说法对吗?
答:可在应用程序的任何地方读取全局变量的值以及给它赋值,这是个问题,因为在应用程序的任何地方都可修改它们。假设您与其他几位程序员合作开发一个项目,并将变量声明为全局的。如果队友不小心在其代码中修改这些变量——即便是在另一个.CPP文件中,都将影响代码的可靠性。因此,为确保代码的稳定性,不要为节省几秒钟甚至几分钟而不分青红皂白地使用全局变量。
问:在C++中,可以声明无符号整型变量,其取值只能是零或正整数。如果一个unsigned int变量的值为零,将其减1的结果如何?
答:这将导致环绕。如果将值为零的 unsigned int变量减 1,它将环绕到可存储的最大值!从表 3.1可知,unsigned short变量的取值范围为 0~65535。为演示这一点,下面的代码声明了一个 unsigned short变量,将其初始化为0,并减1:
这不是 unsigned short的问题,而是使用它的方式有问题;在变量的取值可能为负时,就不应将其类型指定为 unsigned int、unsigned short或 unsigned long。如果MyShortInt被用于指定动态分配的字节数,允许在它为零时减1,将导致分配64KB 的内存!更糟糕的是,如果在访问内存单元是将MyShortInt用作索引,很可能访问外部内存单元,进而导致应用程序崩溃!
3.10 作业
作业包括测验和练习,前者帮助读者加深对所学知识的理解,后者提供了使用新学知识的机会。请尽量先完成测验和练习题,然后再对照附录 D 的答案。在继续学习下一章前,请务必弄懂这些答案。
1.有符号整型和无符号整型有何不同?
2.为何不应使用#define来声明常量?
3.为何要对变量进行初始化?
4.给定如下枚举类型,QUEEN的值是多少?
5.下述变量名有何问题?
1.修改测验题4中的枚举类型YOURCARDS,让QUEEN的值为45。
2.编写一个程序,证明 unsigned int和 int变量的长度相同,且它们都比 long变量短。
3.编写一个程序,让用户输入圆的半径,并计算其面积和周长。
4.在练习3中,如果将面积和圆周存储在int变量中,输出将有何不同?
5.查错:下面的语句有何错误?