1
四、常数和数据的内存布局五,sizeof 运算符六、变 量
2
四、常数和数据的内存布局文字常数是用若干字符序列构成的原始数据,简称为常数。常数具有三个基本特点:
1,常数的类型 ;
2,确定的值 ;
3,常数不具备地址属性(字符串常数例外) ;
常数是一个可以在程序中作为值的一个数或若干有效字符。
常数是在程序中无须进行定义就可以直接使用的立即数。常数是不能修改的最简单的右值,用于初始化同类型的变量。
3
文字常数分为四种,
1,实型常数 ;
2,整型常数 ;
3,字符常数 ;
4,字符串常数 ;
常数是 C/C++处理数据的可理解的原始材料,是二进制数等价的源头处的文本表示。
内存的数据状况是二进制的,数据的原始状况或初始值通过常数确定。
4
( 1) 实型常数和内存布局
a,实型数就是浮点型数。
浮点数是有符号的,由关键字 float,double和 long
double细分为单精度、双精度和长双精度三类。实型常数是一个有符号的十进制实数。实型常数的表示有两种形式:一是小数表示法,又称一般表示法;另一种为科学表示法或指数表示法。
小数表示法由必不可少的小数点和数字构成,格式为:
[ 数字序列 ],数字序列 [ 后缀 ]
整数部分 小数部分指数表示法 e之前必须有数字 e之后数字必须为整数,格式为,[数字序列 ],[ 数字序列 ]e 数字序列 [ 后缀 ]
尾数部分 指数部分
5
数字序列由数字 0123456789 组成,前面可冠以 +,-
号,实型常数除非前面加有一个负号,否则是一个正数。
数字序列前的正号通常省略。方括号 [ ] 中包括的内容表示可省略的部分。实数后缀为字母 f,F,l,L之一。
没有后缀的实型常数具有缺省类型 double。如果有 f或 F
后缀,该常数的类型为 float;如果有 l或 L后缀,其类型为
long double。
scanf 函数读取数据时不添加后缀。
printf 函数也不保留后缀。后缀用于鉴别常数的确切类型。
可以省略小数点之前的数字,75 (该值的整数部分 ),或小数点之后的数字 10.(小数部分 ),但不能都省略。
6
指数表示法尾数部分与指数部分构成,以 10为底的幂次来确定数据的值。 如:
尾数为一个字符序列后带小数点再带上小数部分的可选数字序列。当包括一个指数时可以省去小数点 175e-2。
可以省略指数表示法的整数部分,0075e2,实型常数 例子如下:
float型,10.f,1.575E1f,2500E-6F;
double型,0.75,-0.0025,-2.5e-3;
long double型,.0025L,25E-4l,1575e-3l,3.e5L;
下面的初始化定义语句表示了浮点变量和相应常数的严格匹配:
float f = 1.575E1f; double d = 0.75;
long double xd = 3.e5L;
7
b,浮点数的内存表示
float型数据占 32个 bit,double型数据占 64位,采用阶码或指数 n和尾数 m的形式来确定数值的转换:
单精度浮点数双精度浮点数 最高位一般作为浮点数的符号位用于鉴别其正负。
阶码中有一个位则作为阶码的符号位简称阶符,浮点数的具体细节随机器略有变化。
单精度浮点数中最低的 23位用来表示尾数 m,中间的 8
位表示阶码或指数 n,8位数的临界值 (去掉一个符号位 )微软的编译器定为 27=128;
f m n32 23 82
d m n64 52 112
8
双精度浮点数中最低的 52位用来表示尾数 m,中间的 11位表示阶码或指数 n,11位数的临界值微软的编译器定为
210=1024,尾数 m的范围介于 0.5和 1.0之间,由此浮点数大约的极限范围为:
指数或阶码则限定了符点数的数据范围,小数部分的尾数和阶码如一起界定了符点数的精度。
F mitlim,23 128 382 3 4 10
D mitlim,52 1024 3082 1 7 10
8(或 11)位阶码 n 23(或 52)位尾数 m
上图为 32(或 64)位浮点数的内存表示
9
下面是 float数的内存布局:
1 0 0 0 0 0 1 0 0 1 1 0 0 1 0 01 0 1 0 1 1 1 1 1 0 1 1 0 1 1 1
1 0 0 1 0 1 1 0 0 1 1 1 0 1 0 0s 0 0 0 1 1 1 1 0 010 1 1 1 1 1
低地址8位阶码 23位尾数高地址
4字节 float数的内存布局二进制数
10
( 2) 整型常数和内存表示
a,整型常数的表示整型常数用十进制、八进制或十六进制来表示。整型常数前加一个负号表示对其算术负运算的结果,其含义取决于特定的转换。整型常数的数字之间不能用空白字符分开。
1) 十进制常数格式为,数字序列 [后缀 ]
十进制语法的数字序列由数字 0 1 2 3 4 5 6 7 8 9构成,
不能 0 开头。 例如:
10,123,-32145,2456L,
3218u,776712ul,778899LU。
11
可省略的后缀为 l,L,u,U。用 l,L表示整型常数为 long
类型。通过添加 u或 U表示整型常数为 unsigned类型。小写字母 l可能与数字 1混淆。缺省类型一般处理为 int类型。但没有一个界定 short型整数的后缀。
2) 八进制常数格式为,0数字序列 [后缀 ]
八进制语法的数字序列由数字 0 1 2 3 4 5 6 7构成,必须 0 开头。
例如:
012,0203,-032145,024L,0117L,
0776745ul,034(28)。
12
3) 十六进制常数格式为,0x数字序列 [后缀 ]
十六进制语法的数字序列由数字 0 1 2 3 4 5 6 7 8 9,字母 a b c d e f A B C D E F构成,以 0x,或 0X 开头。 例如:
0x1c (28),0x0a,0X7ef,0x7fffffL,-0x3FFF。
下面的初始化定义语句表示了整型变量和相应常数的严格匹配,unsigned long ul=024uL;
int si= - 32145;
unsigned int ui = 0x9ff0u;
这样 unsigned long 型的常数 024uL严格匹配
unsigned long函数的形参。而整型常数 -32145,0x9ff0u可以匹配函数 unsigned long型的形参,但涉及到数据的类型转换。
13
b,整数的内存表示整型数 (char,short,int,long) 分有符号和无符号两种,
有符号整数其最高的位用于识别数的正负,最高位为 0表示一个正数,1则表示负数。无符号整数的最高位作为数据的有效部分,位长 n的无符号数的值由式子
an-1·2 n-1+an-2·2 n-2+…+a 0·2 0 计算。
例如无符号 8位数 0xff的值为因此 n位无符号整数的变动范围为:
8位有符号数的表示范围是 -128~+127。 n位有符号的整型数的变动范围为:
2 2 2 2 1 2557 6 0 8
0 2 1N n
2 2 11 1n nN
14
数据分整型数和浮点数两种,对于内存中以字节为单位的数据解释由于数据类型不同而不同。
同为 4字节的内存数据由于对于位 bit的映射不同,long
型数据范围为,
32位无符号长整型的数变动范围为:
这比同为 4字节的 float型的极限范围,
小,但精度却丝毫不差。
2 2 131 31( )
0 2 132~?
F mitlim23 1282
15
在 32位的 PC微机上数据的存放方式是低尾端形式的,
即 short型 16位字节的低 8位存放在内存的低地址处,高 8bit
存放在内存的高地址处,高尾端的存放方式则相反。
s 0 01 1 1 1 1
s 1 1 1 1 1 1 1 1 0 01 1 1 1 1
1 0 0 0 0 0 1 0 0 1 1 0 0 1 0 0s 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1
1 0 0 0 0 0 1 0 0 1 1 0 0 1 0 01 0 1 0 1 1 1 1 1 0 1 1 0 1 1 1
二进制数高地址 低地址
char
short
long
图为整数的内存布局 (s对于有符号数作为符号判断指标,s取 0对应正数 )
16
( 3) 字符常数字符常数是字符集里的一个字符。字符集包括 ASCII字符集里所有可显示的字符以及换行符、水平制表、垂直制表、回车符等。字符常数有四种表现形式:
a,单引号表示例如,'a','N','5','+','0','1'
都是字符常数。
其中单引号作为定界符使用,并不表示字符常数本身。
17
b,特殊字符的转义序列表示转义序列由一个反向斜杠 \后紧跟一个字母或数字构成。为了表示换行符、单引号或某些其它字符常数,必须使用转义序列。
转义序列一般用于指出动作,如在终端和打印机上的回车和制表移动,它们也用来提供非打印字符和有特定意义的字符,如双引号。水平制表符 \t在格式控制中表示水平右跳 8
个空格。 例,单引号的表示为 '\'' 。
18
c,八进制转义序列表示在八进制转义序列 \ooo中只能使用 0~ 7数字。八进制转义序列不能长于 3个数字,十进制 0~255的八进制表示范围为 '\000'~'\377'。例如:
字母 A的八进制表示是 '\101',ESC字符为 '\033'。
如果转义序列 \ooo中字符 o是 8以上的数字,则结果超出字符的数据范围。
19
d,十六进制转义序列表示十六进制字符代码 \xhh 中使用 0~9数字和字母 a~f或
A~F,字母 A可表示为‘ \x41?反斜杠可表示为‘ \x5c?。
数字字符‘ 0?为‘ \x30?,?\0?的 ASCII值为 0。小写 c字符的
ASCII值为 0x63。常数‘ c?,?\x63?是同一个字符的不同表示方式。常数 0x6D和‘ \x6D?存在细微的差异。
字符常数‘ \x6D?不可以写为‘ \0x6D?,int型常数 0x6D
不可以写为 x6D。
在转义序列中的字符和普通字符赋给字符变量时,两侧加单引号。 例如:
char x='\"'; char y='\\';
char z='\023'; char w='\x04'; char x='a';
20
无单引号的格式具有另外的语义。 例如,char x=?a?;不同于 char x=a;
字符常数在内存中以 ASCII值存储,即字符常数是占一字节内存的整数。这个 ASCII值既可以字符的方式显示,也可以十进制方式显示,也可以 16进制方式显示。
出现在表达式中的 char数据则以 ASCII值参加运算。
'a'的 ASCII值为 97,'a'+3的值为 100,这也是字符 d的 ASCII
值。
小写字母 ASCII值大于相应的大写字母,存在关系式
'x'='X'+32。
21
转义序列 ASCII值 字符形式 含义
\0 0 NUL 空字符
\a 7 BEL 报警
\b 8 BS 退格键
\t 9 HT 水平制表符
\n 10 0x0a NL(LF) 换行
\v 11 0x0b VT 垂直制表符
\f 12 0x0c FF 换页符
\r 13 0x0d CR 回车符
\" 34 0x22 " 双引号
\' 39 0x27 ' 单引号
\\ 92 0x5c \ 反斜杠
\ooo ooo 八进制数
\xhh hh 十六进制数
22
( 4) 字符串常数字符串常数是用双引号包括的一串字符。这串字符中不能简单地包括双引号“和反斜杠 \,而单引号是可以的。
在转义序列中的字符位于字符串常数中时,两侧可不加单引号,单引号视为普通的字符。下面给出几个字符串常数:
"hello","program C++",
"'A'b\143"(相当于 "'\101'bc")
其中双引号仅作为定界符使用,并不是字符串常数中的字符。
23
字符串常数在内存中存储时,自动在其尾部追加一个
‘ \0?字符,‘ \0?字符在 ASCII码中,其代码值为 0,称为空字符,也称为结束字符。
长度为 n个字符的字符串常数,在内存中占用 n+1个字节的空间。 C/C++语言字符串常数具有这种特性,一般简称为字符串。例如:
字符串常数,hello”字面上具有 6个字符。但存储在内存中时,如下所示占用 7个字节空间即 sizeof (“hello”)=6。
h e l l o \0
24
( 5) 符号常数和 bool 型常数常数是一串意义不确切的数据组合。
例如 3.14159,96000,除了程序员知道这些数值的内在意义外,一般地只能把它们分别视为浮点数与整数。
因此值得把有意义的常数用一个符号来标定,让常数具有一定的语言含义,这样就产生了符号常数。符号常数是通过编译预处理指令 # define来建立的,其语法格式为:
# define 符号常数名 常数或 # define 宏名 文本串
25
例如:
#define PI 3.14159
这条指令把常数 3.14159与圆周率 PI相联系,此处宏名
PI就是常数 3.14159的符号名,当程序进行编译预处理时,
源程序中的符号 PI,就用文本串 3.14159代替,因此符号常数就是文字常数的一个名称。
替换宏名的文本串是字符集中若干字符的有序组合。
C++中存在两个占一个字节的 bool型常数,它们分别是关键字 false(0)和 true(1)。
26
五,sizeof运算符
sizeof是系统的关键字同时又作为常用的运算符。
sizeof运算符用于确定数据占有的内存空间大小,返回一个表达式或某一数据类型的字节数。它主要有两种语法格式:
sizeof (e) 或者 sizeof e? sizeof (type)
type 是定义变量的数据类型,e是表达式。
类型 type格式必须放在括号中,表达式可以不用括号。
sizeof 运算符的操作数既可以是内置数据类型,也可以是用户引入的数据类型或结构变量。
27
sizeof在编译阶段就完成了计算处理,其结果作为无符号 int常数。因而操作数实质上包含数据所占内存的静态大小。
下面是 32位模式下的输出结果,
sizeof (1.0f) = sizeof ( float) = 4,
sizeof (1.0) = sizeof ( double ) = 8
sizeof '1' = sizeof(char)=1,
sizeof 1 = sizeof ( int ) = 4
sizeof (?\x6D?) = 1,
sizeof (0x6D) = 4。
sizeof ("12") = 3
28
六、变 量变量具有四个基本要素:
1,变量类型 2,变量的值 3,变量的地址 4,变量的存储属性 ;
C/C++中名称 (变量 )是大小写敏感的,number与 Number
是不同的名称。在 Windows编程中流行的命名规则为“匈牙利法”,该规则主要是为了提高程序的易读性,容易区分变量的类型,是否类的成员等 ;
如,myBook,theStudent
29
前缀
i 或 n
l
b
c
p
lp
s
sz
h
m_
含 义
int 型 near短整型
long型
bool型
char型
pointer 指针型
long pointer 长整型指针
string 字符串
string zero零结尾的串
handle 句柄
member 类的成员变量命名时前缀的含义
30
1.变量的定义任何一个变量在使用之前必须定义;
变量定义语句具有下面的格式:
类名 变量名 1,变量名 2,…,变量名 n;
type v1,v2,…,vn;
如:
int i,j,k,l,m,n;
//相当于 int i; int j; int k; int l; int m; int n;
float x,y,z;
//相当于 float x; float y; float z;
31
2.变量的初始化在定义变量的同时可给该变量赋一个初始值,变量的初始化常采用下面的形式:
类名 变量名 =表达式; type variable =expre;
表达式通常是常数,表达式一般应与变量具有相同的类型属性。例如,整型常数初始化整型变量,字符常数初始化字符变量,除非进行隐含的或显式的类型转换。
例如:
int i=0; char c='d'; float x=3.456f;
整型变量 i赋以初值 0,字符变量 c赋以初值’ b?,浮点型变量 x赋以初值 3.456。这几个变量的初始化在局部作用域
(函数体界定的区域 )中可以等价地分开写为:
int i; i=0; char c; c='d'; float x; x=3.456f;
32
函数体中的变量位于堆栈空间,局部范围的初始化语句具有动态的性质。
而在全局范围初始化语句不能分开写。
变量一经初始化后就保持该值不变,除非变量重新赋值。
未经初始化的全局变量系统通常设置其为 0,局部变量设置为不确定的随机值。
C++中在定义变量的时候赋初值也可采用另一种形式:
int i (0);
int k (i);
33
3.变量的地址属性变量名携带着存储的位置信息即变量的地址属性。变量的地址可以通过取地址运算符 &与变量名组合在一起得到,
设 a是变量,则 &a就是变量 a的地址,此时系统使用了变量的地址属性。
为了得到变量 a的地址中的内容,系统提供了访问指针运算符 *,例如 *(&a)就是变量 a的值。对变量的地址属性进行操作可以通过指针来实现,指针就是地址的变量,定义指针的格式如下:
int* pi; //定义一个 int*类型属性的指针 pi
long * pl; double * pd;
//定义 long *型的指针 pl,double *型指针 pd
34
4.变量的存储属性变量具有存储属性来指出变量的作用范围、生存期以及与编译相关的连接性。名称的作用范围是指其可以有效访问的区域。变量的生存期是变量持续的时间。有四个关键字来说明变量的存储属性,
1,auto 存储属性,2,register 存储属性
3,static 存储属性,4,extern 存储属性:
局部范围是函数体界定的范围,全局范围 (也称文件范围 )是类声明外和函数定义外的范围。
在函数中定义的变量为局部变量,在函数外文件范围定义的变量为全局变量。
35
[例 ]C++中初始化定义语句 char m=f(3);中的表达式是函数调用。
#include<stdio.h>
int f ( int k)
{ printf ("k=%d,",k);
return k*k;
} //k是定义在堆栈空间的局部变量
void main (void)
{ char m=f(3);
printf ( "m=%d\n",m);
} //输出 k=3,m=9
36