4.1 数据表现形式(常量、变量)
4.1.1 标识符
标识符就是一个对象的名字。用于标识变量、符号常量、函数、数组、类型等,也就是给它们命名;
标识符的命名规则:
1、标识符不能使关键字
2、只能由字母、数字和下划线3种字符组成
3、第1个字符必须为字母或下划线
4、标识符中字母区分大小写
建议:给标识符命名时,最好做到见名知意,让人一看就知道后面的动作是做什么,输出什么结果
C语言关键字:
Auto | break | case | char |
const | continue | default | do |
double | else | enum | extern |
float | for | goto | if |
int | long | register | return |
short | signed | sizeof | static |
struct | switch | typedef | union |
unsigned | void | volatile | while |
#include <iostream>
int main()
{
using namespace std;
//1、标识符不可以是关键字
//int int = 10; //报错
//2、标识符由字母、数字、下划线构成
int abc = 10;
int _abc = 20;
int _123abc = 40;
//3、标识符第一个字符只能是字母或下划线
//int 123abc = 50; //报错
//4、标识符区分大小写
int aaa = 100;
//cout << AAA << endl; //报错,AAA和aaa不是同一个名称
//建议:给变量起名的时候,最好能够做到见名知意
int num1 = 10;
int num2 = 20;
int sum = num1 + num2; //建议用num1、num2、sum表示加法,而不是用a、b、c来表示
cout << sum << endl;
string startDate, endDate; //开始日期、结束日期
string userName,userAddress; //用户姓名、用户地址
int userAge; //用户年龄
return 0;
}
4.1.2 常量
常量的定义:常量是指在程序运行过程中其值不能被改变的量。
C语言中常量可分为直接常量和符号常量。
直接常量是指直接用具体数据表达的一种形式,直接常量又分为整型常量、实型常量、字符常量和字符串常量。符号常量则是指用C语言标识符定义的常量。
4.1.2.1 整型常量
整型常量就是整常数, 10进制数、8进制数和16进制数三种。如: 10、012、0x0A
整型常量是指以文字形式出现的整数,这其中包括正整数、负整数和0。整型常量的表示形式分为十进制、八进制以及十六进制。其可以用后缀字母L或者l表示长整型,后缀U或者u表示无符号型,也可以同时后缀上述两种字母。
4. 1.1.2 实数常量
实数型常量也称为实型常量或浮点型常量。它有两种表现形式,一种就是我们经常用的十进制小数形式,如123.456、23.56等,另一种就是用科学计数法来表示实数,也就是指数形式,如12.34e3。
注意:实型常量默认是double型;3.14、1.2E-4这样的浮点常量都属于double类型。如果希望常量为float类型,需要使用f或F后缀。对于long double类型,可使用l或L后缀。
4.1.2.3 字符常量
字符常量即为用字符构成的常量,由两种表现形式,一种为普通字符,另一种为转义字符。字符常量是括在单引号中。如果常量以 L(仅当大写时)开头,则表示它是一个宽字符常量(例如 L'x'),此时它必须存储在 wchar_t 类型的变量中。否则,它就是一个窄字符常量(例如 'x'),此时它可以存储在 char 类型的简单变量中。
(1)普通字符
如字母、标点符号和数字;最简单的理解就是用单引号括起来的普通字符,事实上这些字符常量在计算机中是以ASCII码表示的,因此可以把它看成变化的数值,如‘a’在计算机中代表的就是97这个数值。
(2)转义字符
转义字符是C语言中表示字符的一种特殊形式。转义字符用反斜杠“\”后面跟一个字符或一个八进制或十六进制数表示。转义字符具有特定的含义,不同于字符原有的意义,故称“转义”字符。转义字符是一种特殊的字符常量。通常使用转义字符表示ASCII码字符集中不可打印的控制字符和特定功能的字符,如用于表示字符常量的单撇号(''),用于表示字符串常量的双撇号("")和反斜杠(\)等。
转义序列 | 含义 |
\\ | \ 字符 |
\' | ' 字符 |
\" | " 字符 |
\? | ? 字符 |
\a | 警报铃声 |
\b | 退格键 |
\f | 换页符 |
\n | 换行符 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\ooo | 一到三位的八进制数 |
\xhh . . . | 一个或多个数字的十六进制数 |
注:应该像处理常规字符那样处理转义字符。也就是说,将它们作为字符常量时,应用单引号括起;将它们放在字符串中时,不要使用单引号。
注意:
字符常量只能包括一个字符,如'AB' 是不合法的。
字符常量区分大小写字母,如'A'和'a'是两个不同的字符常量。
撇号(')是定界符,而不属于字符常量的一部分。如cout<<'a';输出的是一个字母"a",而不是3个字符"'a' "。
4.1.2.4 字符串常量
字符常量的集合版本,将多个字符用双引号括起来,就是字符串常量(string constant)或字符串字面值(string literal)。
例:“asdgsafd”
编译系统会在字符串最后自动加一个'\0'作为字符串结束标志。但'\0'并不是字符串的一部分,它只作为字符串的结束标志。
如:cout<<"abc"<<endl; //输出3个字符abc,而不包括'\0'。
注意: "a"和'a'代表不同的含义,"a"是字符串常量,'a' 是字符常量。前者占两个字节,后者占1个字节。请分析下面的程序片段:
char c; //定义一个字符变量
c='a'; //正确
c="a"; //错误,c只能容纳一个字符
可以使用 \ 做分隔符,把一个很长的字符串常量进行分行。
警告:
在确定存储字符串所需的最短数组时,别忘了将结尾的空字符计算在内。
注意,字符串常量(使用双引号)不能与字符常量(使用单引号)互换。字符常量(如’S’) 是字符串编码的简写表示。在ASCII系统上,'S'只是83的另一种写法,因此,下面的语句将83赋给shirt_ size:
char shirt_ size = 'S' ;
但"S"不是字符常量,它表示的是两个字符(字符S和\0) 组成的字符串。
注:
字符串常量,之所以称之为常量,因为它可以看作是一个没有命名的字符串且为常量,存放在静态数据区。
这里说的静态数据区,是相对于堆、栈等动态数据区而言的。
静态数据区存放的是全局变量和静态变量,从这一点上来说,字符串常量又可以称之为一个无名的静态变量,因为"Hello world!"这个字符串在函数 s1和s2 中都引用了,但在内存中却只有一份拷贝,这与静态变量性质相当神似。
char *c="chenxi";
书上说: "chenxi"这个字符串被当作常量而且被放置在此程序的内存静态区。
那一般的int i=1;
1也是常量,为什么1就不被放置在此程序的内存静态区了呢?
请高手指点!
所有的字符窜常量都被放在静态内存区,因为字符串常量很少需要修改,放在静态内存区会提高效率
例:
char str1[] = "abc";
char str2[] = "abc";
const char str3[] = "abc";
const char str4[] = "abc";
const char *str5 = "abc";
const char *str6 = "abc";
char *str7 = "abc";
char *str8 = "abc";
cout << ( str1 == str2 ) << endl;
cout << ( str3 == str4 ) << endl;
cout << ( str5 == str6 ) << endl;
cout << ( str7 == str8 ) << endl;
结果是:0 0 1 1
str1,str2,str3,str4是数组变量,它们有各自的内存空间;
而str5,str6,str7,str8是指针,它们指向相同的常量区域。
问题的引入:
看看下面的程序的输出:
#include <stdio.h>
char *returnStr()
{
char *p="hello world!";
return p;
}
int main()
{
char *str=NULL;//一定要初始化,好习惯
str=returnStr();
printf("%s\n", str);
return 0;
}
这个没有任何问题,因为"hello world!"是一个字符串常量,存放在静态数据区,
把该字符串常量存放的静态数据区的首地址赋值给了指针,所以returnStr函数退出时,该该字符串常量所在内存不会被回收,故能够通过指针顺利无误的访问。
但是,下面的就有问题:
#include <stdio.h>
char *returnStr()
{
char p[]="hello world!";
return p;
}
int main()
{
char *str=NULL;//一定要初始化,好习惯
str=returnStr();
printf("%s\n", str);
return 0;
}
"hello world!"是一个字符串常量,存放在静态数据区,没错,但是把一个字符串常量赋值给了一个局部变量(char []型数组),该局部变量存放在栈中,这样就有两块内容一样的内存,也就是说char p[]="hello world!"; ”这条语句让“hello world!”这个字符串在内存中有两份拷贝,一份在动态分配的栈中,另一份在静态存储区。这是与前者最本质的区别,当returnStr函数退出时,栈要清空,局部变量的内存也被清空了,所以这时的函数返回的是一个已被释放的内存地址,所以打印出来的是乱码。
如果函数的返回值非要是一个局部变量的地址,那么该局部变量一定要申明为static类型。如下:
#include <stdio.h>
char *returnStr()
{
static char p[]="hello world!";
return p;
}
int main()
{
char *str=NULL;
str=returnStr();
printf("%s\n", str);
return 0;
}
4.1.2.5 布尔常量
布尔常量共有两个,它们都是标准的 C++ 关键字:true 值代表真。false 值代表假。
不应把 true 的值看成 1,把 false 的值看成 0
4.1.2.6 符号常量
用一个标识符代表一个常量,称为符号常量。符号常量在使用之前必须先定义,其般形式为:
#define标识符常量
其中#define也是一条预处理命令(预处理命令都以'#'开头),称为宏定义命令,功能是把该标识符定义为其后的常量值。一经定义,以后在程序中所有出现该标识符的地方均代之以该常量值。习惯上符号常量的标识符用大写字母,变量标识符用小写字母,以示区别。
如:#define PI 3.1415926,当我们需要使用这个数据时,就可以直接调用 PI,使用符号常量可以方便地对程序的修改。
注:
1、末尾不加分号
2、定义的常量没有类型,内存中不存在以符号常量命名的存储单元,因此不能进行类型检查
3、给出的时立即数,不可寻址,预处理时直接进行替换,因此出现几次就有几个副本
4、运行时常量表中没有用 #define 定义的常量,因此系统不会给它分配内存,但它的副本会占据内存
5、它定义的常量是全局的,全程有效
6、当 expression 中有运算时,必须加 (),不然会出逻辑问题。因为 #define 定义的常量是宏,完成的是直接宏替换。
7、用 #define 说明的常量可以用 #undef 指令取消
8、实现的是符号到值的替换,并没有给它分配任何存储空间,定义时符号的值必须确定,不存在初始化的说法
知识点扩充:
在C中,有两种简单的定义常量的方式:
1.使用#define预处理器
2.使用const关键字
const定义的是变量不是常量,只是这个变量的值不允许改变是常变量且带有类型。编译运行的时候起作用存在类型检查。
define定义的是不带类型的常数,只进行简单的字符替换。在预编译的时候起作用,不存在类型检查。
定义符号常量:#define PI 3.1415926 //没有分号
定义常变量 :const float PI=3.1415926;
常变量和文字常量的区别:
常量指值不可改变的量。在C/C++中常量分为两种:文字常量(Literal constant)和常变量(constant variable)。
文字常量和常变量的本质区别:文字常量编译之后存储在代码区,不可寻址,常变量存储在数据区,可寻址。
常变量:前面已经说明概念,它本身的存储区和普通变量的存储也没什么区别,只不过分类不同。
文字常量:文字常量又称为“字面常量”,包括数值常量、字符常量和符号常量。其特点是编译后写在代码区,不可寻址,不可更改,属于指令的一部分。
int& r=5;//编译错误
这条语句出现编译错误,原因是文字常量不可寻址,因而无法为文字常量建立引用。
下面这条语句又是合法的:
const int& r=5;
原因是编译器将一个文字常量转化成常变量的过程。在数据区开辟一个值为5的无名整型常变量,然后将引用r与这个整型常变量进行绑定
数值常量:包括整型常量和实型常量。整型常量就是指常整数,有十进制、八进制、十六进制三种表示形式。实型常量只采用十进制小数形式和指数形式表示,包括单精度浮点数(float)、双精度浮点数(double)和长双精度浮点数(long double)。
int a=4;//4为文字数值常量中的整型常量
float b=4.4//4.4为单精度实型常量
double c=1.4e10//1.4e5表示的值为1.4×10^5,双精度实型常量
字符常量:指单个ASCII码字符,有256个,如’a’和’b’。
符号常量:用标示符代表一个常量,使用之前必须定义。
#define NUM 100//NUM为符号常量,100为整型常量
enum Weekday{SUN, MON, TUES, WED, THU, FRI, SAT};//SUN,MON等均为符号常量
关于static我会自己再在后面去补充。
4.1.3 变量
变量是程序设计语言存储数据的一种方法。为把信息存储在计算机中,程序必须记录3个基本属性:
(1)信息将存储在哪里
(2)要存储什么值
(3)存储何种类型的信息
变量代表一个有名字的、具有特定属性的一个存储单元。
变量用来存放数据,也就是存放变量的值。
在程序运行期间,变量的值是可以改变的。
变量必须先定义,后使用。
4.1.3.1 变量的命名
C++的命名规则:
1、在名称中只能使用字母字符、数字和下划线(_)
2、名称的第一个字符不能是数字
3、区分大写字符与小写字符
4、不能将C++关键字用作名称
5、以两个下划线打头或下划线和大写字母打头的名称被保留给实现(编译器及其使用的资源)使用。以一个下划线开头的名称被保留给实现,用作全局标识符。
6、C++对于名称的长度没有限制,名称中所有的字符都有意义,但有些平台有长度限制
4.1.3.2 变量的定义
变量定义就是告诉编译器在何处创建变量的存储,以及如何创建变量的存储。
变量定义指定一个数据类型,并包含了该类型的一个或多个变量的列表,如下所示:
type variablelist;
在这里,type 必须是一个有效的 C++ 数据类型,可以是 char、wchar_t、int、float、double、bool 或任何用户自定义的对象,variable_list 可以由一个或多个标识符名称组成,多个标识符之间用逗号分隔。下面列出几个有效的声明:
int i, j, k;
char c, ch;
float f, salary;
double d;
在C++语言中,要求对所有用到的变量作强制定义,也就是必须“先定义,后使用”,定义变量的一般形式是:变量类型 变量名表列;
变量名表列指的是一个或多个变量名的序列。如: int a,b,c,d,e;
定义a,b,c,d,e为整型变量,注意各变量间以逗号分隔,最后是分号。可以在定义变量时指定它的初值。如:
int a=83, b, c=64, d=81, e; //对变量a,c,d指定了初值,b和d未指定初值
C语言要求变量的定义应该放在所有的执行语句之前,而C++则放松了限制,只要求在第一次使用该变量之前进行定义即可。也就是说,它可以出现在语句的中间。
C++要求对变量作强制定义的目的是:
1) 凡未被事先定义的,不作为变量名,这就能保证程序中变量名使用得正确。例如,如果在声明部分写了
int student;
而在执行语句中错写成statent。如
statent=30;
在编译时检查出statent未经定义,作为错误处理。输出“变量statent未经声明”的信息,便于用户发现错误,避免变量名使用时出错。
2) 每一个变量被指定为一确定类型,在编译时就能为其分配相应的存储单元。如指定a和b为int型,一般的编译系统对其各分配4个字节,并按整数方式存储数据。
3) 指定每一变量属于一个特定的类型,这就便于在编译时,据此检查该变量所进行的运算是否合法。例如,整型变量a和b,可以进行求余运算:
a%b
%是“求余”,得到a/b的余数。如果将a和b指定为实型变量,则不允许进行“求余”运算,在编译时会给出有关的出错信息。
int age ; //定义(声明)了一个变量,变量名为age,变量类型为int
对外传递的信息:
1、数据类型:int
2、数据类型的大小/范围:-2^31(-2 147 483 648) ~ 2^31-1(2 147 483 647)
因为CPU的差异,各系统中的数据类型所占的字节数(bytes)不同,二进制位数(bit)也不同
DOS是16位磁盘操作系统,当前的操作系统都是32位或64位的
3、变量名称:age
4、存储空间大小:4byte;32bit
#include <iostream>
cout << "int:bytes " << sizeof(int) << " ;bit " << sizeof(int) * 8 << endl;
5、存储单元名称:age
这条语句提供了两项信息:需要的内存及该内存单元的名称;具体地说,这条语句指出程序 需要足够的存储空间来存储一个整数;并给存储单元指定名称,这里的名称为age;
注:
在C中,所有的变量声明通常都位于函数或过程的开始位置;C++的做法是尽可能在首次使用变量前声明它。
4.1.3.3 变量的声明
变量声明:只是向编译器说明将有一个变量存在,而不为它分配所需的存储空间,仅起占位符的作用。
格式: extern type var1,var2,…;
extern 是 C++ 的关键字,表示定义的变量是 “外部”(要么中另一个文件中,要么在本文件后面某处)的,
使用关键字 extern 声明一下变量,可以先使用后定义,可以避免编译时报告变量不存在的错误。
变量声明向编译器保证变量以给定的类型和名称存在,这样编译器在不需要知道变量完整细节的情况下也能继续进一步的编译。变量声明只在编译时有它的意义,在程序连接时编译器需要实际的变量声明。
当您使用多个文件且只在其中一个文件中定义变量时(定义变量的文件在程序连接时是可用的),变量声明就显得非常有用。您可以使用 extern 关键字在任何地方声明一个变量。虽然您可以在 C++ 程序中多次声明一个变量,但变量只能在某个文件、函数或代码块中被定义一次。
注:
1、因为变量的声明并没有给变量分配存储空间,所以不能在声明时给它赋初值,因为它实际上还不存在。
2、如果用 extern 声明类某个变量,则必须确保这个变量真的存在于 “外部” 中,并被 #include 指令包含进来,或真的存在于本文件后面某处。
创建 test.cpp 文件:
#include
using namespace std;
#include “test.h”
extern int a;
extern int b;
int main(void)
{
int c = 2; //定义变量 c
cout << “a = ” << a <<endl; //变量 a 时声明,在另一个文件内
cout << “b = ” << b <<endl; //变量 b 是声明,在本文件后面
cout << “c = ” << c <<endl; //变量 c 是定义
return 0;
}
int b = 1; //定义变量 b
创建 test.h 文件,并放在 test.cpp 同一目录下:
#include
using namespace std;
int a = 3;
然后 gcc test.cpp 即可
4.1.3.4 变量的存储
变量的存储:变量从哪里得到保存数据所需的内存空间,以变量的定义方式而定。
1、如果变量定义在函数体内,且无 static 修饰,则该变量将从堆栈中得到存储空间。当程序执行进入函数后,才为变量分配存储空间。退出后,自动释放分配的空间。
2、如果变量定义时被 static 修饰,则无论是在函数体内,还是函数体外,都将从全局空间中得到存储空间。
3、如果变量定义在函数体外,则在全局内存空间中得到存储空间。
例:
#include
using namespace std;
double d1,d2; //定义变量 d1 和 d2
static int s1; //定义静态变量 s1
int main(void)
{
static char ch; //定义静态变量 ch
int s2; //定义变量 s2
d1 = 3.14; //使用在 main() 外部定义的变量
d2 = d1;
d1 = d1 + d2;
s1 = 10; //使用在 main() 外部定义的静态变量
ch = ‘a’; //使用在 main() 内部定义的静态变量
s2 = 1;
return 0;
}
注:
变量 d1、d2、s1 定义在函数体内,所以存储于全局内存中,
ch 虽然定义函数体内,但有关键字 static 修饰,所以也放在全局内存中,
s2 在函数体内定义又无 static 修饰,所以将从堆栈中得到存储空间。
每一个变量,都有两个信息与其相关联:
数据值(右值):变量的数据值就是存储在变量对应的存储单元中的数据。右值意指被读取,既在赋值运算符的右边,代表的是数据。
地址值(左值):变量的地址值就是变量对应的存储单元的首地址。左值意指被写入,既在赋值运算符的左边,代表的是地址。
4.1.3.5 变量的初始化
变量被定义后,就在内存中给它分配类一块存储单元,用于存放数据,他可能包含预料不到的数据(曾经存储在其中过的数据)。因此直接使用而不初始化的话,可能会数据越界、堆栈溢出等。
允许在定义变量时对它赋予一个初值,这称为变量初始化。初值可以是常量,也可以是一个有确定值的表达式。如
float a,b=5.78*3.5, c=2*sin(2.0);
表示定义了a,b,c为单精度浮点型变量,对b初始化为5.78*3, 对c初始化为2*sin(2.0),在编译连接后,从标准函数库得到正弦函数sin(2.0)的值,因此变量c有确定的初值。变量a未初始化。
初始化不是在编译阶段完成的,而是在程序运行时执行本函数时赋予初值的,相当于执行一个赋值语句。例如:
int a=3;
相当于以下两个语句 :
int a; // 指定a为整型变量
a=3; // 赋值语句,将3赋给a
对多个变量赋予同一初值,必须分别指定,不能写成
float a=b=c=4.5;
而应写成
float a=4.5, b=4.5, c=4.5;
或
float a, b, c=4.5;
a=b=c;
C++另一种初始化方法:
int a(101);
C++11标准的初始化:
C++11将使用大括号的初始化称为列表初始化,因为这种初始化常用于给复杂的数据类型提供值列表。
int a = {10};
int a{10};
int a = {};//在这种情况下,变量将被初始化为0;
int a{};//在这种情况下,变量将被初始化为0;
注:
一般编译器会默认给没初始化的变量 0(int、float型时)或 NULL(字符型时)
4.1.3.6 变理的作用域
作用域是程序的一个区域,一般来说有三个地方可以定义变量:
1、在函数或一个代码块内部声明的变量,称为局部变量。它们只能被函数内部或者代码块内部的语句使用。在程序中,局部变量和全局变量的名称可以相同,但是在函数内,局部变量的值会覆盖全局变量的值。当局部变量被定义时,系统不会对其初始化,必须自行对其初始化。
2、在函数参数的定义中声明的变量,称为形式参数。
3、在所有函数外部声明的变量,称为全局变量。全局变量的值在程序的整个生命周期内都有效。全局变量可以被任何函数访问。也就是说,全局变量一旦声明,在整个程序中都是可用的。定义全局变量时,系统会自动初始化为下列值:
4.1.3.7 变量的分类
按变量的作用域分:
(1)局部变量(自动变量):在函数内部或函数参数中定义,存储在堆栈中,在程序进入函数时才分配空间,退出时就释放。不同的函数内可以有相同名称的局部变量。
(2)全局变量:定义在函数外部,存储在固定的存储区域内,具体由编译器来决定,在程序的生存期内都有效,除非手工释放,否则直到程序运行结束才释放所占空间。全局变量绝不重名。
按变量的存储位置划分:
1、全局空间变量:就是全局变量,存储在某一固定区域内,全程有效,用 extern 关键字声明的变量属于全局空间变量的一种。
2、堆栈空间变量:就是局部变量,存储在函数自有的堆栈空间内。
3、寄存器变量:数据保存在 CPU 的寄存器内的变量,用关键字 register 说明,也是局部变量,仅限于整型和字符型。操作速度快,但不能定义太多,也不能长久占据。
按变量的存储方式划分
1、静态变量:用关键字 static 说明,在程序所占据的数据段(Data Segment,在内存中某处保留的一段大小固定的空间)内分配永久存储空间,整个执行过程中都不释放。静态变量分为:
1.1、静态局部变量:只在定义它的函数或语句块内有效,只在第一次调用时定义,以后每次调用函数时都不再重新定义,仍然保留上次函数调用结束时的值(静态局部变量时有记忆的)。
1.2、静态全局变量:只在定义它的文件内全程有效,在整个运行期间均存在并且保持原来的存储单元位置不变,因此不能用 extern 关键字从其他文件中引用静态全局变量。
2、动态变量:用关键字 auto 说明,且 auto 一般省略,自动变量的赋值时在函数调用时进行的,每调用一次就重新赋值一次。
例:
int a = 100 ; //定义变量 a
extern int b ; //引入外部变量 b
static int c ; //定义静态变量 c
extern int d ; //引入外部变量 d
int main(void)
{
register int g = 100 ; //定义寄存器变量 g
cout << “g = ” << g << endl ;
cout << “d = ” << d << endl ;
cout << “a = ” << a << endl ;
return 0 ;
}
int d = 100 ;
注:
a 是全局变量,可以被引出到其他文件中,
b 是从外部引入的,也是全局变量,
c 是静态全局变量,只在本文件内有效,不能引出到其他文件中,
d 是用 extern 声明的全局变量,其定义在 main() 函数后,
g 是寄存器变量。
4.1.4 常变量
const int a=3;定义a为一个整型变量,指定其值为3,而且在变量存在期间其值不能改变
常变量与常量的异同是:常变量具有变量的基本属性:有类型,占存储单元,只是不允许改变其值。可以说,常变量是有名字的不变量,而常量是没有名字的不变量。有名字就便于在程序中被引用。
#define Pi 3.1415926//定义符号常量
const float pi=3.1415926;//定义常变量
符号常量Pi和常变量pi都代表3.1415926,在程序中都能使用。但二者性质不同:定义符号常量用#defne指令,它是预编译指令,它只是用符号常量代表一个字符串,在预编译时仅进行字符替换,在预编译后, 符号常量就不存在了(全置换成3.1415926了),对符号常量的名字是不分配存储单元的。而常变量要占用存储单元,有变量值,只是该值不改变而已。从使用的角度看,常变量具有符号常量的优点,而且使用更方便。有了常变量以后,可以不必多用符号常量。
具有变量的特征,具有类型,在内存中存在以它命名的存储单元。使用const定义常变量,常变量名小写,定义时需指定数据类型,语句末以分号结束;不允许改变值;常变量占用内存;
符号常量和常变量的区别:
(1)定义的不同
符号常量:是使用一个字符串代替程序中出现的一个标识符,是编译时把所有的符号常量都替换成制定的字符串,它没有类型,在内存中也不存在以符号常量命名的存储单元。在其作用域内其值不能改变和赋值。
常变量:具有变量的特征是存在一个以变量名命名的存储单元,在一般情况下,存储单元中的内容是可以变化的。而对于常变量,在变量的基础上加了一个限定:存储单元中的内容不允许变化,仍然是有存储单元的。
常变量具有变量的特征,它具有类型,在内存中存在着以它命名的存储单元,可以用sizeof运算符测出其长度。与一般变量惟一的不同是指定变量的值不能改变。用#define命令定义符号常量是C语言所采用的方法,C++把它保留下来是为了和C兼容。C++的程序员一般喜欢用const定义常变量。虽然二者实现的方法不同,但从使用的角度看,都可以认为用了一个标识符代表了一个常量。有些书上把用const定义的常变量也称为定义常量,但读者应该了解它和符号常量的区别。
(2)内存空间
define是宏定义,程序在预处理阶段将用define定义的内容进行了替换。因此程序运行时,常量表中并没有用define定义的常量,系统不为它分配内存。
const定义的常量,在程序运行时在常量表中,系统为它分配内存。
(3)类型校验
define定义的常量,预处理时只是直接进行了替换。所以编译时不能进行数据类型检验。
const定义的常量,在编译时进行严格的类型检验,可以避免出错。
(4)边缘效应
define定义表达式时要注意“边缘效应”,例如如下定义:
#define N 2+3 //我们预想的N值是5,我们这样使用N
int a = N/2; //我们预想的a的值是2.5,可实际上a的值是3.5;原因在于在预处理阶段,编译器将 a = N/2处理成了 a = 2+3/2;这就是宏定义的字符串替换的“边缘效应”因此要如下定义:
#define N (2+3)
const定义表达式没有上述问题,const定义的常量叫做常变量原因有二:
1)const定义常量像变量一样检查类型。
2)const可以在任何地方定义常量,编译器对它的处理过程与变量相似,只是分配内存的地方不同(常量在静态区,变量在栈区)
注:
1、它有类型,便于编译器进行类型检查
2、可以寻址,即使出现多个副本,只需分配一次空间
3、可以在任何地地方定义常量,编译器像对待变量一样处理它,只是分配内存的地方不同而已
4、在内存中存在着以它命名的存储单元,除非在语句中出现,否则编译时不会为它分配空间
5、一旦定义完就不能修改,所以必须赋初值,且一定要在定义语句中赋值(唯一的初始化机会)
6、程序运行时,它被记录在专门的常量表中
7、常变量在静态区分配存储空间,不像变量时在堆栈中
用 const 说明常量有两点好处:
1、如果编译程序知道一个变量的值不会改变,就会在编译时对它进行优化
2、编译程序会力图保证该变量的值不会因为程序员的疏忽而被改变
1、#define 定义的符号常量才是真正意义上的常量,其目的就是为了用标识符代替一个不变的常量,
2、const 说明的常变量,更多的目的时为了保护和优化
3、#define是C语言中采用的方法, C++ 更多用的是const 定义常变量,C++保留#define 以兼容C,推出const以替代 #define
有些调试工具可以调试常变量,不能调试符号常量,因为符号常量在编译时就进行了符号到数据的替换,编译后,符号常量已不存在,取而代之的是它所保存的常数。
除了在定义时,否则不能将 const 修饰的对象、引用和指针作为赋值表达式的左值,即不能出现在赋值运算符的左边