C++基础杂记(1)
- 结构体中的位字段
- 共用体
- 烦人的枚举
- 指针
- 使用array和vector来避免数组的反向越界
- ++x 和 x++
- 判断 C 风格字符串是否相同为什么不能直接用 ==
- typedef 与 define
- C++11 for循环的新特性
- 声明一个与表达式类型相同的变量
- 函数返回类型声明
- 自动释放内存的智能指针
结构体中的位字段
struct torgle_register
{
unsigned int SN : 4; // 为SN分配四位空间
unsigned int : 4; // 设置四位空间空余
bool goodIn : 1; // 为goodIn分配一位空间
bool goodTorgle : 1; // 为goodTorgle分配一位空间
};
torgle_register tr = {14, true, false};
共用体
union one4all // 共用体的大小为其中最大数据类型的大小
{
int int_val;
long long_val;
double double_val;
};
one4all pail; // 创建对象
pail.int_val = 15; // 此时,共用体pail中储存的值为 int 15
pail.double_val = 1.38; // 此时,int_val的值被抛弃,共用体pail中储存的值为 double 1.38
烦人的枚举
枚举的声明与赋值
enum spectrum {red, orange, yellow}; // 定义了一个枚举类型spectrum,他包含的元素为red, orange和yellow。默认情况下red=0, orange=1, yellow=2,依次递增
enum spectrum {red, blue = 0, orange = 100, yellow};// 定义了一个枚举类型spectrum,他包含的元素为red, blue, orange和yellow。其中red=0, blue=0, orange=100, yellow=101
spectrum band; // 声明了一个spectrum枚举类型的枚举变量
band = red; // 给枚举变量band赋值为red。枚举变量的值只能为其枚举类型的一个元素
枚举的取值范围与强制类型转换
enum spectrum {red, blue = 0, orange = 100, yellow};// 定义了一个枚举类型,他的值可以是red, blue, orange或yellow。其中red=0, blue=0, orange=100, yellow=101
spectrum band; // 声明了一个枚举变量
band = spectrum(6); // 将6强制转换为枚举变量并赋值给band。
虽然枚举变量储存的值是整数,但是,直接将整数赋给枚举变量是不合法的,枚举变量只能接受声明他的枚举类型所包括的元素作为其值。特殊情况下,可以将枚举类型取值范围内的整形强制转换为该枚举类型的元素来赋给该枚举类型的枚举变量。一个枚举类型的取值范围如下:如果该枚举类型的最大元素值为$n$,则该枚举类型取值范围的上限为 $2^N-1$,其中 $2^n \le 2^N \le 2^{n+1}$,$N$ 为整数;设该枚举类型的最小元素值为 $n$,当 $n \ge 0$ 时,该枚举类型取值范围的下限为 0,当 $n < 0$ 时,则该枚举类型取值范围的下限为 $2^N+1$,其中 $2^n \ge 2^N \ge 2^{n+1}$,$N$ 为整数。
枚举的注意事项
- 整形不能够直接赋值给枚举变量。在枚举类型取值范围内的整形可以通过强制转换赋值给枚举变量。
- 枚举变量参与运算时会被强制转换为int。
指针
为什么是 int* ptr 而不是 int *ptr ?
为什么C++程序员在定义指针时习惯写成int* ptr 而不是 int *ptr ?,看下面一个例子:
#include <iostream>
using namespace std;
int main(int argc, char** argv)
{
int int_value = 5;
int* ptr_value = &int_value;
cout<< "int_value: "<< int_value<< " 地址:"<< &int_value<< endl;
cout<< "ptr_value: "<< ptr_value<< " 值:"<< *ptr_value<< endl;
return 0;
}
int_value: 5 地址:0x7fff00e5116c
ptr_value: 0x7fff00e5116c 值:5
先说一下二者的区别:如果写成int *ptr,则认为*ptr是一个整体,定义了一个整形;若果写成 int* ptr,则是认为int*是一种变量类型,这个变量类型定义了一个指针。当从示例来看,地址&int_value并没有赋给*ptr_value而是赋给了ptr_value,这代表 int*ptr 定义了一个指针而不是一个值,因此写成int* ptr更合适。另外,这样理解的好处还体现在指针安全上。
避免危险的指针
常见的危险指针的例子:
int* ptr;
*ptr = 100;
乍一看上去好像没问题,但是,int* ptr并没有指明初始化的地址,这将导致 ptr 被赋予随机地址,而100将被储存在这个随即地址中,这将导致原有的内存内容被破坏,会给我们的程序造成危险。正确的做法如下:
int* ptr = new int; // 为指针分配内存
*ptr = 100;
delete ptr; // 不删除ptr变量本身,但释放掉new给他的内存
另外,数组在声明时会被分配内存,下列代码不会造成野指针:
int arr[3];
使用array和vector来避免数组的反向越界
C++提供了四种数组:
- 标准数组 int a[size]。储存在栈中。
- array数组 array<int size> a。储存在栈中。
- vector动态数组 vector<int> a。储存在自由存储区域或堆中。
- valarray数组 valarray v(size)。 valarray 是一个模板类,专门用来处理数值数组。它为数值数组提供加和、找最大值、找最小值等算数操作。他的构造函数(初始化方法)有很多:
int gpa[] = {1, 2, 3, 4, 5}; valarray<int> v1; // 一个 int 类型的数组,不包含元素
valarray<int> v2(8); // 一个 int 类型的数组,包含 8 个元素
valarray<int> v3(10, 8); // 一个 int 类型的数组,包含 8 个元素,都初始化为 10
valarray<int> v4(gpa, 4); // 一个 int 类型的数组,包含 4 个元素,初始化为 gpa 的前四个元素
当数组长度固定时,使用array而不是标准数组会给我们提供便利。 在C++中,标准数组的下列语句是可以通过编译的:
int a[] = {1, 2, 3, 4};
a[-2] = 10; // 虽然已经越界了,但是仍然会将*(a-2)的位置赋值为10
而array数组的下列语句将会报错:
array<int 4> a = {1, 2, 3, 4};
a.at(-2) = 10; // at会对越界进行检查
terminate called after throwing an instance of 'std::out_of_range'
what(): array::at: __n (which is 18446744073709551614) >= _Nm (which is 4)
Aborted
vector和array有相同的用法。
++x 和 x++
int x = 5;
int y = ++x; // x=6, y=6; ++x返回值为(x+1)
int x = 5;
int y = x++; // x=6, y=5; x++返回值为(x)
另外,x++在运行时会先记录x的值用于返回,在将x+1;而++x将x+1后直接返回。当使用for循环时,从效率上看,++x比x++更好。
判断 C 风格字符串是否相同为什么不能直接用 ==
char* s[] = "mate";
bool bequal = (s=="mate");
上述表达式从表面上看似乎在判断字符串s是不是"mate"。但实际上,由于s不是字符串本身,而是字符串数组的地址,"mate"也不是字符串本身,而是这个字符串常量的地址,所以表达式s=="mate"实际上是在判断字符串变量s与字符串常量"mate"的地址是否相同,所以bequal的结果是false。
在C++中双引号"..."字符串表示的是存储该字符串的地址,而单引号'.'字符表示的是字符本身,且由于字符实际上是一个整型,所以字符本身也是整形本身。
虽然C风格的字符串无法使用==来判断相等,但是C++风格的字符串(由string类提供)可以。
typedef 与 define
- #define name value 是预处理命令,会机械的将代码中的 name 替换为 value,不做正确性检查。没有作用域限制。
- typedef value name; 是关键字,会将代码中的 name 替换为 value,用来定义类型的别名,因此在为类型取别名时 typedef 有优于 define 的特性。typedef 有自己的作用域。
typedef 在定义类型别名上的优势参见如下例子:
#define intptr1 int*
typedef int* intptr2;
intptr1 p1, p2;
intptr2 p3, p4;
在该代码中 p1, p3, p4 是整形指针,而 p2 是整形。可以看出,当使用 #define 给类型取别名时, 由于是机械的替换程序中的别名,造成无法对指针变量进行连续的定义;而 typedef 不存在这样的问题。
另外,在使用 typedef 和 define 时要注意,const 的使用位置不同代表不同的意思,参考如下示例程序:
#define INTPTR1 int*
typedef int* INTPTR2;
int a = 1;
int b = 2;
int c = 3;
const INTPTR1 p1 = &a;
const INTPTR2 p2 = &b;
INTPTR2 const p3 = &c;
首先说明两个重要概念:
- 指针常量:指针类型的常量。例如 int* const p,指针 p 代表的地址是无法被改变的,但是指针 p 所指向的值可以变。也就是说,指针 p 本身是一个常量。
- 常量指针:指向常量的指针。例如 const int* p,指针 p 代表的地址是可变的,但是所指向的值无法被改变。也就是说,值 *p 本身是一个常量。这里的无法被改变是指无法使用 *p = value 语句更改该地址指向的值,但由于 p 是可以改变的,所以 *p 会因为 p 的不同而不同。
在示例程序中,p1 是常量指针,可以使用如下程序验证:
#include <iostream>
using namespace std;
#define intptr1 int*
typedef int* intptr2;
int main(int argc, char** argv)
{
int a = 1;
int b = 2;
const intptr1 p1 = &a;
p1 = &b;
*p1 = 10;
cout<< *p1<< endl;
return 0;
}
test.cpp: In function ‘int main(int, char**)’:
test.cpp:17:8: error: assignment of read-only location ‘* p1’
*p1 = 10;
^~
程序在编译时报错,*p1 是只读的,不可以被改变。但是代码中改变指针 p1 的语句 p1=&b 是合法的,说明指针自身可以被改变。因此 p1 是常量指针。
在示例程序中,p2 为指针常量,可以使用如下程序验证:
#include <iostream>
using namespace std;
#define intptr1 int*
typedef int* intptr2;
int main(int argc, char** argv)
{
int a = 1;
int b = 2;
const intptr2 p2 = &a;
*p2 = 10;
p2 = &b;
cout<< *p2<< endl;
return 0;
}
test.cpp: In function ‘int main(int, char**)’:
test.cpp:16:8: error: assignment of read-only variable ‘p2’
p2 = &b;
^
程序在编译时报错,p2 是只读的,不可以被改变。但是代码中改变值 *p2 的语句 *p2=10 是合法的,说明指针指向的值可以被改变。因此 p2 是指针常量。
在示例程序中, p3 也是指针常量。这里不再设计程序验证。
可以看到,typedef 关键字将指针类型(例如 int* )组合为一个整体,并定义别名,这个特质让示例程序中的const INTPTR2 p2 = &b 和 INTPTR2 const p3 = &c产生了一致的结果,都是定义了一个指针常量。因此,在为类型定义别名时,最好使用 typedef ,具有更好的可解释性。
C++11 for循环的新特性
int prices[] = {1, 2, 3, 4, 5};
for(int x : prices)
{
cout<< x<< endl;
}
这段代码会将数组 prices 中的值从头开始以此以传值的方式传给 x,并由 cout 输出。在循环体中修改 x 无法改变数组 prices 中的值。
int prices[] = {1, 2, 3, 4, 5};
for(int &x : prices)
{
cout<< x<< endl;
}
这段代码会将数组 prices 中的值从头开始以此以传引用的方式传给 x,并由 cout 输出。在循环体中修改 x 会改变数组 prices 中的值。
声明一个与表达式类型相同的变量
decltype(x*n) y; // y 的类型将与 x*n 相同
函数返回类型声明
auto func1()->double;
auto func2()->decltype(x*n);
自动释放内存的智能指针
- auto_ptr
- unique_ptr
- shared_ptr
- weak_ptr
C++基础杂记(1)的更多相关文章
- java复习(2)---java基础杂记
java命名规范: 参考:http://www.cnblogs.com/maowang1991/archive/2013/06/29/3162366.html 1.项目名小写 2.包名小写 3.类名每 ...
- Webpack系列-第一篇基础杂记
前言 公司的前端项目基本都是用Webpack来做工程化的,而Webpack虽然只是一个工具,但内部涉及到非常多的知识,之前一直靠CV来解决问题,之知其然不知其所以然,希望这次能整理一下相关的知识点. ...
- python基础杂记
一.编码 1.ACSII 0000 0001 8位 一个字节 2. uncoide ...
- .Net基础杂记
1.面向对象程序思想 面向对象是程序开发的一种机制,特征为封装.继承.多态.以面向对象方式编写程序时,将复杂的项目抽象为多个对象互相协作的模型,然后编写模型结构,声明或实现类型的成员,即描述对象的特征 ...
- Webpack系列-第三篇流程杂记
系列文章 Webpack系列-第一篇基础杂记 Webpack系列-第二篇插件机制杂记 Webpack系列-第三篇流程杂记 前言 本文章个人理解, 只是为了理清webpack流程, 没有关注内部过多细节 ...
- webpack-插件机制杂记
系列文章 Webpack系列-第一篇基础杂记 webpack系列-插件机制杂记 前言 webpack本身并不难,他所完成的各种复杂炫酷的功能都依赖于他的插件机制.或许我们在日常的开发需求中并不需要自己 ...
- 5天揭秘js高级技术-第一天
一.基础杂记 1. document.write() <script type="text/javascript"> document.write('<h2> ...
- 面试基础知识集合(python、计算机网络、操作系统、数据结构、数据库等杂记)
python python _.__.__xx__之间的差别 python中range.xrange和randrange的区别 python中 =.copy.deepcopy的差别 python 继承 ...
- java基础(杂记)
java基础夯实(杂记):1:创建实例对象可以通过无参的构造函数然后调用成员变量去初始化属性,也可以自己定义有参构造方法直接初始化属性,当属性为private时我们可以通过getset方法间接访问:2 ...
- elasticsearch基础知识杂记
日常工作中用到的ES相关基础知识和总结.不足之处请指正,会持续更新. 1.集群的健康状况为 yellow 则表示全部主分片都正常运行(集群可以正常服务所有请求),但是 副本 分片没有全部处在正常状态. ...
随机推荐
- Redis的设计与实现(3)-字典
Redis 的数据库使用字典实现, 对数据库的增, 删, 查, 改也是构建在对字典的操作之上的. 字典是哈希键的底层实现之一: 当一个哈希键包含的键值对比较多, 又或者键值对中的元素都是比较长的字符串 ...
- vue基本操作[上] 续更----让世界感知你的存在
Vue引用js文件的多种方式 1.vue-cli webpack全局引入jquery (1)首先 npm install jquery --save (--save 的意思是将模块安装到项目目录下,并 ...
- zabbix 使用监控项原型(自动发现规则)
以kafka为例,需要先对 topic-parttion 做发现,脚本如下 cat topic_parttion_discovery.py #!/usr/bin/env python import j ...
- K8S 对象
本页说明了在 Kubernetes API 中是如何表示 Kubernetes 对象的, 以及使用 .yaml 格式的文件表示 Kubernetes 对象. https://kubernetes.io ...
- 钟表练习 html+css实现
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...
- Maven资源导出问题所需配置
<!--在build中配置resources,来防止我们资源导出失败的问题--> <build> <resources> <resource> < ...
- 群晖DS218+部署PostgreSQL(docker)
欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 起因是懒 最近在开发中要用到PostgreSQL数据库 ...
- MySQL 1130错误原因及解决方案
错误:ERROR 1130: Host 'http://xxx.xxx.xxx.xxx' is not allowed to connect to thisMySQL serve 错误1130:主机x ...
- 系统内存管理:虚拟内存、内存分段与分页、页表缓存TLB以及Linux内存管理
虚拟内存 虚拟内存是一种操作系统提供的机制,用于将每个进程分配的独立的虚拟地址空间映射到实际的物理内存地址空间上.通过使用虚拟内存,操作系统可以有效地解决多个应用程序直接操作物理内存可能引发的冲突问题 ...
- python如何提取浏览器中保存的网站登录用户名密码
python如何提取Chrome中的保存的网站登录用户名密码? 很多浏览器都贴心地提供了保存用户密码功能,用户一旦开启,就不需要每次都输入用户名.密码,非常方便.作为python脚本,能否拿到用户提前 ...