您的位置:首页 > 编程语言 > C语言/C++

C++ primer plus (第6版)里的技术点(1)

2015-01-16 21:48 162 查看
1, 结构的初始化:

struct fish
{
int type;
double weight;
double length;
};

typedef struct {
int type;
double w;
double h;
} cat;

int main(int argc, std::string argv[])
{
fish f = { 1, 8.0, 10.0 };
cat c = { 1, 10, 20 };
exit(0);
}


2, 枚举的取值范围(CPP 97):

书上说的取值范围是: 先找到枚举量的最大值(如8), 再找到大于这个最大值的, 最小2的幂, 将它减去1(2^4-1 = 15), 这样就得到了取值范围的上限.

但是, 在G++和VS2012下测试, 无论是什么值(int的取值范围), 都是行的通的.

enum test_e {
TYPE1 = 0,
TYPE2 = 1,
TYPE3 = 2,
TYPE4 = 4
};

int main(int argc, std::string argv[])
{
test_e e = (test_e)-800000;
std::cout<<e<<std::endl;
std::cin.get();
exit(0);
}


3, 指针与数组(CPP 108):

它们两个的区别之一是, 可以修改指针的值, 但数组名是常量.

下面是错误的用法:

int arr[10] = {0};
int * p = arr;

p += 3;
arr += 3;


另一个区别是, 对数组应用sizeof运算符得到的是数组的长度, 而指针应用sizeof得到的是指针的长度, 即使指针指向的是一个数组.

4, 数组的地址(CPP 109):

数组名被解释为其第一个元素的地址, 而对数组名应用地址运算符时, 得到是整个数组的地址.

short tell[10];
std::cout<<tell<<std::endl;
std::cout<<&tell<<std::endl;

short (*pas)[10] = &tell;
从数字上说, 这两个地址相同;但从概念上说, &tell(即tell)是一个2字节内存块的地址, 而&tell是一个20字节内存块的地址.因此, 表达式tell + 1将地址值加2, 而表达式&tell + 1将地址加20.(书上的印刷错误多的离谱).换句话说, tell是一个short指针(* short), 而&tell是一个指向包含10个元素的short数组的指针(short(*)[10]).

声明这样一个指针如上, 如果省略括号, 优先级规则将使得pas先与[10]结合, 导致pas是一个short指针数组, 它包含10个元素, 因此括号是必不可少的.因此, pas的类型为short(*)[10].另外, 由于pas被置为&tell, 因此*pas与tell等价, 所以(*pas)[0]为tell数组的第一个元素.

这里详细描述一下strncpy和strncat 这两个函数(个人说明部分):

char buf[10] = {0};
strncpy(buf, "1234567890", sizeof(buf) - 5);
std::cout<<buf<<std::endl;
strncat(buf, "1234567890", sizeof(buf) - strlen(buf) - 1);
std::cout<<buf<<std::endl;
输出为:

12345

123451234

以上strncpy的调用, 只是将第二个参数中的n(sizeof(buf) - 5)字符copy到buf, 而strncat的调用是将第二个参数的n(这里是buf的剩余空间)个字符加到buf中.

5, C++中复合语句的一条有趣的特性(CPP 137):

如果在语句块中定义一个新的变量, 则仅当程序执行该语句块中的语句时, 该变量在才在, 执行完该语句块后, 变量将被释放.

注意: 这条规则适用于复合语句, 并非是for循环的专属.

6, 字符函数库(CPP 177):

这个没什么重点, 只是如果平常如果需要的时候可以看一下, 而不用过多的编写已有的函数.

std::isalnum(c);			//参数是否是字母或数字
std::isalpha(c);			//参数是否是字母
std::iscntrl(c);			//是否是控制符
std::isdigit(c);				//是否是数字(0-9)
std::isgraph(c);			//是否是除空格之外的打印字符
std::islower(c);			//是否是小写字母
std::isprint(c);			//是否是打印字符(包括空格)
std::ispunct(c);			//是否是标点符号
std::isspace(c);			//是否是空白字符, 如空格, 回车, 制表符, 换行符等.
std::isupper(c);			//是否是大写字母
std::isxdigit(c);			//是否是16进制数 (1-9, abcdef)
std::tolower(c);			//如果c是大写, 则转成小写, 否则返回c
std::toupper(c);			//如果c是小写, 则转成大写, 否则返回c


7, 函数和二维数组(CPP 224):

int sum(int (*ar2)[4], int size)
{
int sum = 0;
for (int i = 0; i < size; i++)
for (int j = 0; j < 4; j++)
sum += ar2[i][j];
return sum;
}

int main(int argc, std::string argv[])
{
int data[3][4] = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } };
std::cout<<sum(data, 3)<<std::endl;

int (*pas)[4] = data;

std::cout<<**pas<<std::endl;

