C/C++之数据类型

Tips:
 1. 本人当初学习C/C++的记录。
 2. 资源很多都是来自网上的,如有版权请及时告知!
 3. 可能会有些错误。如果看到,希望能指出,以此共勉!

C数据类型

C数据类型
ANSI C99标准中规定的数据类型如下图所示。
C99规定
说明:

  • 同一行类型的语义相同,即可以相互替代。
  • long float类型与double相同,故在C99标准中没有此类型。
  • 部分编译器也提供了unsigned float和unsigned double,最好不要使用,以免影响程序的可移植性。
  • int默认是signed,所以int, signed, signed int三者等价。其它unsigned的情况类似。char默认情况不确定。

C语言中数据类型的长度

  ANSI C99标准中定义了两类(四个)类型修饰符:long/short和unsigned/signed。C99标准规定,long类型不能比变通类型短,short类型不能比普通类型长。而unsigned与signed的区别在实现上是有无符号的区别,而是使用上是取值范围的区别,两者表示范围相同,但前者全是正数,后者关于0对称。
说明:

  • long/short可以修饰int,long还可以修饰double。
  • unsigned/signed可以修饰int、char,不可以修饰浮点型。
  • int长度是机器的字长,short int是半个字长,long int是一个或两个字长。
  • unsigned/signed长度与普通类型一样,只是表示区间不同。

C语言中数据类型的转换

类型转换分为显示和隐式两种,前者比较简单,这里只讲后者。下面是C99标准中给出的各种类型对象的基本转换规则:

  • 枚举常量: 转换成int,如超出int范围,则转成long int等
  • 浮点型:
    • 如果转成整类型,只保留整数部分,如果超出整型表示范围,则转换错误;
    • 如果向上转成double/long double,值不变;
    • 如果向下转成float/double等,如果能用float/double表示,则正常,如果超出表示范围,则转换错误,而如果在表示范围内,但精度降低,则要依赖于编译器的处理了
  • 整型: short int/char/枚举类型/位类型都可转换成int,如果超出int表示范围,则提升到unsigned int。
    对于二元运算符中的普通算术运算转换,C99标准给出了如下图所示的转换规则:
    转换规则
    说明:

  • 对于unsigned char和unsigned short int的整型提升问题,C99标准给出“保值”的转换方法:方法是将unsigned char和unsigned short int转换成int,如果超出表示范围,则转成unsigned int。

  • 对于表格中第五行,long int与unsigned int的情况,在vc6.0没有这样实现,是直接转成unsigned int。

C++数据类型

  面向对象编程(OOP)的本质是设计并扩展自己的数据类型。C++兼容C的数据类型,又稍有区别。当然,下面的数据类型说明多数同样适用于C类型,部分不同于C的地方将单独指出。

基本类型

  基本类型主要就是整型和浮点型,同时对这两种进行了多种变形。(C++11新增了bool类型,兼容C99)

整型

  基本整型包括:char、short、int、long和C++11中新增的long long(兼容C99)。其中每种类型都有有符号版和无符号版。
