C++指针
露忆丶十二 人气:0什么是指针
C/C++语言拥有在程序运行时获得变量的地址和操作地址的能力,这种用来操作地址的特殊类型变量被称作指针。
翻译翻译什么tmd叫tmd指针!
变量或常量的指针存储的数据是 :对应的变量或常量在内存中的地址。
图解:
此时 我们定义三个指针 p1, p2, p3 分别指向a, b, c ,那么p1中存储的数据是变量a所占用内存的首地址:0x00;
b和c中存储的数据是什么呢? 没错,就是0x02和0x04。
显然。若要知道变量a的值,首先读取指针p1中的数据0x00,然后读取内存0x00和0x01中的数据就可以了。这个时候,会出现一个问题:怎么才能确定究竟要读取几个单位内存中的数据呢?毕竟p1中只存储了0x00呀。这个时候 ,指针的类型就发挥了作用。举个例子:如果定义的指针是int类型的,那么读取的时候自动从指针中存储的初值向后读取4个字节;float类型的指针就是读取8个字节。这样就可以既完整又不多余的取出所需要的数据了。
总结一下一个指针的两个要素:指针类型和指针所指变量的初地址。
定义指针变量
指针在定义的时候最好直接初始化,否则可能出现意想不到的结果。
@定义一个指向变量的指针。
/*定义一个指向int类型变量a的指针p*/ int a = 3; int * p; p = &a; //把a的地址赋值给p; /*下面的语句最好不要用!!!!*/ p = &28; //这也是可以的,p指向的地址中存储的数据是28. /*可以用下面的语句代替上面的语句*/ *p = 28;
间接引用指针
间接引用操作符也是 *
*p 取出p指向的内存空间中的值。
所以区分定义或是引用指针可能有些麻烦。
只有同类型的指针和变量才能通过 * 和 & 互相建立关系。
int a = 0; int temp; int * p = &a; /*指针的间接引用*/ cout<< *p; //输出结果是0 /*以下两种表达等价*/ *p = 3; //把3赋值给a a = 3; /*以下两种表达效果相同。都是把变量a赋值给temp*/ temp = a; temp = *p; //temp被赋值为0 /* 以下表达是错误的*/ float f = 3.1; int * p = &f; //error,指针类型与变量类型不匹配
常or常常
这个部分本来应该在定义指针变量里,但是因为需要指针的间接引用作为铺垫,所以把它单独拿出来了
我们先区分一组概念:常量、指向常量的指针、常指针以及指向常量的常指针。
1.常量:顾名思义不发生改变的量,想必大家是熟悉的。
2.指向常量的指针:只限制间接访问操作,不限制指针指向的值本身的操作规范。
3.常指针:指针中存储的地址一经初始化就不能改变了,也就是说常指针只能指向一个固定的地址,但是地址中存储的数据是可以改变的哦~
4.指向常量的常指针:根据上面三个概念大家应该可以理解了,就是指针中存储的地址和该地址中的数据皆不可更改。
例如:
int b = 4; const int a = 5; //定义一个常量a。 /*注意观察以下三个定义中const的位置*/ const int *p = &a; //定义一个指向常量的指针p,指向常量a。 p = &b; //这个也是没问题的哦 *p = 6; //error,这个间接访问操作是不可以。 int * const p2 = &b;//定义一个常指针p2 *p2 = 6; //这个是可以的,注意区分和上面的区别 const int * const p3 = &a; //指向常量的常指针p3 /* emm我也不知道该注释点什么,自行体会吧~*/ *p = 11; //error,因为p指向的是常量,常量的值不可更改 *p2 = 2; //true,p2是常指针,指向的地址不变,但地址中的值可以更改 *p3 = 4; //error p3 = &b; //error
指向指针的指针
指针本质上也是一个变量或常量,那么指针也是有地址的,而指向这些地址的指针被称为指向指针的指针。
int a = 25; int * p = &a; int pp = &p; //true,pp指向的地址中存储的是指针p的地址。
指针与数组
一个数组的数组名就是一个常指针。
int arr[] = {5,4,6,9,8,3};
arr就是一个指针,而且指向数组的第一个元素arr[0]。
指针数组:
char * arr[] ={"this is", "a", "C++ !"};
在此提及一下字符串常量:
char * a /* 双引号的作用: 在字符串结尾加一个\0,并分配内存空间,返回首地址。 */ a = "dshfw";
指针的运算
指针只能支持 + 和 - 的运算,但这已经足够满足大多数指针操作的需求了。
/*接下来以数组为例,只介绍加法 ,减法同理*/ int arr[] = {5,4,6,9,8,3}; cout<< *p; //输出结果为5 cout<< *(p+1); //输出结果为4 /*打印整个数组*/ for(int *p = arr; p < arr+5; ++p) { cout<< *p<<' '; } cout<<endl;
堆内存分配
C语言
/*函数原型*/ void * malloc(size_t size); /*使用:开辟5个int类型变量的存储空间,返回首地址*/ /**/ int *arr; if(arr = (int *) malloc(5 *sizeof(int)) == NULL) { exit(1); } /*释放堆内存*/ free(arr);
C++语言
注意释放数组内存空间时,delete后有[] !!!
/*申请一个5个元素的数组空间*/ int * arr = new int[5]; delete[] arr; /*申请一个变量的空间*/ int * arr = new int; delete arr;
指针与函数
函数名和数组名一样都是一个指针,有时我们需要把函数名作为参数传入其他函数中。
数组名作为函数的入口参数
arr[] = {4,6,9,8,5,3,2,7,5}; /*两种不同的写法均可以*/ void Sort_Shell(int arr[], int n) { /*code*/ } void Sort_Shell(int * arr, int n) { /*code*/ } /*下面这种是不可以的!!!*/ void Sort_Shell(int * arr[], int n) { /*code*/ }
举个小小的例子
/*来个小小的希尔排序算法*/ void Sort_Shell(int arr[], int n) { int gap = n / 2; for(; gap > 0; gap /= 2) { for(int i = gap; i <n; ++i) { int temp = a[i]; int j = i; while(j >= gap&& temp < arr[j - gap]) { a[j] = a[j - gap]; j -=gap; } a[j] = temp; } } }
开了个小差,接下来回归正题,我们的指针。
函数名作为参数传入其他函数
static bool cmp(int a, int b) { return a < b; } void show(bool * b) { if( &b) { cout<< true; } else { cout<<false; } }
使用指针修改函数参数
首先来看个例子:
void swip(int a, int b) { int c = a; a = b; b = c; } int main() { int a = 5, b = 6; swip(a, b); cout<<a<<' '<<b; }
输出结果:5 6
并未达到交换的效果,因为函数内部对形参的修改并不能反映到上层的main函数中。
此时我们可以通过指针作为函数的入口参数来实现预期的功能。
void swip(int * a, int* b) { int c = *a; *a = *b; *b = c; } int main() { int a = 5, b = 6; swip(&a, &b); cout<<a<<' '<<b; }
输出结果:6 5
传递函数的指针虽然能达到预期效果,但是确实以破坏函数的黑盒为代价,可读性差,调试困难。
有没有什么更好的办法呢?接下来 引用登场了。
变量的引用作为函数的参数
void swip(int &a, int &b) { int c = a; a = b; b = c; } int main() { int a = 5, b = 6; swip(a, b); cout<<a<<' '<<b; }
输出结果:6 5
克服了指针作为参数的弊端。话又有点多了,哈哈,这和指针有什么关系呢······
两个常用的字符串函数
- strcmp() :用于比较字符串的大小。
- strcpy() :复制字符串。
//函数原型 int strcmp(const char * arr1, const char * arr2); //使用举例: char arr1[] = "dfetf"; char arr2[] = "cfefef"; int b = strcmp(arr1, arr2);
输出b的值是大于0 的。
总结
指针强大而又危险,却也是C和C++的灵魂,由于其可以直接操作内存地址的特殊性,使得理解指针是如何工作的在C和C++中必不可少,若想成为一名优秀的C++工作者,必须要掌握指针。
加载全部内容