标签 C++ Primer Plus 下的文章

[title]什么是指针[/title]

我们如何得到常规变量的地址呢?我们只需对变量应用地址运算符(&),就可以以获得它的位置;例如,如果home是一个变量,则&home是它的地址。

使用常规变量时,值是指定的量,地址是派生量;

而指针变量恰恰相反,地址是指定量,值是派生量。

指针名表示地址。*运算符被称为简介值或解除引用运算符,将其应用于指针,可以得到改地址处存储的值。下面是具体的使用。

#include <iostream>
using namespace std;
int main(){
    int updates = 6; // declare a variable
    int *p_updates;  //declare pointer to an int
    p_updates = &updates; // assign address of int to pointer
    
    //express values two ways
    cout << "Values: updates = " <<updates; // value = 6
    cout << ", *p_updates = " << *p_updates <<endl; // value = 6
    
    //express address two ways
    cout << "Addresses: &updates = " << &updates;
    cout << ", p_updates = " << p_updates << endl;
    
    //use pointer to change value
    *p_updates = *p_updates + 1;
    cout << "Now updates = " << updates <<endl;
    return 0;
}

[title]申明和初始化指针[/title]

申明指针:

ps:*旁的空格是可选的;

typeName *pointer_name;

初始化指针:

ps:1.指针的地址长度都是相同的(具体的取决于计算机系统)

         2.初始化的是指针pt,不是它(*pt)指向的值

 3.初始化的时候应该赋给它的是地址而不是值,这发生最隐匿、最难以跟踪的bug;

int higgens = 5;
int *pt = &higgens;

[title]指针和数字[/title]

如果我们一定要将数字作为地址来使用辣么,应该通过强制转换,例如下

int *pt;
pt = (int *) 0xB8000000; // types now match

[title]使用new来分配内存[/title]

在C语言中,可以库函数malloc()来分配内存;在C++中我们可以用new运算符

通用格式如下

typeName *pointer_name = new *typeName;

我们告诉new,需要哪种数据类型分配内存;new找到一个长度正确的内存块,并返回内存块的地址。我们只要将该地址赋给一个指针就OK了;

[title]使用delete释放内存[/title]

delete运算符在使用完内存后,能够将其归还给内存池,具体用法: delete(pointer_name)

这里我们要注意的是:

1、释放内存,但不会删除指针本身

2、delete和new一定要搭配使用(就是delete只能释放使用new分配的内存,但对空指针使用delete是Ok的),否则会发生内存泄漏问题

3、释放过的内存块不能再释放

4、不能使用delete来释放申明变量所获得的内存

5、如果使用new[]为数组分配内存,则应使用delete[]来释放。

int * ps = new int; // ok
delete ps; // ok
delete ps; // not ok now
int jugs = 5; // ok
int *pi = &jugs //ok
delete pt; // not allowed, memory not allocated by new

[title]使用new来创建动态数组[/title]

就是使用new,如果在运行阶段 需要数组,就创建 它;不需要就不创建;就叫动态数组

为数组分配内存的通用格式如下:

type_name *pointer_name = new type_name[num_elements]; // 第一个元素的地址赋给指针

创建了动态数组后,我们如何访问其中的元素呢?

只要把指针当做数组名使用就可以,对于第一个元素可以使用pointer_name[o], 而不是*poiter_name, 第二个就是pointer_name[1],一次类推

[title]指针、数组和指针算术[/title]

C++中将数组名解释为地址,数组名为第一个元素的地址

double *pw = wages; // wages为数组名
wages = &wages[0] = address of first elements of array

由于pw指向第一个元素,因此*pw显示的是第一个元素的值,这里唯一的区别是我们可以修改指针的值,而数组名是常量,另一个区别是,对数组使用sizeof 得到的是数组的长度,而对指针使用的到的是指针的长度;

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

 

指针算术:

C++允许指针和整数想家,加K的结果是原来的地址值加上指向的对象占用的总字节数。还可以将一个执政减去另一个指针,获得两个指针的差,它会得到一个整数,仅当两个指针指向同一个数组才有意义,得到两个元素的间隔。