1. 每种数据类型都有一定的数据范围,不同的系统可能有不能的范围。C++采用灵活的标准,确保了数据类型的最小长度(C语言可能不同):

  • short 至少16位
  • int 至少与short一样长
  • long 至少32位,且至少与int一样长
  • long long 至少64位,且至少与long一样长
    在头文件climits(旧limits.h)中包含了关于整型限制的信息。如下图(VS2010):
    climits
    数据类型的范围是怎么算出来的。(C及C++)
  • 计算机中数据都是以二进制存储;
  • 二进制可以由不同的编码(原码、补码、反码)表示,计算机统一采用补码表示。
  • 计算机中的正负号,0表示正数,1表示负数
    以2字节(16位)有符号类型来说:
    范围: (10进制)-32768到32767;(16进制)8000到7FFF;(2进制补码)1000,0000,0000 ,0000到0111,1111,1111,1111
    原码:最高位为符号位,其余位与正常二进制表示方法一致;
    原码表示范围:
    最大为0111111111111111 = 2^15-1 = 32767
    最小为1111111111111111 = -2^15-1 = -32767
    +0和-0:
    +0:0000000000000000
    -0:1000000000000000
    即正零与负零表示方法不同。也就意味着:原码能表示的有符号数范围是:-32767~-0和0~32767
    补码: 正数补码与原码相同,负数补码需要把除符号位以外的原码取反加1
    补码表示范围:
    最大为0111111111111111 = 2^15-1 = 32767
    最小为1000000000000001 = 原码:1111111111111111的符号不变其余取反为:1000000000000000,再加1为:1000000000000001
    +0和-0:
    +0:0000000000000000 // 与原码相同
    -0:0000000000000000 // 原码:1000000000000000的符号不变其余取反为:1111111111111111,再加1为:0000000000000000(进位舍掉)
    也就是正0和负0在补码系统中的编码是一样的。也就是补码会比原码多一个编码出来,这个编码就是1000000000000000。因为任何一个原码都不可能在转成补码时变成1000000000000000。所以,人为规定1000000000000000这个补码编码为-32768。所以,补码系统中,范围是-23768~32767。
    范围
    1. C++中,对于变量的赋值更加灵活,原来用于结构体和数组的赋值,现在对于单个变量也是可以的。
      C++
    2. C++如何确定整型常数的类型
  • 对于有字符修饰的整型数字,根据字符来判断类型。例如:123L,则为long;123uL为unsigned long。
  • 对于没有符号修饰的十进制数字,C++总是采用int、unsigned int、long、unsigned long、long long (没有short)中能够存储该数的最小类型表示。
  • 对于没有符号修饰的十六进制或者八进制,则总是以对应的无符号类型表示。
    常量

浮点型

C++基本浮点型包括:float、double、long double。unsigned和signed不能修饰浮点型
C/C++对于浮点型有效位的规定:

  • float至少32位
  • double 至少48位,且不少于float
  • long double 至少和double一样多

注意:默认情况下,浮点常数字为double型,例如程序中直接写1.0,其被当做double型数字。

在头文件cfloat(旧float.h)中包含了关于浮点型限制的信息(有些系统没有提供该文件)。如下图:
float.h
从上表中可看到,在VS2010中,double的有效位数为15,float的有效位数为6
测试

浮点数的存储

  C/C++编译器都是按照IEEE的浮点数表示法,即一种科学计数法,用符号、指数和尾数来表示,底数为2。也就是把浮点数表示为尾数乘以2的指数次方再添加上符号的形式。因为科学技术法 a×bm的形式,a介于1~10,而浮点数表示法中,a始终为1,所以在最终的表示结果中,这个1被略去。即:尾数二进制最高位的1不要
具体规格是:

类型符号位阶码尾数总长度
float182332
double1115264

