草庐IT

第3章 处理数据

木三百川 2023-03-28 原文

说明

看《C++ Primer Plus》时整理的学习笔记,部分内容完全摘抄自《C++ Primer Plus》(第6版)中文版,Stephen Prata 著,张海龙 袁国忠译,人民邮电出版社。只做学习记录用途。

3.1 简单变量

内置的 C++ 类型分两组:基本类型复合类型,本章主要介绍基本类型

3.1.1 变量名

变量命名时,需遵循以下规则:

  • 名称只能使用字母数字下划线
  • 名称第一个字符不能是数字
  • 区分大写字母与小写字母。
  • 名称不能是 C++ 关键字
  • 两个下划线下划线和大写字母打头的名称被保留给实现(编译器及其使用的资源)使用。以一个下划线开头的名称被保留给实现,用作全局标识符。
  • C++ 对名称没有长度限制,但有些平台有长度限制。

倒数第二点原因:使用像 _time_stop_Donut 这样的名称不会导致编译错误,但会导致行为的不确定性,不知道结果将是什么。最后一点需注意:ANSI C (C99标准) 只保证名称中的前 63 个字符有意义(前 63 个字符相同的名称被认为是相同的,即使第 64 个字符不同)。目前比较流行的有 4 种命名习惯:

  1. 小驼峰命名法:第一个单词首字母小写,后面单词首字母大写,如 myVariableName
  2. 大驼峰命名法:又称帕斯卡命名法(Pascal),所有单词的首字母都大写,如 MyVariableName
  3. 匈牙利命名法:变量名 = 属性 + 类型 + 对象描述,如 m_lpctstrStudentName 表示类中一个成员变量(属性m_),类型为指向字符串常量的长指针(类型lpctstr),所指字符串常量用来表示学生姓名(对象描述StudentName)。
  4. 下划线命名法:用下划线分隔单词,如 my_variable_name

3.1.2 整型

计算机语言只能表示所有整数的一个子集,存储时使用的内存量越大,可表示的整数值范围也越大。C++ 的基本整型按内存量宽度排序有 charshortintlong和 C++11 新增的long long,其中每种类型都有符号版本无符号版本,因此总共有 10 种类型可供选择(实际上shortshort int的简称,longlong int的简称,long longlong long int的简称,但一般不使用长名称)。最好在声明变量时就对它的值进行初始化,即将声明语句与赋值语句合并在一起,以防出现未初始化就使用变量的情况,这时变量的值是不确定的。C++ 变量初始化有以下几种方式:

//传统C语言方式
int uncles = 5;

//C++支持的方式
int uncles(5);

//C++98支持的方式
int uncles = {5};

//C++11支持的方式
int uncles = {5};
int uncles{5};

//C++11将变量初始化为0
int uncles = {};
int uncles{};

3.1.3 符号整型

不是在所有的系统中,每种类型的宽度都相同,例如int不总是 32 位,在早期的 16 位操作系统中,int是 16 位,但在后来的 32 位操作系统以及 64 位操作系统中,int是 32 位。C++ 标准只确保了最小宽度:

  • short至少 16 位。
  • int至少和short一样宽。
  • long至少 32 位,且至少和int一样宽。
  • long long至少 64 位,且至少和long一样宽。

想知道某种整型的内存量大小,可以使用sizeof运算符返回类型或变量的内存量宽度,单位为字节(字节的含义也依赖于实现,在一个系统中一个字节可能是 8 位,而在另一个系统中可能是 16 位),对类型名(如int)使用sizeof运算符时,应将名称放在括号里面,但对变量名(如n_short)使用该运算符,括号是可选的。

//对类型名使用sizeof运算符(必要括号)
cout << "int is " << sizeof(int) << " bytes.\n";

//对变量名使用sizeof运算符(括号可选)
cout << "short is " << sizeof(n_short) << " bytes.\n";
cout << "short is " << sizeof n_short << " bytes.\n";

想知道某种整型所能表示的整数范围,一种方式是根据该整型的内存量宽度进行计算,例如:若short为 16 位,则其符号版本可表示的整数范围为 \([-2^{15}, 2^{15}-1]\)\([-32768, 32767]\),无符号版本可表示的整数范围为 \([0, 2^{16}-1]\)\([0, 65535]\),这样计算的原因可参考原码补码反码的相关资料。另一种方式是#include <climits>,这个库文件里面包含了关于整型限制的信息,使用示例如下:

//获得int所能表示的最小整数值
cout << "Minimum int value = " << INT_MIN << endl;

//获得当前系统一个字节占多少位
cout << "Bits per byte = " << CHAR_BIT << endl;

climits文件中将上述限制信息定义为

#define INT_MIN -2147483648

