指针和字符串, demo:
#include "iostream"
#include "cstring"
using namespace std;
int main() {
char animal[20] = "bear";
// 字符常量指针, 也就是bird指向的内存时不允许修改的
const char* bird = "wren";
char* ps;
cout << animal << " and ";
cout << bird << endl;
// 随机显示, 可能是空格, 也可能崩溃
// cout << ps << endl;
cout << "Enter a kind of animal : ";
cin >> animal;
ps = animal;
cout << ps << "!\n";
cout << "Before using strcpy()" << endl;
// 输出的是animal字符数组的首地址
cout << animal << " at " << (int *) animal << endl;
cout << "&animal is " << &animal << endl;
cout << ps << " at " << (int *)ps << endl;
cout << " ps is " << ps << endl;
// 这里输出的不是指针指向的内容的地址, 而是指针变量的地址, 因此与上面的输出地址不同
cout << " &ps " << &ps << endl;
// 这么声明字符数组可以节省空间
ps = new char[strlen(animal) + 1];
stpcpy(ps, animal);
cout << "After using strcpy() : " << endl;
cout << animal << " at " << (int *) animal << endl;
cout << ps << " at " << (int *)ps << endl;
delete[] ps;
return 0;
}
上面程序的运行结果如下:
从程序运行结果可以看出:
对数组名应用(int *)animal 可以得到字符数组的首地址, 和对指针应用(int *)类似
指针和结构体
看一个简单的应用demo:
// 指针和结构体
#include "iostream"
#include "cstring"
using namespace std;
struct things {
int good;
int bad;
};
struct inflatable {
char name[20];
float volume;
double price;
};
int main() {
things gurbnose = {1, 23};
// 将创建的结构体的首地址赋值给指针
things* pt = &gurbnose;
// 结构体变量访问结构体内成员使用.符号
cout << "gurbnose.good = " << gurbnose.good << ", and gurbnose.bad = " << gurbnose.bad << endl;
// 结构体指针访问结构体内成员使用->符号
cout << "pt->good = " << pt->good << ", and pt->bad = " << pt->bad << endl;
// 另一种通过指针访问结构体的成员的方法使使用(*pt).price, 这种方法
// 因为pt是指向结构体的指针, 因此(*pt)就是该结构体, 因此可以通过(*pt).的这种方式访问成员
// 创建了一个结构体指针
inflatable* ps = new inflatable;
cout << "Enter name of inflatable item: ";
cin.get(ps->name, 20);
cout << "Enter volume in cubic feet: ";
cin >> (*ps).volume;
cout << "Enter the price : ";
cin >> ps->price;
cout << "Name : " << (*ps).name << endl;
cout << "volume : " << ps->volume << endl;
cout << "price : " << (*ps).price << endl;
// 记得释放new申请的空间
delete ps;
return 0;
}
程序的运行结果如下:
从demo中可以看出指针引用结构体的成员的方法有两种, 假如ps是指向结构体的指针, 结构体里有price这个成员:
- ps->price
- (*ps).price
这两种方法均可
从上面的demo中可以看出new和delete是成对出现的, 再看一个new和delete的例子:
// new和delete
#include "iostream"
#include "cstring"
using namespace std;
// 声明函数原型
char* getName(void);
int main() {
char* name;
name = getName();
cout << name << " at " << (int*)name << endl;
cout << "name : " << name << endl;
// 释放资源
delete[] name;
name = getName();
cout << name << " at " << (int*)name << endl;
// 这个输出的是指针name的地址而不是指针指向的字符串的首地址
cout << "&name : " << &name << endl;
delete[] name;
return 0;
}
char* getName() {
// 声明了一个临时存储空间
char temp[80];
cout << "Enter your name : ";
cin >> temp;
// 声明了一个char型指针, 指向一个长度是输入的字符串长度+1的char型数组的起始地址
char* pn = new char[strlen(temp) + 1];
strcpy(pn, temp);
return pn;
}
程序运行结果:
从代码中可以看出来,
1.new和delete要成对的使用,
2.delete释放的空间可以重复使用
3.new和delete可以分别在两个函数中使用, 但是要注意的是需要成对的使用
最后看一个多种类型混合使用的例子:
#include "iostream"
#include "cstring"
using namespace std;
struct my_year {
int year;
};
int main() {
// 声明了三个my_year结构体的变量
my_year y1, y2, y3;
y1.year = 1998;
// 声明了一个my_year结构体指针, 并指向y2的首地址
my_year* py2 = &y2;
py2->year = 1999;
// 声明了一个长度为3的my_year结构体数组
my_year years[3];
years[0].year = 2003;
// 输出2003, 第一个元素的year
cout << years->year << endl;
// 声明了一个指针数组
const my_year* parr[3] = {&y1, &y2, &y3};
// 1999
cout << parr[1]->year << endl;
// 指向指针的指针
const my_year** pparr = parr;
const my_year** pparr2 = parr;
// 下面这种写法需要c++11才能编译通过
// auto pparr2 = parr;
// 由于pparr是指向指针的指针, 所以*pparr还是指针, 因此需要用->来引用year
cout << (*pparr)->year << endl;
// (*(pparr2 + 1)) 是一个指向了parr[1]的结构体的指针,
// 因此(*(*(pparr2 + 1)))就是parr[1]这个指针指向的结构体了, 1999
cout << (*(*(pparr2 + 1))).year << endl;
return 0;
}
看一下运行结果:
具体的代码解释, 看一下注释即可, 写的比较详细
接下来说一下C++里的自动存储, 静态存储和动态存储
1.自动存储
在函数内部定义的常规变量使用自动存储空间, 被称为自动变量(automatic variable), 也就是它们在所属的函数被调用的时候自动产生, 在该函数结束的时候消亡.
例如上上个demo中的getName()函数活动的时候temp被创建, 当getName执行完毕的时候释放.
自动变量通常存储在栈中, 这意味着执行代码块的时候, 其中的变量将以此加入到栈中, 而在离开代码块的时候, 将按相反的顺序释放这些变量(LIFO后进先出).
2.静态存储
静态存储是整个程序执行期间都存在的存储方式. 声明方式有两种:
一种是在函数外面定义,
另一种是在声明变量时使用关键字static如: static double fee = 1.23;
3.动态存储
new和delete运算符提供了一种比自动变量和静态变量更灵活的方法. 他们管理了一个内存池, 在c++中称为自由存储空间或者堆(heap). 该内存池同用于静态变量和自动变量的内存是分开的. 上上个demo表明, new和delete能让我们在一个函数中分配内存, 而在另一个函数中释放. 因此数据的生命周期不完全受程序或函数的生存时间控制. 需要注意delete否则可能导致内存泄漏的危险
注意在堆内存上使用new创建变量后, 要记得使用delete进行释放, 否则可能会造成内存泄漏