下面通过例子来解释上面的表示规格:

  • 38414.4表示为double:
    • 分开整数和小数部分,整数化为16进制,0x960E;小数部分为:0.4=0.5×0+0.25×1+0.125×1+……+0.5×(1 or 0)/n+……。//实际上这永远算不完!
    • 有的小数可以穷尽,有的是永远不会穷尽的,此时只需要提取出各项的系数,即011……,这些项的和加上整数部分共53位就可以了。正如上面所言的,最高为不变的1可以省略(归一化),最终是53-1=52位。
    • 38414.4可以表示为1001011000001110.0110011001100110011001100110011001100B。
    • 用科学计数法表示为1.0010110000011100110011001100110011001100110011001100×215。
    • 然后计算阶码,阶码共11位,可以表示-1024~1023,因为指数可以为负数,规定先加上1023变为非负数(指数偏移),上面的15表示为15+1023=1038,二进制为10000001110。符号位,0为正,1为负。所以最终结果是
    • 0 10000001110 0010110000011100110011001100110011001100110011001100
    • 颜色与上表对应。
  • 3490593表示为float:
    • 3490593的浮点数为3490593.0。
    • 整数化为二进制,为1101010100001100100001B,即1.101010100001100100001×221,由于float的尾数有23位,需要补0。即1.10101010000110010000100×221。
    • 计算阶码时,类似double的表示,阶码共8位,表示的范围是-128~127,为了方便,加上127,上面的21表示为21+127=148=10010100B。最终结果是:
    • 0 10010100 10101010000110010000100
    • 颜色与上表对应。
  • 0.5的二进制表示:
    • 上面给出了0.4的二进制表示的计算方法:
    • 0.4 = 0.5×0+0.25×1+0.125×1+……+0.5×(1 or 0)/n+……。
    • 它是无穷尽的,直到精度合适了为止。
    • 然而对于有的数来说,是有穷的,比如0.5=1×0.5。整数部分为0,小数部分为0.1,所以0.5的二进制形式是0.1,即1.0 × 2-1。
    • 计算阶码时,用127+(-1)=126=b1111110B。所以最终结果是:
    • 0 01111110 00000000000000000000000
    • 颜色与上表对应。
  • -12.5的二进制浮点表示:
    • 整数部分为12,即1100B;小数部分为0.5,即0.1B,即1100.10000000000000000000,即1.10010000000000000000000 × 23。
    • 计算阶码,3+127=130,即10000010B,所以最终结果是:
    • 1 10000010 10010000000000000000000
    • 颜色与上表对应。
  • 逆向求取,1011 1101 0100 0000 0000 0000 0000 0000转为十进制:
    • 1011 1101 0100 0000 0000 0000 0000 0000为:
    • 1 01111010 10000000000000000000000
    • 所以该数为-1.10000000000000000000000 × 201111010-127=-5 = -0.000011B = 0.046875
      详细见http://blog.163.com/yql_bl/blog/static/847851692008112013117685/
      有了以上知识,那么printf(“%f”,10/3);的结果是什么?结果是0.0000
      10/3的结果无疑应该是3,但是,我们却要求printf按照浮点数来去这个数,通过以上我们知道,整数和浮点数的存储方式是不一样的。
      整型数3在内存存储如下:
      0000 0000 0000 0000 0000 0000 0000 0011
      但是现在我们要用浮点数的方式来解析这32位数字。按照浮点数方式:
      0000 0000 0000 0000 0000 0000 0000 0011
      上面红色是符号为0,表示正数;蓝色的是指数位,结果为0,但是这儿要注意的一点是指数在存储的时候是进行过偏移的,所以这儿要剪掉127,所以指数为-127。最后的紫色是尾数,结果是2^(-22)+2^(-23),但是也要注意一点是,尾数在进行存储的时候是归一化过的,小数点前面其实有个1,所以最后尾数是1+2^(-22)+2^(-23)。所以最后的浮点数是:[1+2^(-22)+2^(-23)]*2^(-127)转化为可读数字就是5.87747385606e-39 ,这个数就非常小了,所以显示的时候就是0.000000啦。

数据类型转换

隐式类型转换

在某些情况下,C++将自动对数据类型进行转换:

  • 不同数据类型之间的赋值 例如:int a = 1.0;目标类型是被赋值对象的类型。
  • 算数表达式中存在不同数据类型的数运算,例如int a = 1;double b = 2.0; int c = a+b;
  • 将一个表达式作为实参传递给函数调用,此时形参和实参类型不一致:目标转换类型为形参的类型
  • 从一个函数返回一个表达式,表达式类型与返回类型不一致:目标转换类型为函数的返回类型
    C++11表达中,不同数据进行运算时的校验规则(与C语言稍有区别):
  • 如果其中有一个数为long double,那么另一个就被转换为long double
  • 否则,如果有一个数double,那么另一个就被转换为double
  • 否则,如果有一个数为float,那么另一个数就被转换为float
  • 否则,否则说明操作数都是整型,执行整型提升
  • 如果两个操作数同是有符号或同是无符号,这转换为等级较高的类型进行运算
  • 如果一个有符号一个无符号,且无符号类型级别高,这转换无符号数运算
  • 否则,如果有符号可以表示所有无符号取值,这转换为有符号类型运算
  • 否则,将两个数都转换为有符号数的无符号版本运算
    C++

显示类型转换

(typename)value;  // C语言风格
typename(value);  // C++风格

此外,C++还提供了四个关键字来实现转换,与传统强制转换相比,其转换更加严格

static_cast <type-id> (expression); //该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性。
dynamic_cast <type-id> (expression); //该运算符把expression转换成type-id类型的对象。Type-id必须是类的指针、类的引用或者void *;如果type-id是类指针类型,那么expression也必须是一个指针,如果type-id是一个引用,那么expression也必须是一个引用。
reinpreter_cast <type-id> (expression); // type-id必须是一个指针、引用、算术类型、函数指针或者成员指针。它可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针(先把一个指针转换成一个整数,在把该整数转换成原类型的指针,还可以得到原先的指针值)
const_cast<type_id> (expression);    // 用来修改类型的const或volatile属性。除了const 或volatile修饰之外, type_id和expression的类型是一样的。