编译指令#define的工作方式与文本编辑器的全局搜索并替换的命令相似,它告诉预处理器:查找独立的标记INT_MIN并将其替换为-2147483648,但它会跳过嵌入的INT_MIN,不会将PINT_MINTM替换为-2147483648。C++ 有一种更好的创建符号常量的方法,就是使用const关键字,这将在后续章节学习。

3.1.4 无符号整型

前面介绍的 4 种符号整型都有对应的无符号版本,仅当数值不会为负时才使用无符号版本:unsigned shortunsigned intunsigned longunsigned long long整型变量的取值如果超过了取值限制,其值将成为另一端点的值。依然以 16 位short为例,其符号版本的最大值加 1 后,值会变成 -32768,最小值减 1 后,值会变成 32767,其无符号版本的最大值加 1 后,值会变成 0,最小值减 1 后,值会变成 65535,这种现象分别称为整型的上溢下溢

3.1.5 选择整型类型

int被设置为计算机处理起来效率最高的长度,若没有非常有说服力的理由选择其他类型,则应首选int。若变量取值不可能为负数,则应首选无符号版本。若变量取值可能超过 16 位整数的最大值,则应首选long类型,这样可确保程序移植到 16 位系统时不会出现问题。若变量取值超过 20 亿(\(2^{31}=2147483648\)),则可选择long long。若存在大型数组且节省内存很重要,可考虑使用short

3.1.6 整型字面值

整型字面值是显式地书写的常量,C++ 能够以三种不同的计数方式来书写整数:基数为 10、基数为 8(老式UNIX版本)、基数为 16(硬件黑客的最爱)。C++ 使用前一(两)位来标识数字常量的基数。如果第一位为 1~9,则基数为10(十进制),例如 93。如果第一位为 0,第二位为 1~7,则基数为8(八进制),例如 042(等于十进制数 34)。如果前两位为 0x 或者 0X,则基数为16(十六进制),例如 0x42(等于十进制数 66)。

//十进制
int chest = 42;

//八进制
int waist = 042;

//十六进制
int inseam = 0x42;

3.1.7 C++ 如何确定常量的类型

数字常量后面可以添加后缀以指明类型:

后缀 常量类型
l , L long
u , U unsigned int
UL , LU , ul , lu , Ul , Lu, uL, lU unsigned long
LL, ll long long
ULL, ull, Ull, uLL unsigned long long

对于不带后缀的十进制整数,使用下面几种类型中能够存储该数的最小类型做默认值:intlonglong long

对于不带后缀的十六进制和八进制整数,使用下面几种类型中能够存储该数的最小类型做默认值:intunsigned intlongunsigned longlong longunsigned long long

3.1.8 char 类型:字符和小整数

char 类型是专为存储字符(如字母和数字)而设计的,通常为 8 位,也用来表示比 short 更小的整型。常用的符号集有 ASCII字符集国际Unicode字符集,例如字符 A 的 ASCII 码是 65,字符 a 的 ASCII 码是 97。另外还有一种特殊的字符:转义字符,常用的转义字符有:

字符名称 ASCII符号 代码表示 十进制ASCII码 十六进制ASCII码
换行符 NL(LF) \n 10 0xA
水平制表符 HT \t 9 0x9
垂直制表符 VT \v 11 0xB
回车 CR \r 13 0xD
反斜杠 \ \\ 92 0x5C
单引号 ' \' 39 0x27
双引号 " \" 34 0x22

可以使用数字转义序列(只支持八进制、十六进制)或符号转义序列来表示转义字符,但数字转义序列与特定的编码方式(如ASCII)相关,而符号转义序列适用于任何编码方式,可读性也更强,比如\n\012\0xA 都表示换行符。C++ 标准还允许实现提供扩展源字符集和扩展执行字符集,这种机制独立于任何特定的键盘,使用的是通用字符名,通用字符名以 \u 或者 \U 打头,\u 后接 8 个十六进制位,\U 后则是 16 个十六进制位,这些位表示的是字符的 ISO 10646 码点。

char 用于表示整型时,其默认情况下既不是没有符号,也不是有符号,这由具体的 C++ 实现来决定。可以显式地对其进行设置:

//默认可能是无符号,也可能是有符号
char fodo;

//显式声明为无符号
unsigned char bar;

//显式声明为有符号
signed char snark;

宽字符类型 wchar_t 可以表示扩展字符集,它的内存量宽度由 C++ 实现来决定,可能是 16 位,也可能是 32 位,可能是有符号的,也可能是无符号的,其底层类型可能是 int 也可能是 unsigned short ,前缀 L 可以用来指明宽字符常量与宽字符串:

//右边为宽字符常量
wchar_t bob = L'P';