std::cin.get();
exit(0);
}


pas 等同于data

pas + 1等同于data + 1, pas为行首地址, pas + 1为第二行首地址, 表示整行

*(pas + 1)为元素地址, 为第二行第一个元素的地址.

**(pas + 1)为元素地址的值, 为第二行第一个元素的地址所存储的值.

*(*(pas + 2) + 2)为元素地址的值 , 为第三行第三个元素的地址所存储的值.

注意sum函数的签名, 不能定义为int **ar2,

**ar2表示一个int类型指针的指针, 即int * 的指针, 它还可以表示为指针数组, 如 int * p[10] 可以于 int **pa的形参匹配.

而int (*ar2)[4]表示一个ar2是一个指向一个4个int类型的数组的指针.

二维数组指针一定要给出第二维的宽度值.

而二维数组的指针也一定要给出表示二维数组第二维的宽度下标.

注意: 传递二维数组的数组名不能使用二级指针来接收他们, 类型是不匹配的.

8, 临时变量, 引用参数和const(CPP 262):

如果实参与引用参数不匹配, C++将生成临时变量.仅当数为const引用, C++才允许这样做.

什么时候将创建临时变量呢?如果引用参数是const, 则编译器将在下面两种情况下生成临时变量:

1) 实参的类型正确, 但不是左值.

2) 实参的类型不正确, 但可以转换为正确的类型.

这里主要注意一下左值的概念, 左值是可以被赋值的变量.

9, 函数的默认参数(CPP 275):

只有原型指定默认值. 函数定义与没有默认参数时完全相同.

就是说在声明函数的时候给定默认值, 在定义的时候不要加了, 否则编译会出错.

10, 模板函数, 常规模板, 具体化模板和实例化模板.

这里主要是要注意一下语法, 具体化模板是要给定针对某种类型的定义, 而实例化模板只是将实际类型的代入到函数声明中而以, 并不用重新给出定义.

#include <iostream>

//常规模板 1#
template <typename T>
void Fun(T a)
{
std::cout<<a + 1<<std::endl;
}

//实例化 2#
template
void Fun<int>(int a);

//实例化 3#
template
void Fun(long a);

//具体化 4#
template<>
void Fun(char c)
{
std::cout<<"Fun (char c)"<<std::endl;
}

//具体化 5#
template<>
void Fun<double>(double c)
{
std::cout<<"Fun (double)"<<std::endl;
}

int main(int argc, std::string argv[])
{
Fun(10L);			//3#
Fun(10);			//2#
Fun(14.1F);		//1#
Fun(18.0);			//5#
Fun('F');				//6#
std::cin.get();
exit(0);
}


在调用时, 也可以显示调用

Fun<int>('F');

这样的话将调用2#函数

编译器在调用时如果发现无法确定调用哪一个函数时, 将出现编译错误.

11, 关键字decltype(C++11)(CPP 295)

这是一个可以推断出的类型.

template<class T1, class T2>
auto Fun(T1 t1, T2 t2) -> decltype(t1 + t2)
{
decltype(t1 + t2) t3;
return t3;
}
只有C++11.才支持这种语法.

12, 说明符和限定符(CPP 317):

说明符:

auto 自动类型推断.

register 在现有的系统中, 以不在有效, 是否是用寄存器, 由操作系统决定.

extern 表明引用声明, 表示所引用的变量或是函数在其他地方文件中定义.

thread_local C++11指出变量的持续性与其所属线程的持续性相同.

mutable 该说明指出, 即使结构或类的对象声明为常对象, 但用mutable修饰过的变量, 也可以修改.

限定符:

const 常量

volatile 尽止编译器优化的变量

13, 链接性

链接性有 外部链接 即全局变量或函数. 在头文件中声明的对象(如果是在源文件中声明或定义的, 在其他文件中需要用extern声明引用一下)

内链接性 只有声明定义变量或函数的文件才能使用. 在文件中级经过static所修饰的对象.

无链接性 只有声明的域中才能使用, 无链接性只是用来指定变量. 在函数级中经过static修饰的对象

在全局范围的变量将会被初化始为默认值.而局部变量不会.

14, 定位new运算符(CPP 321):

使用new的定位操作方式, 不一定需要手动调用delete来释放, 因为定位的new操作可以分配到动态内存区也可能只是全局静态内存区,

想要定位需要事先定义一块有内存, 而该内存的作用域必需有效, 否则将出现预期以外的事情.

#include <iostream>

char buffer[1024];

int main(int argc, std::string argv[])
{
int * p1 = new(buffer) int[10];
int * p2 = new(buffer + sizeof(int) * 10) int[20];

std::cout<<(int)&buffer<<std::endl;
std::cout<<(int)p1<<std::endl;
std::cout<<(int)p2<<std::endl;

std::cin.get();
exit(0);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: