声明和初始化指针
int a = 5;
int* pi = &a
cout<<*pi<<endl;
可以理解为pi的类型是指向int的指针(int*)。也就是说,pi是指针( 地址),而*pi
是int,而不是指针
int* pi;
*pi = 23333;
一定 要在 对 指针 应用 解除 引用 运算符(*) 之前, 将 指针 初始 化为 一个 确定 的、 适当 的 地址。 这是 关于 使用 指针 的 金科玉律。 如果指针未被初始化,不知道他的地址是啥的情况下,不能随意的给他指向的内存块赋值,如果 pi 的 值 碰巧 为 1200, 计算机 将把 数据 放在 地址 1200 上, 即使 这 恰巧 是 程序 代码 的 地址。 pi指向 的 地方 很可能 并不是 所要 存储 23333 的 地方。 这种 错误 可能 会 导致 一些 最 隐匿、 最难 以 跟踪 的 bug。
使用new来在运行时分配内存
int* pi = new int;
delete pi;
new int 告诉 程序, 需要 适合 存储 int 的 内存。 new 运算符 根据 类型 来 确定 需要 多少 字节 的 内存。 然后, 它 找到 这样 的 内存, 并 返回 其 地址。 接下来, 将 地址 赋 给 pi, pi 是 被 声明 为 指向 int 的 指针。 现在, pi 是 地址, 而* pi 是 存储 在那里 的 值。当 需要 内存 时, 可以 使用 new 来 请求, 这 只是 C++ 内存 管理 数据 包 中有 魅力 的 一个方面。 另一个 方面 是 delete 运算符, 它 使得 在使 用完 内存 后, 能够 将其 归还 给 内存 池, 这是 通向 最 有效地 使用 内存 的 关键 一步。
char* getName() {
int* mm = new int;
*mm = 5;
char temp[5] = "1234";
char* pn = new char[strlen(temp) + 1];
strcpy(pn, temp);
return pn;
}
int mian(){
char* pn = getName();
cout << pn << endl;
delete[] pn;
}
上述代码中getName()执行结束后,temp使用的内存将会自动释放,pn使用的内存在main函数释放,而mm指向的内存将不会被释放。如果使用new申请的内存mm没有得到释放,那么相应的内存将不会被再次利用。如果此时没有指向mm的指针,mm将无法被释放,那么会将会造成内存泄漏。
使用指针
指针与结构体
struct st {
int a;
string b;
st(int a, string b) {
this->a = a;
this->b = b;
}
};
void pointAndStruct()
{
st* st3 = new st(577, "dd");
//A->B A是指针,获取A中的成员B 相当于 (*A).B;
cout << st3->a << endl;
//三种赋值方式
(*st3).a = 588;
st3->a = 599;
*&st3->a = 611;
cout << (*st3).a << endl;//611
//释放内存(释放内存的意思当前内存可以再次new了,所以只能用delete 释放使用new分配的内存)
delete st3;
}
指针与数组
void pointAndArray()
{
//手指
int* p = new int[2]{ 0,1 };
cout << p[0] << endl; //0
cout << *p << endl; //0
cout << *(p + 1) << endl; //1
int* p1 = p + 1;
cout << p << endl; //006E5858
cout << p1 << endl; //006E585C
//加 1 的 结果 等于 原来 的 地址 值 加上 指向 的 对象 占 用的 总 字节数。
delete[]p;
}
指针与字符串
void pointAndString()
{
//末尾的被'\0',自动填充,这是必不可少的
char s5[7] = "123456"; //实际上 = {'1','2','3','4','5','6','\0'}
cout << (int*)s5 << endl; //获取地址
auto s6 = s5 + 1;
cout << s6 << endl; //23456(输出会到'\0')停止
const char* s7 = "12345"; //字符串 字 面值 是 常量, 这就 是 为什么 代码 在 声明 中 使用 关键字 const 的 原因。
char* s8 = new char[5]{ '1','2' }; //末尾自动补'\0',但不能new char[2]{ '1','2' }这样
int as = 5;
cout << s8 << endl;
cout << strlen(s8) << endl; //2
delete[]s8;
}
指针与const
typedef int* int_point;
int main()
{
int a[5] = { 1,2,3,4,5 };
int_point ap = a;
cout << ap[0] << endl; //1
const int* ac; //*ac 是const 不能被修改
ac = ap;
ac++;
cout << *ac << endl; //2
int* const af = a; //af 是const 不能被修改
*af = 6;
cout << *a << endl; //6
}
函数指针
函数指针能够使函数地址作为参数传递
int add(int a, int b)
{
return a + b;
}
void printAdd(int a,int b,int (*pf)(int, int)) {
cout << (*pf)(a, b) << endl;
//cout << pf(a, b) << endl;//两种方式都可以,第一种更规范,第二种更方便
}
int main()
{
printAdd(4, 5, add);//add代表函数add(int a,int b)的地址
}
更复杂的函数指针
typedef const double* (*p_func)(const double*, int);//减少代码犯错率(有点委托的味道了)
const double* f1(const double* ar, int b) {
return ar;
}
const double* f2(const double ar[], int b) {
return ar + 1;
}
int main()
{
double ar[3] = { 1,2,3 };
p_func p1 = f1;
p_func p2 = f2;
cout << *p1(ar, 5) << endl;//1
cout << *p2(ar, 5) << endl;//2
p_func pa[2] = { f1,f2 };
cout << *pa[0](ar, 5) << endl;//1
p_func(*pd)[2] = &pa;//一个指向有两个p_func元素数组的指针=>(*pd)[0] = pa[0],(*pd)[1] = pa[1]
cout << *(*pd)[1](ar, 5) << " " << (*pd)[1](ar, 5) << endl;//2 001CFC34
}
摘要
Stephen Prata. C++ Primer Plus(第6版)中文版(异步图书) (C和C++实务精选) (p. 295 - p380). 人民邮电出版社. Kindle 版本.