1.指针数据类型
众所周知,指针是用来保存数据地址的,虽然各种类型数据的地址的格式是一致的,但由于如char类型,double类型数据的字节数不一致,因此指针的指向字节数也不一致,所以我们声明指针时需定义好指针所指向的类型。基于此,我们认为指针和数组是一样的均是基于其他类型的新类型。其中有一个特别类型的指针:void* 指针。该种类型的指针可以保存任何类型对象的地址 。
void* 指针只支持几种有限的操作: 1. 与另一个指针比较。 2. 向函数传递void* 指针或从函数返回void* 指针。 3. 不允许使用void* 指针操纵它所指向的对象
2.声明指针
有两种声明方式:
- int* ptr:这种写法可以理解为定义了一个指向int型变量的指针(int*)
- int *ptr:这种写法可以理解为定义了一个int型变量(int),而这个变量的名称就是*ptr,因为*ptr的用法和int是一样的。
正常情况下,在C++中我们用前者,也就是认为(int*)是一种新的数据类型。当执行操作:int* ptr=&abc时,因为此时赋值的是给ptr赋值。所以把ptr看成一个指针变量更合理。但需要注意的是,int* p1,p2表示的是声明了一个指针p1,一个int型变量p2.如果要声明两个指针,那么需要写成int* p1, * p2 。
3.指针初始化
初始化指针时请给定指针指向,这将会是一个良好的编程习惯。
int* ptr;
*ptr = 233;
如上述若在声明指针ptr时,未给出其指向,那么意味着ptr的指向是随机的,此时会造成许多隐私错误的发生,如将233数据存到了代码段。指针初始化要在定义的时候直接初始化掉,不然会产生内存危险,因为一开始不赋值就会随机分配地址,有可能分配什么代码的地址。
给指针变量赋值数字的时候,不能将整型数字赋值给指针变量,要把数字强制类型转换成指向int的指针类型:
int* ptr;
ptr = 0x12121212;
int* ptr;
ptr = (int*)0x12121212;
在C++中,对数据类型十分严格。因此,上述代码段的全两句将整型数据赋值给int型指针是错误的,这本质上是两种不同的类型,一种是int一种是int*。代码段的后两句才是正确的赋值方式。如下为三种常见的指针初始化方式。
//初始化空指针(一般用Null)
int *P = Null;
//初始化同类型变量的地址
int ival = 2;
int *P = &ival;
//初始化同类型的另一个有效指针
int ival = 2;
int *P = &ival;
int *p2 = P;
4.指向指针的指针
某一指针的存储地址,可存放在另一个指针中。
#include<iostream>
using namespace std;
int main()
{
int ival = 100;
int *Pi = &ival;
int **PPi = Π//指向指针的指针
cout<< "ival=" << ival <<endl;
cout<< "Pi指向的值=" << *Pi<<endl;
cout<< "PPi指向的指针所指向的值=" << **PPi<<endl;//最终都是输出100
return 0;
}
5.函数返回指针(决不能返回局部变量的指针)
主要原因在于函数中的局部变量在函数返回之后就会被清空,所以是返回不出函数的。
int* fp()
{
int local = 3;
return &local;
}
//当然也不能返回局部对象的引用
const string& f(const string& s)
{
string ret = s;
return ret;
}
6.指向函数的指针
对于函数SORT而言,其函数名为(SORT)所以一般调用的时候要用(SORT)(1)这样的形式,但在C++中可以用SORT(1)这个形式,C中必须用(SORT)(1)。这是对于全局函数而言的,对于类的成员函数来说就不一样了。 原因是对于全局函数而言,它的名称就是它的地址,当然直接取地址也仍然表征的是地址,所以上述代码中会有2*2=4种调用方式。然而类的成员函数用的时候必须用取地址符,因为成员函数本质上是变量并不是函数。
#include <iostream>
using namespace std;
void (*SORT)(int n);
//void *SORT(int n); 这样声明是不行的,这样声明仅仅只是声明一个函数其返回值是void* 的指针。
void Sort1(int n){printf("冒泡排序\n");}
void Sort2(int n){printf("希尔排序\n");}
void Sort3(int n){printf("快速排序\n");}
void TestFunPointer()
{
SORT = Sort1; SORT(1);//SORT = Sort1; (*SORT)(1); 所以也可以这样写
//SORT = &Sort1; (*SORT)(1); 所以也可以这样写,
//这两个组合一些有四种方式都可以(C++中)
SORT = Sort2; SORT(1);
SORT = Sort3; SORT(1);
}
int main()
{
TestFunPointer();
return 0;
}
当然这种指向函数的指针真正应用的时候会用到typedef限定符,因为这样会比较方便易懂。调用方式如下:
typedef (*PSORT)(int n);
void TestFunPointer()
{
PSORT sort = Sort1;sort(1);
(*sort) = &Sort2; (*sort)(1);//这里用到了上面讲的不同种的调用方式。
}
7.指向类的成员函数的指针
#include <iostream>
using namespace std;
class CA
{
typedef int((CA::*PClassFun)(int ,int));
public:
int max(int a, int b)
{
return a>b? a:b;
}
int min(int a, int b)
{
return a<b? a:b;
}
int Result(PClassFun fun, int a, int b)
{
//像上面说的调用类的成员函数的时候必须加上“*”,因为这个函数相当于变量
return (this->*fun)(a,b);
}
};
void TestMemberFun()
{
CA ca;
int a = 3; int b = 4;
printf("test member fun\n");
//所以成员函数必须用&来调用,因为其本质是变量
printf("max result = %d\n", ca.Result(&CA::max, a,b));
printf("min result = %d\n", ca.Result(&CA::min, a,b));
}
int main()
{
TestMemberFun();
return 0;
}
//输出结果:
//test member fun
//max result = 4
//min result = 3
8.指针操纵二维数组
#include "stdafx.h"
#include <iostream>
using namespace std;
int main()
{
int i, j;
int a[2][3] = { { 1, 2, 3 }, { 4, 5, 6 } };//定义一个二维数组a
int b[2][3] = { 1, 2, 3, 4, 5, 6 };//定义一个二维数组b
int s[2][3];//声明一个二维数组 s用来存放两个数组的和
int *p, *q, *su;//声明三个int类型的指针 用来指向三个二维数组
//*****下边是分别指向*****
p = a[0];
q = b[0];
su = s[0];
int sum;
//******用指针来操作二维数组******
for (i = 0; i<6; i++)
{
sum = p[i] * q[i];
su[i] = sum;
}
//******用数组本身来操作二维数组******
printf("the sum of the arrays is \n{");
for (i = 0; i<2; i++)
{
for (j = 0; j<3; j++)
{
printf("%d ", s[i][j]);
}
printf("\n");
}
printf("}");
getchar();
}
具体来说指针和二维数组的关系如下:
int b[2][3] = { 1,2,3,4,5,6};
int* p2 = b[0];
此时我们有
// p2[0] = b[0][0] ,p2[1] = b[0][1] ,p2[2] = b[0][2]
// p2[3] = b[1][0] ,p2[4] = b[1][1] ,p2[5] = b[1][2]
9.指针动态分配内存
1.主要实现方式为:
在程序执行的过程中,利用new动态的分配所需要的资源,并用指针跟踪这些资源。最终实现动态资源分配。
2.使用方式:
typename* ptr = new typename;
new后面的typename指定了当前变量类型以及该类型所需要的字节数。前面的typename*指定了当前指针变量的类型。需要注意的是:利用new分配的内存称为堆(heap)内存区域。而普通定义的变量所分配的内存称为栈(stack)内存区域。利用关键字delete可以释放new分配内存,delete只能释放new分配的内存。利用delete释放内存块时,需指定指向该内存块的指针(之前用new分配内存块时指向的指针)。如:
int* ptr = new int;
...
delete ptr;
3.new关键字真正的用武之地1
利用new动态分配数组,即能够使得数组元素个数无需预先确定,可在程序运行过程中根据需要制定,具体操作如下:
int* ptr = new int[10]; //其中ptr表示的是指向了十个元素的指针变量,也就是数组的首地址。
...
delete [] ptr; //需要知道删除的是数组,所以需要添加[]
4.new关键字真正的用武之地2
利用new动态分配结构or类。(C++中结构和类用法基本一致)。具体操作如下:
//首先定义一个未命名的结构
struct str1
{
char name[20];
float volume;
doduble price;
};
//利用new分配内存块
str1* ptr = new str1;
//利用“.”或者“->”访问结构内部的变量
//注解:什么时候用“.”,什么时候用“->”:
//若结构的标识符是结构名,用“.”;若结构的标识符是指向结构的指针用“->”
cin.get(ptr->name,20);
cin.get(ptr->volume);
cin,get((*ptr).price);
10.指针和引用
1.引用
定义引用时没有初始化是错误的 :
/正确
int ival = 1024;
int &refcal = ival;
//错误
int &refval; //错误原因:引用在定义的时候必须初始化
int &refval = 10;//错误原因:引用必须是一个变量(对象)
引用是变量的别名,所以对引用进行操作时,实质是对引用的那个变量进行操作:
int ival = 1024;
int &refval = ival;
refval += 2;
cout<< "refval=" << refval << endl;
cout<< "ival=" << ival <<endl;
//输出时ival也被加了2.
引用就是对象的另一个名字,实际应用中引用主要用作函数的形式参数,或函数返回。
#include <iostream>
#include <vector>
using namespace std;
class VectorRef
{
//定义类变量
std::vector<int> vecInts;
public:
//构造函数
//初始化压入向量容器0~4
VectorRef(int size = 5)
{
for(int i =0; i< size; i++)
vecInts.push_back(i);
}
//将类变量vecInts以引用的形式返回
std::vector<int> &GetVecInts()
{
return vecInts;
}
};
//将引用作为函数参数
void PrintVecInts (const std::vector<int> & vecInts)
{
printf("\n");
for(int i = 0; i< vecInts.size(); i++)
printf("%d current value = %d\n",i,vecInts[i]);
}
void TestVecInts()
{
//定义一个类,此时自动调用了构造函数,已经填充了数字
VectorRef vRef;
//返回引用,不会发生拷贝构造,v和vRef是同一个东西,如图1
vector<int>& v = vRef.GetVecInts();
//返回非引用,会发生拷贝构造,就是v变量vRef是不会变的,如图2
vector<int> v = vRef.GetVecInts();
v[0] = 100;
PrintVecInts(v);
PrintVecInts(vRef.GetVecInts());
}
int main()
{
TestVecInts();
//getchar();
return 0;
}
2.指针和引用的比较
引用存储的是值,而指针存储的是地址。
引用只能对已经存在的对象进行,而指针确可以定义为空。
引用直接访问不需要定义内存空间,而指针需要有自己的内存空间。
网友评论