转换

显示对浮点数进行强制转换时,规则如下:

较大浮点转较小浮点,例如double转float精度降低,如果超出float范围,结果不确定
浮点型转整型小数被省略,如果超出float范围,结果不确定
较大整型转较小整型,例如long转int如果超出int范围,通常只复制右边的值

潜在的数据转换问题

复合类型

复合数据类型由基本数据类型组成,C++中类就是一种符合数据类型。此外数组、字符串、结构体、共同体、枚举、指针和自由存储空间都作为复合数据类型

数组(C/C++)

  1. 如果只对数组部分赋值,后面的默认为零。
    数组

  2. 不能将一个数组赋值给另一个数组。

指针和二维数组(C/C++)

指针

首先,指针也是一个变量,其在内存中一般占用4个字节。比较特殊的是,指针变量中存放的值是一个地址。例如:
int *p;
这里,定义了一个指针,编译器在内存中拿出4个字节,名字叫p,里面存放一个4字节的地址。对于未初始化的指针,其值是随机的,很危险!
常见的指针操作:*与++、–

int a=2, b=5, c, d, *p; 
p = &a;
 (*p)++;    // 等价于a++
c = *p++;   // 等价于 c = *p; p++; 两句
d = *++p    // 等价于 p++; d = *p; 两句

指针和引用的联系与区别(仅C++)
(1)指针是一个实体,而引用仅是个别名;
(2)引用使用时无需解引用(*),指针需要解引用;
(3)引用只能在定义时被初始化一次,之后不可变;指针可变;
(4)引用没有 const,指针有 const;
(5)引用不能为空,指针可以为空;
(6)“sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身(所指向的变量或对象的地址)的大小;
(7)指针和引用的自增(++)运算意义不一样;
(8)从内存分配上看:程序为指针变量分配内存区域,而引用不需要分配内存区域。
在说明指针的时候,有必要额外说明一下二维数组。

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

二维数组

  有很多地方说数组就是指针,这是错误的一种说法。这两者是不同的数据结构。其实,在C/C++中没有所谓的二维数组,书面表达就是数组的数组。为了表述方便才叫它二维数组。二维数组在概念上是二维的,即其下标在两个方向上变化,下标变量在数组中的位置也处于一个平面之中,而不是像一维数组只是一个向量。但是,实际的硬件存储器却是连续编址的,也就是说存储器单元是按一维线性排列的。如何在一维存储器中存放二维数组,可有两种方式:一种是按行排放, 即放完一行之后顺次放入第二行。另一种是按列排放, 即放完一列之后再顺次放入第二列。
  在C语言中,二维数组是按行排列的。即,先存放a[0]行,再存放a[1]行,最后存放a[n]行。每行中的元素也是依次存放。例如对数组a[5][3]赋值两种方式(结果完全相同):

  • 按行分段赋值可写为:
    int a[5][3]={ {80,75,92}, {61,65,71}, {59,63,70}, {85,87,90}, {76,77,85} };
  • 按行连续赋值可写为:
    int a[5][3]={ 80,75,92,61,65,71,59,63,70,85,87,90,76,77,85};
    注意:
    1. 可以只对部分元素赋初值,未赋初值的元素自动取0值。
    2. 如果对全部元素赋初值,则第一维的长度可以不给出,例如:int a[][3]={1,2,3,4,5,6,7,8,9};

二维数组一维化

  我们可以用一个指向int型的指针变量来访问这个数组,下面的代码是将数组一维化(以上面的a数组为例):

int *p = a[0];          // 这样就可以用 p 访问每个元素了
p[3]             // 第三个元素
*(p+3)           // 这个 = p[3]

这样就实现了将二维数组一维化,通过p访问的是每个元素,而不是行

数组指针和指针数组

指针数组: 指针数组就是个数组,只不过元素是指针。定义方式如:int *p[3]; 表示三个指针,分别为:p[0]、p[1]、p[2]
数组指针: 指向数组的指针。定义方式如:int (*p)[3]; 表示 p指向的是一个数组元素为int类型并且数组元素的个数为3的一个指针。
数组指针

int (*pArr)[3] = a;
*(*(pArr+1) + 2)             //  这就相当于 a[1][2]

