c++数据类型及复杂声明推演

关键字:c++ datatype 数据类型 声明 推演

 

数据类型

c++程序员已经知道c语言有五种数据类型:void,int,float,double和char:


类型       描述
void       无数据类型
int         整数类型
float      浮点数
double   双精度浮点数
char       字符型

c++有额外定义了两种类型:bool和wchar_t:
类型 描述
bool       布尔变量,true或者false
wchar_t  宽字符

 

类型修饰符

这几个类型中的部分可以使用以下几个关键字来修饰:signed,unsigned,short和long. 如果只有类型修饰符自己被使用,则数据类型会被缺省地认为int.下面是一个完整的列表列出了可能的数据类型(等效的会被显示在同一行,并且用,隔开):
整数类型
bool
char, signed char
unsigned char
short, short int, signed short int
unsigned short, unsigned short int
int, signed int
unsigned, unsigned int
long, long int, signed long, signed long int
unsigned long, unsigned long int
浮点类型
float
double
long double
可选支持的整数类型
long long, signed long long, long long int, signed long long int
unsigned long long, unsigned long long int
wchar_t

 

类型的大小和范围

任何一种数据类型的大小和范围都是编译器相关的. 头文件"cfloat"(或者"float.h")经常定义了各种数据类型的最大最小值.你可以使用操作符sizeof来测试任何一种数据类型的大小(这里的大小是指该类型的变量说占用的空间大小,即占用的字节数).然而,很多的系统会依据某一标准来实现数据类型.整型和浮点型通常是32位,字符8位,双精度64位.布尔变量则常会是8位.long long类型是64位.

数字值的限制是定义在头文件<limits>里. 模板化的类numeric_limits提供了系统相关的c++数据类型的表现。 使用适当的函数并且以数据类型作为模板参数可以返回一些需要的信息。需要注意的是mumeric_limits是可以被用户定义的类型重载。
下面是一些相关的函数(译者:其实有些是静态成员变量,至少在dev-c++里如此):
方法                 返回类型 描述
is_specialized          bool
radix                   int             指数的底
digits                  int             尾数中的基数位数
digits10                int             尾数中的10进制数位数
is_signed               bool     
is_integer              bool
is_exact                bool
min                     <type>          可以表示的最小数(不是负的最小数)
max                     <type>          最大数
epsilon                 <type>          固有误差
round_error             <type>          最大取正误差(即四舍五入就是0.5)
infinity                <type>   
quiet_NaN               <type>          不能标示浮点错误的无效数字
signaling_NaN           <type>          标示浮点错误的无效数字
denorm_min              <type>       
min_exponent            int 
min_exponent10          int 
max_exponent            int 
max_exponent10          int 
has_infinity            bool 
has_quiet_NaN           bool 
has_signaling_NaN       bool 
has_denorm              <type>_denorm_style 
has_denorm_loss         bool 
is_iec559               bool            遵照IEC-559规范
is_bounded              bool 
is_modulo               bool 
traps                   bool 
tinyness_before         bool 
round_style             float_round_style { round_to_nearest, … } //float_round_style是一个枚举类型,具体可以参考<limits>

最通常的用法就是边界检查来确认某一数据类型可以表示的最大最小值。接下来的代码打印出了该系统平台上short类型的最值:
  #include <limits>
  std::cout << "Maximum short value: " << std::numeric_limits<short>::max() << std::endl;
  std::cout << "Minimum short value: " << std::numeric_limits<short>::min() << std::endl;

 

理解类型声明

简单的类型声明很容易理解:
int i
然而,有时也会有十分复杂的类型声明:
double **d[8]   //hmm...
char  *(*(**foo [][8])())[]  //augh! foo到底是什么?

为了理解上面的声明,可以遵循下面的三个规则:
1.从变量名开始(在上例中就是d或者foo)
2.以数据类型结尾(double或者char)
3.如果可以的话向右,必须的话则向左(括号可能会让你改变方向)

例如:
表达式          意义
double **d[8];  
double **d[8];  d 是 … double 
double **d[8];  d 是 8维数组 … double 
double **d[8];  d 是 8维数组且成员为 指针且指向 … double 
double **d[8];  d 是 8维数组且成员为 指针且指向 指针且指向 double 

另外一个例子:
表达式                      意义
char *(*(**foo [][8])())[]  
char *(*(**foo [][8])())[]  foo 是 … char 
char *(*(**foo [][8])())[]  foo 是 一个数组 … char 
char *(*(**foo [][8])())[]  foo 是 一个数组且成员为 8维数组 … char 
char *(*(**foo [][8])())[]  foo 是 一个数组且成员为 8维数组且成员为 指针且指向 … char 
char *(*(**foo [][8])())[]  foo 是 一个数组且成员为 8维数组且成员为 指针且指向 指针且指向 … char 
char *(*(**foo [][8])())[]  foo 是 一个数组且成员为 8维数组且成员为 指针且指向 指针且指向 函数且返回 … char 
char *(*(**foo [][8])())[]  foo 是 一个数组且成员为 8维数组且成员为 指针且指向 指针且指向 函数且返回 指针且指向 … char 
char *(*(**foo [][8])())[]  foo 是 一个数组且成员为 8维数组且成员为 指针且指向 指针且指向 函数且返回 指针且指向 数组 … char 
char *(*(**foo [][8])())[]  foo 是 一个数组且成员为 8维数组且成员为 指针且指向 指针且指向 函数且返回 指针且指向 数组且成员为 指针且指向 char 

