详解C语言/C++指针:篇1
详解C语言/C++指针:篇2
指针是一种地址值!例如: 0x000012ae
什么是指针类型?
指针类型是一种新类型!格式: 类型 *
常见的指针类型:
int *
:整型指针类型char *
:字符型指针类型float *
:浮点型指针类型什么是指针变量?
顾名思义,用指针类型定义的变量就是指针变量。
如何定义指针变量?
//指针类型 变量名;
int* a; //定义一个整型指针变量
char* b; //定义一个字符型指针变量
float* c; //定义一个浮点型指针变量
...
指针变量怎么赋值?
指针变量存放的是地址值!
什么类型的指针变量,就应该指向该类型变量的地址!
给指针变量赋值?
利用&运算符可以获取变量的内存地址。
int a=10; //假设a的地址为0×2000
int *p=&a; //定义一个整型指针变量p,存放整形变量a的地址,此时p的值为0x2000
int c=*p; //取地址所在内存中的内容
空类型指针:void *
void*可以指向任何类型的地址
int a=10;
char b='a';
float c=12.345;
void *pa = &a; //指向整型变量地址
void *pb = &b; //指向字符型变量地址
void *pc = &c; //指向浮点型变量地址
// 从void *指针取数据
int a1 = (*(int*)pa);
int b1 = (*(char*)pb);
int c1 = (*(float*)pc);
什么是野指针?
定义:指向一个非法的或已销毁的内存的指针
危害:对系统造成不可预知的危害!
给指针赋初值NULL:
#define NULL ((void*)0)
int *p=NULL;
指针p
被free
或 delete
之后,只是把指针所指的内存给释放掉,没有改变指针的值。此时p
沦落为“野指针
”。
p = (int*)malloc(4);
free(p); //被销毁的内存地址,此时p沦为野指针
p = NULL; //置空,避免野指针
数组名本身就是一个指针(地址)!
int a[5] = {
1,2,3,4,5};
int *p=a;
数组名代表了数组的首地址!a
与&a[0]
相等!
p
指向数组首地址
for(int i=0;i<5;i++){
printf("%d\n", p[i]);
}
指针作操作数组二
指针+1或-1是向上或向下偏移 sizeof(数组类型)个字节
for(int i=0;i<5;i++){
printf("%d\n", *(p+i));
}
自加++
来获得每个元素的首地址for(int i=0;i<5;i++){
printf("%d\n", *(p++));
}
数组名a
是一个常量,值无法改变,所以不能用于++
,--
指针与数组名的区别:
二维数组: 可以理解为是一个一维数组,只不过数组元素又是一维数组!
指向二维数组的指针(行指针):类型 (*p)[N];
,数组的第二维长度为N
int a[2][3]={
{
1,2,3},{
2,5,6}};
int (*p)[3];
p = a; //p指向数组首地址
虽然四个地址值完全一样,但含义不同:
a
:行指针a[0]
:整型指针&a[0]
:行指针&a[0][0]
:整型指针函数名本身就是一个指针(地址)!
int sum(int a, int b)
{
return a+b;
}
sum
:就是函数的地址值
函数指针变量定义:
返回值 (*变量名)(参数1, 参数2, ..., 参数N);
示例:
int sum(int a, int b){
return a+b}
int (*pSum)(int a, int b); //函数指针变量
pSum = sum; //给指针赋地址值
函数指针类型定义:
typedef 返回值 (*类型)(参数1, 参数2, ...,参数N);
函数指针使用当成函数一样使用!
//p是一个函数指针,指向的函数,参数整型,
//返回值又是一个函数指针(参数、返回值都是整型)
int (*(*p)(int))(int);
()的优先级最高,因此p先与结合,说明p首先是个指针A,再与后面()结合,说明该指针A指向的内容是一个函数A,再与括号中的int结合,说明该函数的参数是一个int,再与(*p)
前面的结合,说明该函数的返回值是一个指针B,再与最后面的()结合,说明该指针B是函数指针。
字符串可以看成一个无名字符数组!
字符串常量本身就是一个地址!
“hello”
在内存中:
字符串常量实质就是一段内存(首地址来标识)!
// 给指针变量赋予字符串常量的首地址!
char *p = "hello";
char *p = NULL;
p = "hello"; // 把hello的首地址给它
p = "world"; // 把world的首地址给它
字符串常量的值不能改变!
指向指针的指针!
什么是指针类型?
格式:类型 *
常见的指针的指针类型:
int**
:整型指针的指针类型char**
:字符型指针的指针类型float**
:浮点型指针的指针类型int a=10;
int b=20;
int c=30;
//指针数组,本身数组名是一个地址,数组元素又是一个地址,双重指针
int *d[3] = {
&a,&b,&c};
//定义指针的指针
int **p = d; // 指针的指针就是为指针数组而生的
// 指针操作数组方法一(把指针当做数组名来用了!)
for(int i=0;i<5;i++)
{
printf("%d\n", *p[i]);
}
// 指针操作数组方法二()
for(int i=0;i<5;i++)
{
printf("%d\n", *(*(p+i)));
}
交换两个数:
// 交换失败,函数内仅仅是实参的拷贝,与实参无关
void swap(int a, int b)
{
int temp = a;
a = b;
b = temp;
}
// 交换成功,实参的地址传入,直接操作实参的内容
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
交换两个指针变量?
void swap2(int **a, int **b)
{
int* temp = *a;
*a = *b;
*b = *temp;
}
int *pA = &a;
int *pB = &b;
swap2(pA, pB);
面向对象+指针!
什么是智能指针?
能自动释放指向的内存,并置空指针!
如何实现智能指针?
思路:将基本类型指针封装为类对象指针(这个类肯定是个模板,以适应不同基本类型的需求),并在析构函数里编写 delete语句删除指针指向的内存空间。
#include
using namespace std;
template <class T>
class SmartPointer
{
public:
SmartPointer(T *p)
{
ptr = p;
}
~SmartPointer()
{
delete ptr; //自动删除内存,防止泄露
ptr = NULL; //自动置空,防止野指针
}
// 重载*运算符
T operator *()
{
return *ptr;
}
// 重载->运算符
T* operator ->()
{
return ptr;
}
private:
T* ptr;
}
void main()
{
SmartPointer<int> sp(new int(123));
//获取指针内容
int a = *sp;
}
STL库提供了四种智能指针:
auto_ptr
(废弃)unique_ptr
shared_ptr
weak_ptr
auto_ptr
( c++11废弃)
delete_ptr
,而不是delete[] ptr
auto_ptr
指向同一个对象。这是最重要的。也就是说,auto_ptr
不能用作STL容器的元素。主要由于上边这个原因,auto_ptr
在C++11
中已经被废弃了(虽然仍然可以使用它),推荐用unique_ptr
取代之。
int *p = new int(4);
auto_ptr<int> ap(p);
cout << *ap << endl;
unique_prt
std::unique_ptr
总是拥有它所指向的资源。std::unique_ptr
将会把所有权也从源指针转移给目标指针(源指针被置空)std::unique_ptr
将不被允许,因为如果你拷贝一个std::unique_ptr
那么拷贝结束后,这两个 std::unique_ptr
都会指向相同的资源,它们都认为自己拥有这块资源(所以都会企图释放)。因此std::unique_ptr
是一个仅能移动(move only)的类型。#include
using namespace std;
void main()
{
//独享型智能指针
unique_ptr<int[]> p(new int[5]{
1,2,3,4,5});
cout << p[2] << endl;
//无法拷贝,赋值,因为是独享的,只能move
unique_ptr<int[]> p2 = move(p);
//如果转移所有权,那么自己变成空
cout << p2[4] << " " << (p==nullptr) << endl;
}
shared_ptr
auto_ptr
和 unique_ptr
都只能一个智能指针引用对象,而shared_ptr
则是可以多个智能指针同时拥有一个对象。
shared_ptr
实现方式就是使用引用计数。这一技术在COM中是用来管理COM对象生命周期的一个方式。这种方式使得多个智能指针同时对所引用的对象有拥有权,同时在引用计数减到0之后也会自动释放内存,也实现了auto_ptr
和 unique_ptr
的资源释放的功能
void main()
{
//共享型智能指针
shared_ptr<int> sp(new int(10));
cout << sp.unique() << endl; //是否唯一持有者
shared_ptr<int> sp2 = sp; //第二个shared_ptr,构造拷贝函数
cout << (sp==sp2) << " " << (sp.use_count() == 2) << endl;
*sp2 = 100; //使用解引用操作符修改被指对象
cout << *sp << endl; //另一个shared_ptr也同时被修改
sp.reset(); //释放
cout << (sp==nullptr) << " " << sp2.use_count() << endl;
}
weak_ptr
weak_ptr
是为了配合 shared_ptr
而引入的一种智能指针,它更像是 shared_ptr
的一个助手而不是智能指针,因为它不具有普通指针的行为, 没有重载 operator*
和->
,它从一个 shared_ptr
或者另一个weak_ptr
对象构造,获得观测权。但weak_ptr
没有共享资源,它的构造不会引起指针引用计数的增加void main()
{
//共享型智能指针
shared_ptr<int> sp(new int(10));
cout << sp.unique() << endl; //是否唯一持有者
weak_ptr<int> wp = sp; //配合shared_ptr的工作
cout << sp.use_count() << endl;
shared_ptr<int> sp2 = sp; //第二个shared_ptr,构造拷贝函数
cout << (sp==sp2) << " " << wp.use_count() << endl;
*sp2 = 100; //使用解引用操作符修改被指对象
cout << *sp << endl; //另一个shared_ptr也同时被修改
sp.reset(); //释放
cout << (sp==nullptr) << " " << wp.use_count() << endl;
}