第5 章 数据类型 数据是程序处理的对象,数据可以依据自身的特点分成不同的类型。计算机内部只有 二进制比特位,取值只能是0或1。计算机科学家设计了多种数据类型,用0和1表示各种 不同类型的数据。 5.1 数据类型概述 使用C++语言编写程序时,需要用到各种变量来存储各种信息。变量名保留的是它所 存储的值的内存位置。当定义一个变量时,就会在内存中分配一定的存储空间。C++提供 了种类丰富的内置数据类型和用户自定义数据类型,如图5-1所示。 图5-1 C++数据类型 C++提供的基本数据类型有整型、浮点型、布尔型、空类型、空指针类型。用户自定义 的构造类型有枚举、数组、结构、联合、类、位域。C++提供了不同长度的有符号整数和无符 号整数。C++浮点数类型使用IEEE-754标准表示实数的近似值。 58 C++程序设计 指针是一个变量,其值为另一个变量的地址,即内存位置的直接地址。不同类型的指针 变量保存不同类型的内存地址。将整数变量的地址取出来,可以保存在整数指针中;将整数 指针变量的地址取出来,可以保存在指向整数指针的指针中;将函数的地址取出来,可以保 存在函数指针中;将对象的地址取出来,可以保存在对象指针中。 引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字。一旦把引用初始 化为某个变量的别名,就可以使用该引用来访问那个变量。常引用可以引用一个常量。对 象的引用是一个对象的别名。 C++中,还可以使用typedef关键词为已有的数据类型定义别名,通过给出有具体意义 的别名,可以使程序中的类型使用更明确。 5.2 指针和引用 指针(Pointer)是一个整数,保存的是另外一个变量的地址,即它指向内存中的另一个 地方。引用(Reference)是某一个变量的一个别名,对引用的操作与对原变量直接操作完全 一样,引用必须在定义时初始化。 如果想表示指针不指向任何位置,C++提供空指针nullptr,它是std∷nullptr_t类型的 常量。C++兼容C语言的空指针NULL。nullptr和NULL的区别:nullprt是指针类型, 而NULL是整数类型。nullptr和NULL都等价于0。 例5.1 理解指针。 //Pointer.cpp #include using namespace std; int main(int argc, char**argv){ int n = 100; int *p = &n; //& 取地址*定义指针 *p = 200; //*解引用符 cout << n << " " << *p << endl; //200 200 return 0; } 【运行结果】 200 200 【代码解读】 程序在主函数中定义了整数变量n,并初始化为100。“int*p”是定义了一个整数指针 变量p,可用于保存整数变量的地址。p是一个正在被定义的变量,此时* 的作用是定义 指针变量。作为一个已经存在的变量n,“&n”的含义是取变量n的内存地址,此时& 是取 地址运算符。程序取变量n的地址,作为指针变量p的初始化值。语句“*p=200;”中,* 后面是一个已经存在的指针变量,此时*是解引用运算符,即通过整数指针p间接访问它所 指向的变量n,结果是将变量n赋值为200。 例5.2 理解引用。 //Reference.cpp #include 第5 章 数据类型 59 using namespace std; int main(int argc, char**argv){ int a = 300; int &b = a; //& 用来定义引用, b 是a 的别名, 定义引用时必须初始化 b = 500; cout << a << " " << b << endl; //500 500 return 0; } 【运行结果】 500 500 【代码解读】 程序在主函数中定义了整数变量a,并初始化为300。语句“int&b=a;”定义了引用变 量b,将a作为引用的初始化值,b成为a的别名。b是一个正在被定义的变量,此时& 的作 用是定义引用变量。语句“b=500;”执行后,变量a的值也被修改成了500,因为b就是a。 例5.3 理解指向指针的指针。 //PointerPointer.cpp #include #include using namespace std; int main(int argc, char**argv){ int n = 100; int *p = &n; //取变量n 的地址,作为指针变量p 的初值 int**q = &p; //取指针p 的地址, 作为指向指针的指针q 的初值 **q = 600; //2 次解引用访问n*q 得到p**q 得到n cout << n << " " << *p << " " << **q << endl; //600 600 600 cout << sizeof(p) << " " << sizeof(q) << endl; //8 8 //p 是整数指针,类型是int*,指向n printf("memory address of n: %p %p\n", p, &n); //q 是指向整数指针的指针,类型是int**,指向p printf("memory address of p: %p %p\n", q, &p); return 0; } 【运行结果】 600 600 600 8 8 memory address of n: 000000F126AFF7D4 000000F126AFF7D4 memory address of p: 000000F126AFF7F8 000000F126AFF7F8 【代码解读】 程序在主函数中定义了整数变量n,并初始化为100;取整数变量n的地址,作为整数指 针变量p的初始化值;取指针p的地址,作为指向整数指针的指针q的初始化值。指针p的 数据类型是int*,指针q的数据类型是int**。 60 C++程序设计 对于已经存在的指针q,*q是解一次引用得到p,**q是解两次引用得到n。因此,语 句“**q=600;”将变量n的值修改为600 。 输出语句中,n、*p、**q访问的都是整数变量n,因此输出三个600 。 指针变量自身是一个无符号整数,占用的字节数取决于程序编译成32位应用,还是编 译成64位应用。如果程序编译成32位应用,每个指针变量占用4字节内存。如果编译成 64位应用,每个指针变量占用8字节内存。指针变量的值是一个内存地址,是无符号的整 数,可以使用格式控制符%p输出这个值。 如果将程序编译成X64指令系统CPU的应用程序,则程序执行了main函数中前4行 代码后,n、p、q之间的关系如图5-2所示。n是一个有符号整数变量,占4字节。X64指令 系统采用小端(LitleEndian)格式存放多字节数据。LitleEndian格式是在低字节中存放 数据的低位有效字节。变量n的4字节的值依次是58 、02 、00 、00,理解这个数的时候要从 高到低按字节倒着看才行,即00 、00 、02 、58 。0x00000258就是十进制数600 。 图5- 2 指向指针的指针 变量n在内存中的地址是000000F126AFF7D4 。将变量n的地址取出来保存在整数指 针变量p里面。指针p的8字节的值依次是D4 、F7 、AF 、26 、F1 、00 、00 、00,理解这个数的时 候也需要从高到低按字节倒着看。 指针p作为一个变量,保存在内存中,也是有地址的:000000F126AFF7F8 。取指针p 的地址保存在变量q中,则变量q是一个指向指针的指针。 3 ASCI 码 5. ASCI(AmericanStandardCodeforInformationInterchange)即美国信息交换标准码, 是一套标准的单字节英文编码方案。它是由美国国家标准学会(AmericanNational StnadIsiue,制定的, ItrainlOgnztoo adrntttANSI) 后来它被国际标准化组织(nentoaraiainfr StnadztoISO) 称为ISO646标准。 adriain,定为国际标准, 标准ASCI 码使用7位二进制数来表示128个字符,如表5-1所示。它使用7位二进 制数来表示所有的大写和小写字母、数字0~9、标点符号,以及特殊控制字符,取值范围是 00000000~01111111(0~127),其中0~31和127是控制字符或通信专用字符,其余为可 显示字符。扩展ASCI 码是最高位为1的8位二进制数,用来表示特殊符号字符、外来语 字母和图形符号,取值范围是10000000~11111111(128~255 )。 第 5 章 数据类型61 62 C++程序设计 例5.4 标准ASCII码中的可显示字符。 //ASCII.cpp #include int main(int argc, char**argv){ for(int i=32; i<=126; i++){ printf("%3u %02X %c\n", i, i, i); } return 0; } 【代码解读】 程序将整数32~126以三种形式输出:无符号十进制整数、十六进制数、字符。%3u表 示占3个字符宽度的无符号十进制整数。%02X 表示占2个字符宽度的无符号大写十六进 制整数,不足2位填0。%c表示一个字符。十进制数32~126对应的二进制数是00100000 ~01111110,对应的十六进制数是20~7E,这个范围是ASCII码的可显示字符。比如,32 是空格,48是零,126是波浪线。 0010 0000 = 32 = 0x20 = ' ' 空格 0011 0000 = 48 = 0x30 = '0' 零 0111 1110 = 126 = 0x7E = '~' 波浪线 例5.5 英文字母、数字、换行的ASCII码。 //Character.cpp #include using namespace std; int main(int argc, char**argv){ //英文字母的ASCII 编码 if('A'==65){ //0100 0001 cout << "A 的编码是65\n"; //会执行 } cout << (char)97 < using namespace std; int main(int argc, char**argv){ char data[100]= "5678"; //字符数组 char *p=data; //字符指针p 指向第一个字符'5' int s = 0; //转换后的整数 while(*p){ //同*p!='\0',遇到0 的时候停止 s = s*10 + (*p-'0'); //'0'等于48 p++; //指针指向下一个字符 } cout << "s = " << s << endl; cout << "2*s = " << 2*s << endl; return 0; } 64 C++程序设计 【运行结果】 s = 5678 2*s = 11356 【代码解读】 变量data是一个拥有100个字符的字符数组,里面保存的是53、54、55、56、0,后面还有 95字节的0。53是字符5' '的ASCII码,54是字符6' '的ASCII码,55是字符7' '的ASCII码,56 是字符8' '的ASCII码。 C语言中,空字符NUL被定义为字符串的结束符。空字符是ASCII码中的第一个字 符,编码是00000000,使用转义字符\' 0'表示,和整数0相等。常量字符串"5678"的末尾有 一个隐含的空字符\' 0',也就是一字节中每个比特都是0的字符。 C的语法允许在定义字符数组时使用常量字符串初始化字符数组。程序中使用常量字 符串"5678"初始化字符数组data,编译器是将53、54、55、56和隐含的\' 0'复制到了字符数组 中前5个元素的位置。 数组作为局部变量定义时,如果没有初始化,数据是不确定的。如果程序给出了部分初 值,编译器会将剩余的空间清零。data是100个字符的字符数组,前5个字符使用常量字符 串"5678"初始化,后面95个字符编译器负责清零。"5678"是位于全局数据区的常量字符串, 是不可以被修改的。data是主函数内部定义的局部变量,是一个拥有100个字符的数组,是 可以被修改的。局部变量data占用的内存空间在栈上分配,每个线程拥有一个栈。 要计算2*5678的值,需要将字符串转换为整数。如图5-3所示,指针p首先指向字符 数组中的第一个字符,然后逐个指向后续的每一个字符。循环条件是*p,其中* 是解引 用运算符,指针变量p的值是某个字符的内存地址,*p表示取p指向的字符。while循环共 执行了5次,每次执行的判断分别是while(53)、while(54)、while(55)、while(56)、while(0), 当判断到while(0)时循环结束。 图5-3 遍历字符数组中的字符串 在循环内部,*p-0' '将p指向的字符转换成对应的数值,并将得到的数值作为整数s 新的个位。p+ + 将指针p 指向数组的下一个元素。循环结束后,s的值就变成了整 数5678。 5.4 整 数 整数可以表示某个特定范围的所有整数。int类型是默认的基本整数类型,它默认是有 符号整数。有符号整数使用二进制补码表示负数、零和正数,加上signed修饰符也表示有 第5 章 数据类型 65 符号整数。unsigned修饰符表示只能保存非负值的无符号整数。 大小修饰符指定整数类型的宽度,即位数。C++支持short、long和longlong修饰符。 short类型至少16位,long类型至少32位,longlong类型至少64位。 使用signed、unsigned或大小修饰符时,可以省略int关键字。修饰符和int类型(如果 存在)可以按任何顺序显示。例如,shortunsigned和unsignedintshort指同一类型。编译 器将以下类型组视为同义词。 ● short、shortint、signedshort、signedshortint ● unsignedshort、unsignedshortint ● int、signed、signedint ● unsigned、unsignedint ● long、longint、signedlong、signedlongint ● unsignedlong、unsignedlongint ● longlong、longlongint、signedlonglong、signedlonglongint ● unsignedlonglong、unsignedlonglongint 在C++主流编译器中,各种整数类型的表示范围如表5-2所示。 表5-2 整数类型 类 型说 明字节数表示范围常量举例 char 字符1 -27~27-1 'A' unsignedchar 无符号字符1 0~28-1 'A' short 短整数2 -215~215-1 (short)100 unsignedshort 无符号短整数2 0~216-1 (unsignedshort)100 int 整数4 -231~231-1 -100 unsignedint 无符号整数4 0~232-1 100 long 整数4 -231~231-1 -100L unsignedlong 无符号整数4 0~232-1 100L longlong 长整数8 -263~263-1 -200LL unsignedlonglong 无符号长整数8 0~264-1 200LL 整数的字面值(Literal)是整数的具体表示形式,C++ 支持十进制(Decimal)、八进制 (Octal)、十六进制(Hexadecimal)的书写形式。编译器负责转换成二进制,计算机内部只有 二进制。编程时,用非0数字开头的数字序列表示十进制数,0开头的数字序列表示八进制 数,0X或0x开头的数字序列表示十六进制数。A、B、C、D、E、F是十六进制中的一位数,A 等于十,B等于十一,C 等于十二,D 等于十三,E 等于十四,F 等于十五。F+1 的值是 0x10,即十六进制中的十六。整数还可以加后缀,后缀有L、l、U、u四种。L和l表示长整 数,U 和u表示无符号整数。如“023l”(小写L)是八进制长整数,等于十进制数19。 char是为处理ASCII码而设计的,只有1字节(8bit),表示范围是-128~+127,其中 非负整数部分0~127表示全部标准ASCII码。unsignedchar的表示范围是0~255。C++ 使用单引号表示一个字符常量,不可显示字符和部分可显示字符需要转义表示。char和 66 C++程序设计 unsignedchar都是整数的子类型,可以作为整数参与运算。例如: char ch = 'A' + 2; cout << ch << endl; //C cout << (int)ch << endl; //67 新式C++ 增加了更多的字符类型:wchar_t、char8_t、char16_t、char32_t。char和 wchar_t的字符编码是不确定的,而char8_t、char16_t、char32_t的编码假设是UTF-8、 UTF-16和UTF-32。 ● wchar_t:宽字符类型,每个wchar_t类型占2字节,16位宽。 ● char8_t:UTF-8字符的类型。 ● char16_t:UTF-16字符的类型。 ● char32_t:UTF-32字符类型。 例5.7 -1等于最大整数。 //integer.cpp #include int main(int argc, char**argv){ int x = 0xFFFFFFFF; std::printf("x = %d\n", x); //十进制有符号整数 std::printf("x = %u\n", x); //十进制无符号整数 std::printf("x = %x\n", x); //十六进制小写a~f std::printf("x = %X\n", x); //十六进制大写A~F return 0; } 【运行结果】 x = -1 x = 4294967295 x = ffffffff x = FFFFFFFF 【代码解读】 内存中只有0 和1,值是多少取决于怎么去理解它。整数变量x 的值初始化为 0xFFFFFFFF,变量x的4个字节的内容是11111111111111111111111111111111。 按int类型理解,x是-1;按unsignedint类型理解,x是232-1,即4294967295。有 符号整数采用二进制补码表示,零和正数的补码是其自身,负数的补码是绝对值相等的正数 按位取反再加1。例如,求-1的补码: 00000000000000000000000000000001 -1的绝对值+1 11111111111111111111111111111110 +1按位取反 +1 加1 11111111111111111111111111111111 -1的补码 例5.8 -128等于+128。 //neg128.cpp #include using namespace std;