想有更详细的了解,可以参考Steve Friedl对如何理解c数据类型的精彩解说:
http://www.unixwiz.net/techtips/reading-cdecl.html.

 

译注:
我们也可以采用另外一种方法就是层层类型定义的方法来解读,不过是从外向内的.比如例子1:
typedef double * pDouble;
typedef pDouble* ppDouble;
typedef ppDouble a8ppDouble[8];
a8ppDouble d;

第二个例子:
typedef char * pchar;
typedef pchar apchar[];
typedef apchar * papchar;
typedef papchar fpapchar();
typedef papchar (*pfpapchar)();
typedef pfpapchar * ppfpapchar;
typedef ppfpapchar a8ppfpapchar[8];
typedef a8ppfpapchar aa8ppfpapchar[];
typedef aa8ppfpapchar foo;
 


附文:
下面是对Steve Friedl的参考文章的部分译文,因为这里也同样的是解说这两个例子,不过对于规则上述说更详细。

基本和派生类型

基本类型就是上边提到的那些基本类型,这里还包括了struct,enum和union(译注:因为这个说明是针对c语言的,所以对于c++来说还要加上一个class类型)

一个声明只能定义一种基本类型,并且它总是出现在声明式的最左边。基本类型可以被派生类型扩张或者说是修饰,并且c有三种派生类型:
* 指针且指向(pointer to...)
  这里*表明这是一个指针,而且不言自明地它要指向某个东西
[] 数组且成员为(array of...)
  数组可以是已定维数的--[10]--也可以是未定维数的--[],但是维数在我们理解声明具体含义的时候不是那么重要。典型地我们会把维数包含在声明里。这样可以明了的说“数组且成员为”某些东西。
() 函数且返回(function returning...)
  函数通常是由一对圆括号来表明的--尽管可能在里面发现参数类型。参数列表其实在我们理解声明的时候是不重要的,可以忽略。应该注意到的是函数的括号和分组改变优先级的括号是不一样的:分组括号总是包围变量而函数括号却总是在变量名(即函数名)的右边。

  如果函数不返回值则它是无意义的(我们可以使用void来表示返回为空)。

一个派生类型总是修饰一个基本变量或者另外一个派生变量。我们在理解声明的时候一定要包括介词(to,of,returning)。说“指针”而不是“指针且指向...”会导致你的声明分离。

操作符的优先级
几乎每一个c程序员都很熟悉操作符的优先级表。比如乘除的优先级要高于加减,但是括号可以改变这个优先级。这个对于通常的表达式很自然,但是这个规则也可以运用到声明语句中--它们是类型表达式而不是计算表达式。

数组符号[]和函数符号()比指针*有更高的优先级,这样我们可以推导出一些规则(这个规则和上边所提的是一样的,这里不再啰嗦,离子的推衍也不再重复)。

抽象声明

C标准也支持抽象声明。它可以用在需要类型描述但是又不需要实际变量的情况下--cast,抛掷,和作为sizeof的参数。比如:
int (*(*)())()

对于上边的问题--从哪里开始,答案是找到一个变量名可以放得下的地方,然后把它当成一个通常的声明。只有一个地方可以放置这个变量名并且找到它也是直截了当的。遵循以下语法规则:
 - 在*的右边
 - 在[]的左边
 - 在函数操作符()的左边
 - 在分组括号()的内部

由上面的例子我们可以看到有两个地方符合前三条规则:
int (*(* • ) • ())()

但是如果考虑则四条,则只有左边的位置符合了。
int (*(*foo)())()

它表示:
foo是 指针且指向 函数且返回 指针且指向 函数且返回 int

语义限制

注意不是所有的派生组合都被允许的.这样就可能声明一个合乎语法却没有语义内涵的东西来:
 --不能有 数组且成员为函数
   使用 数组且成员为 指针且指向 函数且返回.... (译注:但是我们知道函数的地址就是函数本身,所以可以说 数组且成员为 函数指针且返回)
 --函数不能返回函数
   同样使用函数指针代替
 --函数不能返回数组
   可以使用 函数且返回 指针且指向 数组且成员为...(译注:同样地我们知道数组名同时也是数组首元素的地址,还是该数组的地址)
 --数组中只有最左的[]可以是未定义的
 --void是限制类型,它只有在和指针*以及函数()在一起时才有效.


     

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值