//输出宽字符串
std::wcout << L"tall" << endl;

C++11 新增了类型 char16_tchar32_t ,这两者都是无符号的,分别长 16 位、32 位,其底层类型也是一种内置整型,具体底层类型随系统而变化。前缀 u 用来指明 char16_t 字符常量和字符串常量,前缀 U 用来指明 char132_t 字符常量和字符串常量,可使用通用字符名来对它们进行初始化。

3.1.9 bool 类型

ANSI/ISO C++ 标准添加了一种名叫 bool 的新类型,它只有两种取值 true 或者 false,常用于判断语句,内存量宽度一般为 8 位。过去,C++ 和 C 一样,是没有布尔类型的。布尔类型支持隐式转换,任何数字值或指针值都可以转换为 bool 值,转换时任何非零值都被转为 true ,零值转换为 false ,布尔值也可以隐式提升为 int 类型,true 被转换为 1,false 被转换为 0。

//布尔值隐式提升为1
int ans = true;

//布尔值隐式提升为0
int ans = false;

//非零被转换为true
bool ans = -100;

//零值被转换为false
bool ans = 0;

3.2 const 限定符

创建常量的通用格式如下:

const type name = value;

应该在声明常量时就对其进行初始化,如果在声明常量时没有初始化,则该常量的值是不确定的,且无法修改。常量被初始化后,其值就被固定了,编译器将不允许再修改该常量的值。const 常量相比于 #define 常量的好处有三点:明确指定类型作用域限制可用于复杂类型

C++constANSI Cconst 有两点主要区别:C++ 版本有作用域限制、可在C++ 中用来声明数组长度

3.3 浮点数

浮点类型是 C++ 的第二组基本类型,它能够表示带小数部分的数字。

3.3.1 书写浮点数

C++ 有两种方式书写浮点数,一种是常用的标准小数点表示法,另一种是 E 表示法

//标准小数点表示法
12.34;

//E表示法(中间不能有空格)
2.52e+8;
2.52e8;
2.52E+8;
2.52E8;
2.52e-8;
2.52E-8;

3.3.2 浮点类型

ANSI C 一样,C++ 也有 3 种浮点类型:floatdoublelong double。C 和 C++ 对浮点数内存量宽度的要求是:

  • float 至少 32 位。
  • double 至少 48 位,且至少和 float 一样宽。
  • long double 至少和 double 一样宽。

通常,float 为 32 位,double 为 64 位,long double 为 80、96 或128 位。可以通过 #include <cfloat> 获取各类型浮点数所能表示的数值范围,各浮点类型所能表示的精度(有效位数)一般为:float 至少 6 位,double 至少 15 位,long double 至少 18 位。

3.3.3 浮点常量

浮点常量后面可以添加后缀以指明类型(部分老式编译器可能不支持浮点常量后缀):

后缀 常量类型
f , F float
l , L long double

对于不带后缀的浮点常量,默认类型为 double

3.3.4 浮点数的优缺点

与整数相比,浮点数有两大优点:可表示整数之间的值可表示的数值范围更大

与整数相比,浮点数也有两大缺点:运算更慢精度将降低

3.4 C++ 算术运算符

C++ 提供了 5 种运算符来完成基本算术计算:

  • 加法运算符 + 执行加法运算,例如 \(4+20=24\)
  • 减法运算符 - 执行减法运算,例如 \(12-3=9\)
  • 乘法运算符 * 执行乘法运算,例如 \(12*4=112\)
  • 除法运算符 / 执行除法运算,例如 \(1000/5=200\)
  • 求模运算符 % 执行取余运算,两个操作数必须都为整数,它生成第一个整数除以第二个整数后的余数,计算结果满足等式 \(a\%b=a-(a/b)*b\)

3.4.1 运算符优先级和结合性

当多个运算符可用于同一操作数时,C++ 使用优先级规则来决定首先使用哪个运算符,也可用括号来执行自己定义的优先级。乘法除法求模运算符的优先级相同,加法减法运算符的优先级相同,但比其他三个要低。当两个运算符的优先级相同且作用于同一个操作数时,C++ 使用结合性规则来决定首先使用哪个运算符,上述 5 种运算符的结合性规则都是从左到右

3.4.2 除法分支

除法运算符 / 的行为取决于操作数的类型。如果两个操作数都是整数,则 C++ 将执行整数除法,结果的小数部分将被丢弃,即向零取整。若其中有一个(或两个)操作数是浮点值,则结果的小数部分将保留,结果为浮点数

3.4.3 求模运算符

求模运算符 % 返回整数除法的余数,尤其适用于解决要求将一个量分成不同的整数单元的问题,例如单位转换。

3.4.4 类型转换