上例中,pArr是个数组指针,每次+1是移动一行,不是一个元素。比如说,pArr+1代表的现在指针已经指向第一行元素了(0行开始),而要取得指针所指的对象,就要用到解引用运算符,所以(pArr+1)就代表第一行数组,是整个这一行元素就取到了,那现在要取这一行的第二个元素,只须将指针再移动两个元素,即*(iArr+1) + 2,这样就指向了这个元素的地址,再解引用取得元素的值即可。
这里写图片描述
也许我们应该这样来数组指针:
int (*)[10] p2;
int (*)[10]是指针类型,p2 是指针变量。这样看起来的确不错,不过就是样子有些别扭。其实数组指针的原型确实就是这样子的,只不过为了方便与好看把指针变量p2 前移了而已。
既然这样,那问题就来了。现在再来看看下面的代码:

int main()
{
   char a[5]={'A','B','C','D'};
   char (*p3)[5] = &a;
   char (*p4)[5] = a;       //必须使用强制转换,如:char (*p2)[5]=(char (*)[5])a;
   return 0;
}

上面对p3 和p4 的使用,哪个正确呢?p3+1 的值会是什么?p4+1 的值又会是什么?
毫无疑问,p3 和p4 都是数组指针,指向的是整个数组。&a 是整个数组的首地址,a是数组首元素的首地址,其值相同但意义不同。在C 语言里,赋值符号“=”号两边的数据类型必须是相同的,如果不同需要显示或隐式的类型转换。p3 这个定义的“=”号两边的数据类型完全一致,而p4 这个定义的“=”号两边的数据类型就不一致了。左边的类型是指向整个数组的指针,右边的数据类型是指向单个字符的指针。在Visual C++6.0 上给出如下警告:
warning C4047: 'initializing' : 'char (*)[5]' differs in levels of indirection from 'char *'。
还好,这里虽然给出了警告,但由于&a 和a 的值一样,而变量作为右值时编译器只是取变量的值,所以运行并没有什么问题。不过我仍然警告你别这么用。
但是如果修改一下代码,把数组大小改小点,会有什么问题?p3+1 和p4+1 的值又是多少呢?

int main()
{
   char a[5]={'A','B','C','D'};
   char (*p3)[3] = &a;
   char (*p4)[3] = a;
   return 0;
}

或把数组大小改大点:

int main()
{
   char a[5]={'A','B','C','D'};
   char (*p3)[10] = &a;
   char (*p4)[10] = a;
   return 0;
}

测试结果:把数组大小改变,都会编译不通过。

地址的强制转换

以下,以x86 Windows为例

#include <stdio.h>
int main()
{
    int a[4]={1,2,3,4};
    int *ptr1=(int *)(&a+1);
    int *ptr2=(int *)((int)a+1);
    printf("%x,%x",ptr1[-1],*ptr2);
    return 0;
}

下面分析上面的数据结果
分析
ptr1: a为数组名,那么&a+1不是增一个int,而是(int*)(a的地址+sizeof(a)),因此ptr1指向了数组结尾的第一个字节。
可以这样理解:不管是增1还是减1,这里的1都是sizeof(类型),上面对a取地址,可认为此时类型为int a[4],这里增的是sizeof(a)
ptr2: 任何数值一旦被强制转换,其类型就改变了。这里实际上就是将地址a,转换为了数,然后+1,把转换后的数再次转换为地址。如下图:
结果

字符串

C++有两种风格的字符串,一种是C语言风格的,一种是C++语言风格string。

C语言风格字符串

C语言风格的字符串以空字符结尾,空字符被写作\0,ASCII码为0。C不会检查字符串长度是否越界。
对于C风格的字符串操作一般通过库函数来实现,在头文件string.h中(C++ cstring)包换大量字符串操作的函数。
C字符串

要保证目的字符串可以容纳原字符串,否则,编译不会出错,但是运行时,会出现错误:Stack around the variable ‘xxx’ was corrupted.

C++字符串 String

IOS/ANSI C++98标准添加了String类,使用者可以直接将它作为一种数据类型来用,定义字符串变量(对象)。要使用String类必须包含头文件string,而且string位于std命名空间中。
详细的使用方法见文件: 双击图标查看!

结构体