int tacos[10] = {5, 2, 8, 4, 1, 2, 2, 4, 6, 8};
int *pt = tacos; //suppose pf and tacos are the address 3000
pt = pt + 1; // now pt is 3004 if a int is 4 bytes
int *pe = &tacos[9]; // pe is 3036 if an int is 4 bytes
pe = pe - 1; // now pe is 3032, thie address of tacos[8]
int diff = pe - pt; // diff is 7, thie separation between
                    // tacos[8] and tacos[1]

[title]指针和字符串[/title]

cout对象认为char的地址是字符串 的地址,因此它打印该地址处的字符,然后继续打印后面的字符,直到遇到空字符(\0)位置。

在C++中,用引号括起的字符串像数组名一样,也是第一个元素的地址。

注意:在cout和多数C++表达式中,char数组名、char指针以及用引号括起的字符串常量都被解释为字符串的第一个字符的地址。

经常要将字符串放到 数组中。初始化数组时,要使用=运算符,苟泽使用strcpy或strncpy()

char food[20] = "carrotes"; // initialization
strcpy(food, "flan"); // otherwise

但如果food数组比字符串要小,就会出现问题

这时我们就要用strncty(),如果该函数在到达字符串结尾之前,目标内存已经用完,则它将不会添加空字符,此时就该这样做,如下

strncpy(food ,"aaaaaaaaaaaaaaaaaaaaaaaaaaa", 19);
food[19] = '\0';

这样最多将19个字符复制到数组中,然后将最后一个元素设置成空字符。如果该字符少于19个字符,则复制完它后加上空字符,以标记该字符串的结尾。

 

[title]const限定符[/title]

const叫做限定符,因为它限定了声明的含义。

符号名称指出了常量表示的内容。如果程序在多个地方使用同一个常量,则需要修改常量时,只需要修改一个符号定义即可。在C语言中,#define语句已经足够完成这样的工作了,但是const要优于#define。

首先,它能够明确指定类型。其次,可以使用C++的作用域规则将定义限制在特定的函数过文件中。第三可以将const用于更加复杂的类型。

[title]关于浮点数[/title]

缺点:浮点运算速度要比整数运算慢,且精度降低。

cout.setf(ios_base::fixed,ios_base::floatfield);

当把这行代码加入时,输入两个浮点数,相加后结果如1图所示

但如果去除这一行代码,运算结果就对了,如图二

cout.setf(ios_base::fixed,ios_base::floatfield)
cout.selt():通常cout会删除结尾的零。例如,将2.120000显示为2.12,而调用cout.selt()将保留
ios_base::fixed是设置cout为定点输出格式
ios_base::floatfield是设置输出时按浮点格式,小数点后有6位数字,
浮点型数据精度不高 所以导致结果错误
对于float,C++只保证6位有效数。

出处:https://www.zhihu.com/question/48684548/answer/115342961(知乎)

[title]强制转换的三种格式[/title]

(typeName) value //converts value to typeName type

这种格式来自C语言

typeName (value) // converts value to typeName type

这种格式是纯粹的C++。新格式的想法是,是让强制转化就像是函数的调用。这样对内置类型的强制类型的转化就像是为用户定义的类设计的类型转化

static_cast<typeName> (value) // converts value to typeName type

运算符static_cast<>比传统的强制类型转化更为严格。

[title]数组[/title]

1、sizeof:sizeof运算符返回类型或数据对象的长度(单位为字节)。注意,如果将sizeof 运算符用于数组名,得到的就是整个数组的字节数。但如果将sizeof 用于数组元素,则得到的将是元素的长度(单位为字节)。而 strlen()函数返回的是存储在数组中的字符串长度,而不是数组本身的长度。另外,strlen()只计算可见的字符,而不把空字符计算在内。
2、如果只对数组的一部分进行初始化,则编译器将把其他的元素设置为0。因此,将数组中所有的元素都初始化为0非常简单——只要显示的将一个元素初始化为0就OK了。
3、初始化方法
这里记录的是C++11中的列表初始化新增的一些功能
首先,初始化数组时,可省略(=)