整型和浮点型统称为算术类型,在以下情况下 C++ 将自动执行类型转换:

  • 将一种算术类型的值赋给另一种算术类型的变量时。
  • 表达式中包含不同算术类型时。
  • 将参数传递给函数时。

自动类型转换时的规则如下:

  1. 赋值以及普通初始化:将一种算术类型的值赋给另一种算术类型的变量时,值将被转换为接收变量的类型。有些转换是安全的,但转换时也可能出现以下问题:

    转换 潜在问题
    大浮点数转小浮点数(如doublefloat 精度(有效位数)降低,超过取值范围的结果是不确定的
    浮点数转整型(如doubleint 小数部分丢失(向零取整),超过取值范围的结果是不确定的
    大整型转小整型(如longshort 超过取值范围的结果会出问题,此时只复制右边的字节
  2. 列表初始化:C++11 将使用大括号\(\{\}\)的初始化称为列表初始化,这种初始化方式对类型转换的要求更严格,它不允许缩窄转换,上面表格中浮点数转整型在这是不被允许的,当大浮点数转小浮点数、大整型转小整型时,若超过了目标类型的取值范围,也是不被允许的。

  3. 表达式中的转换:编译器通过校验表以及整型级别来确定算术表达式中的类型转换。有符号整型按级别从高到低依次为long longlongintshortsigned char,无符号整型的排列顺序与有符号整型相同,类型charsigned charunsigned char的级别相同,类型bool的级别最低,wchar_tchar16_tchar32_t的级别与其底层类型相同。校验表的规则较复杂,这里只举一个简单的例子:两个short整型进行算术运算时将先做整型提升至int,然后再运算,最后再转回short

  4. 传递参数时的转换:传递参数时的类型转换通常由 C++ 原型控制,也可取消原型对参数传递的控制,这将在第 7 章介绍。

  5. 强制类型转换:C++ 允许通过强制类型转换机制显式地进行类型转换。强制转换的格式有两种:

    //C语言风格
    (typeName) value;
    
    //纯粹的C++风格
    typeName (value);
    

    强制类型转换不会修改变量本身,而是创建一个新的、指定类型的值,可以在表达式中使用这个值。C++ 还引入了 4 个强制类型转换运算符,这将在第 15 章介绍,其中static_cast<>可用于将数值从一种数字类型转换为另一种数值类型:

    //使用运算符强制转换为typeName类型
    static_cast<typeName> (value);
    

3.4.5 C++11 中的 auto 声明

C++ 重新定义了 C 语言中的 auto 关键字,在初始化声明中,如果使用关键字auto ,而不指定变量的类型,编译器将把变量的类型设置成与初始值相同(自动类型推断):

//自动推断为int类型
auto n = 100;

//自动推断为double类型
auto x = 1.5;

//自动推断为long double类型
auto y = 1.3e12L;

有关第3章 处理数据的更多相关文章

  1. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  2. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  3. ruby - Ruby 有 `Pair` 数据类型吗? - 2

    有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳

  4. ruby - 我如何添加二进制数据来遏制 POST - 2

    我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_

  5. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  6. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

  7. 使用canal同步MySQL数据到ES - 2

    文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co

  8. ruby-on-rails - 创建 ruby​​ 数据库时惰性符号绑定(bind)失败 - 2

    我正在尝试在Rails上安装ruby​​,到目前为止一切都已安装,但是当我尝试使用rakedb:create创建数据库时,我收到一个奇怪的错误:dyld:lazysymbolbindingfailed:Symbolnotfound:_mysql_get_client_infoReferencedfrom:/Library/Ruby/Gems/1.8/gems/mysql2-0.3.11/lib/mysql2/mysql2.bundleExpectedin:flatnamespacedyld:Symbolnotfound:_mysql_get_client_infoReferencedf

  9. STM32读取串口传感器数据(颗粒物传感器,主动上传) - 2

    文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,

  10. SPI接收数据异常问题总结 - 2

    SPI接收数据左移一位问题目录SPI接收数据左移一位问题一、问题描述二、问题分析三、探究原理四、经验总结最近在工作在学习调试SPI的过程中遇到一个问题——接收数据整体向左移了一位(1bit)。SPI数据收发是数据交换,因此接收数据时从第二个字节开始才是有效数据,也就是数据整体向右移一个字节(1byte)。请教前辈之后也没有得到解决,通过在网上查阅前人经验终于解决问题,所以写一个避坑经验总结。实际背景:MCU与一款芯片使用spi通信,MCU作为主机,芯片作为从机。这款芯片采用的是它规定的六线SPI,多了两根线:RDY和INT,这样从机就可以主动请求主机给主机发送数据了。一、问题描述根据从机芯片手

随机推荐