详解C语言/C++指针:篇1

详解C语言/C++指针:篇1

详解C语言/C++指针:篇2

文章目录

  • 1. 什么是指针?
  • 2. 空指针、野指针
  • 3. 指针与一维数组
  • 4. 指针与二维数组
  • 5. 指针与函数
  • 6. 指针与字符串
  • 7. 指针的指针
  • 8. 指针作为参数
  • 9. 智能指针auto_ptr、unique_ptr等

1. 什么是指针?

指针是一种地址值!例如: 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;	//取地址所在内存中的内容

2. 空指针、野指针

空类型指针: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;

指针pfreedelete之后,只是把指针所指的内存给释放掉,没有改变指针的值。此时p沦落为“野指针”。

p = (int*)malloc(4);
free(p);	//被销毁的内存地址,此时p沦为野指针

p = NULL;	//置空,避免野指针

3. 指针与一维数组

数组名本身就是一个指针(地址)!

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是一个常量,值无法改变,所以不能用于++--

指针与数组名的区别:

  1. 指针是一个变量,可以变,可以++,–
  2. 数组名是一个常量,不可变,无法++,–

4. 指针与二维数组

二维数组: 可以理解为是一个一维数组,只不过数组元素又是一维数组!

指向二维数组的指针(行指针):类型 (*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]:整型指针

5. 指针与函数

函数名本身就是一个指针(地址)!

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是函数指针。

6. 指针与字符串

字符串可以看成一个无名字符数组!

字符串常量本身就是一个地址!

“hello”在内存中:

字符串常量实质就是一段内存(首地址来标识)!

// 给指针变量赋予字符串常量的首地址!
char *p = "hello";

char *p = NULL;
p = "hello";	// 把hello的首地址给它
p = "world";	// 把world的首地址给它

字符串常量的值不能改变!

7. 指针的指针

指向指针的指针!

什么是指针类型?

格式:类型 *

常见的指针的指针类型:

  • 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)));
}

8. 指针作为参数

交换两个数:

  • 第一种写法:( 交换失败
// 交换失败,函数内仅仅是实参的拷贝,与实参无关
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);

9. 智能指针auto_ptr、unique_ptr等

面向对象+指针!

什么是智能指针?

能自动释放指向的内存,并置空指针!

如何实现智能指针?

思路:将基本类型指针封装为类对象指针(这个类肯定是个模板,以适应不同基本类型的需求),并在析构函数里编写 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_ptrC++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_ptrunique_ptr都只能一个智能指针引用对象,而shared_ptr则是可以多个智能指针同时拥有一个对象。

  • shared_ptr实现方式就是使用引用计数。这一技术在COM中是用来管理COM对象生命周期的一个方式。这种方式使得多个智能指针同时对所引用的对象有拥有权,同时在引用计数减到0之后也会自动释放内存,也实现了auto_ptrunique_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;
}

你可能感兴趣的:(#,0.1,C/C++,c++,指针)