doouble earning[4] {1.2e4, 1.6e4, 1.1e4, 1,7e4} // okay with C++11

其次,可不在大括号里包含任何东西,这将把所有的元素都设置为0

unsigned int counts[10]  = {} // all elements set to 0 
float balances[100] {} // all elements set to 0

第三,列表初始化禁止缩窄转化

long plifs[] = {25, 92, 3.0}; // not allowed 浮点数转化为整形是缩窄操作
char slifs[4] {'h', 'i', 1122011, '\0'}; // not alllowed 1122011超出了char变量的取值范围
char tlifs[4] {'h', 'i', 112, '\0'}; //allowed

[title]字符串[/title]

C++处理字符串的方式有两种,一种来自于C语言,常被称为C-风格的字符串它被存储于char数组中,另一种是基于string类库的方法

第一种:

以空字符串(null character)结尾,空字符被写作\0,其ASCII码为0,用来标记字符串的结尾。例如下面两个申明:

char dong[8] = {'b', 'e', 'a', 'u', 'x', ' ', 'I', 'I'}; // not a string!
char cat[5] = {'f', 'a', 't', 'e', '\0'}; // a string!

warning: 在确定存储字符串所需的最短数组时,别忘了将结尾的空字符计算在内。

 

拼接字符串常量

有时候,字符串很长,无法放到一行中。C++允许拼接字符串字面值,即将两个用引号括起的字符串合并为一个。事实上,任何两个由空白(空格、制表符和换行符)分隔的字符串常量都将自动拼接成一个。因此,下面所有的输出语句都是等效的:

cout << "I'd give my right arm to be " " a great violentst.\n";
cout << "I'd give my right arm to be a great violentst.\n";
cout << "I'd gibe my right ar"
"m to be a great violenist.\n";

注意,拼接时不会在被连接的字符串之间添加空格,第二个字符串的第一个字符将紧跟在第一个字符串的最后一个字符(不考虑\0)后面。第一个字符串中的\0字符将被第二个字符串的第一个字符取代。

[title]每次读取一行字符串输入[/title]

getline():
getline()函数读取整行,它使用通过回车键输入的换行符来确定输入结尾。调用方法为cin.getline()。该函数有两个参数。第一个参数是数组名,第二个参数是要读取的字符数。例如第二个参数为n,则函数最多读取n-1个字符,因为至少要保留一个空间存空字符,即'\0'。getline()成员函数在读取指定数目的字符或遇到换行符时停止读取。当遇到换行符时,它也会从输入流中读取该换行符,但在存储字符串时,用空字符代替换行符来存储在字符串中。
get():
该函数有几种变体,其中一种变体的工作方式与getline()类似,接受的参数的意义与解释都相同,并且都是读取到行尾。但是当遇到换行符时,get()方法并不读取换行符,而是将其留在输入流中。假设我们两次调用get():

cin.get(name, ArSize);
cin.get(dessert, Arsize); // a problem

由于第一次调用后,换行符将留在队列中,因此第二次调用时看到的第一个字符便是换行符,因此get()认为已经到达行尾,而没有发现任何可以读取的内容。

幸运的是,get()有另一种辩题。使用不带任何参数的cin.get()调用可读取下一个字符(即便是换行符),因此可以用它来处理换行符,为读取下一行做准备。

cin.get(name, ArSize);   // read first line
cin.get();               // read newline
cin.get(desser, Arsize); // read second line

 另一种使用get()的方式将两个成员函数拼接起来(合并),如下所示
cin.get(name, ArSize).get(); // concatenate member functions

之所以可以这样做是由于cin.get(name, ArSize)返回一个cin对象,该对象将被用来调用get()函数。同样,下面的额语句将把输入中连续的两行分别读入到数组name1和name2中,其效果与两次调用cin.getline()相同

cin.getline(name1, ArSize).getline(name2, ArSize);