无论在C还是C++中,结构体都是很常用的一种数据类型。结构体名,用作结构体类型的标志,它又称结构体标记。大括号内是该结构体中的成员列表,又称为域表。

结构体的内存对齐

结构体内存分配的原则:编译器按照成员列表顺序一个接一个地给每个成员分配内存。只有当存储成员需要满足正确的边界对齐要求时,成员之间才可能出现用于填充的额外内存空间。如果不按照平台要求对数据存放进行对齐,会带来存取效率上的损失。此外,合理利用字节对齐还可以有效地节省存储空间。但要注意,在32位机中使用1字节或2字节对齐,反而会降低变量访问速度。因此需要考虑处理器类型。还应考虑编译器的类型。在VC/C++和GNU GCC中都是默认是4字节对齐。
结构体字节对齐的细节和具体编译器实现相关,但一般而言满足三个准则:
1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
2) 结构体每个成员相对结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节{trailing padding}。

位域(位段)

有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态, 用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几个不同的区域, 并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。 这样就可以把几个不同的对象用一个字节的二进制位域来表示。定义方式如下:

struct 位域结构名
{
类型说明符 位域名:位域长度
};
例:
struct bs
{
  int a:8;          // 8个二进制位
  int b:2;          // 2个二进制位
  int c:6;          // 6个二进制位
}; 

位域需要遵循以下规则:
1. 位域的长度不能大于数据类型本身的长度,比如int类型就能超过32位二进位。有其他人说是不能超过8位,我在我的机子上是可以实现int :32的位域长度的。
2. 位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的
3. 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
4. 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
5. 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式(不同位域字段存放在不同的位域类型字节中),Dev-C++和GCC都采取压缩方式;
6. 如果位域字段之间穿插着非位域字段,则不进行压缩
7. 整个结构体的总大小为最宽基本类型成员大小的整数倍
8. C99规定int、unsigned int和bool可以作为位域类型

系统会先为结构体成员按照对齐方式分配空间和填塞(padding),然后对变量进行位域操作。

举例如下:

#include <iostream>
#include <memory.h>

using namespace std;
struct A
{
    int a:5;
    int b:3;
};
int main(void)
{
    char str[100] = "0134324324afsadfsdlfjlsdjfl";
    struct A d;
    memcpy(&d, str, sizeof(A));
    cout << d.a << endl;
    cout << d.b << endl;
    return 0;
}

如上代码,执行结果如下:

分析:
高位 00110100 00110011 00110001 00110000 低位
‘4’ ‘3’ ‘1’ ‘0’ // 以上二进制位字符的ASCII码
其中d.a和d.b共同占用低位一个字节(00110000), d.a : 10000, d.b : 001
然后,int 是有符号的。所以d.a对应的数为11111111 11111111 11111111 11110000;d.b对应的二进制为10000000 00000000 00000000 00000001
同理,如果int a:5改为了int a:16,此时,d.a对应的值就是10000000 00000000 00110001 00110000

共同体(联合体)

共同体是一种数据格式,它能够存储不同的数据类型,但同时只能存储一种。
匿名共同体:定义时,直接省去共同体的名称,但这里一般同时定义一个对象,因为没有名字以后就没法定义了!除非是放在其他结构里面,可以不定义对象。
共同体
关于共同体的嵌套
嵌套

注:结构体与联合体有何区别?

  1. 结构体变量所占内存长度是各成员占的内存长度的总和(不考虑内存对其)。联合体变量所占内存长度是最长的成员占的内存长度。
  2. 结构体和联合体都是由多个不同的数据类型成员组成,但在任何同一时刻,联合中只存放了一个被选中的成员(所有成员共用一块地址空间), 而结构的所有成员都存在(不同成员的存放地址不同)。
  3. 对于联合的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了,而对于结构的不同成员赋值是互不影响的。

枚举

1、枚举值默认从零开始,后面的比前面的增加1
2、C早期版本规定,枚举赋值必须是int型,现在该限制被取消了,赋值可以是long、long long
3、可以定义具有相同值的枚举值
在C++98中enum变量的实际大小由编译器决定,只要能够保存enum的成员即可,而在将要发布的新的C++0x中,可以指定enum的实际实现类型,如实现为int类型。
enum Month:int{ Jan, Feb, …, Dec }
枚举

其他类型:自由存储

单独说明

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZC·Shou

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值