【原创】C++指针详解
指针 一,基础知识 1,内存(memory) 电脑是在CPU里面执行任务的,CPU就相当于人的大脑。CPU计算速度很快,所以我们写程序时,哪怕例如执行100多次循环,看起来似乎都是一秒就能执行好的。然而,CPU的存储能力很差,里面自带的几个寄存器(以32位为例)才只能存储区区32个字节。就算把段寄存器全部用上,也才只有44字节。所以CPU就需要一个东西辅助存储数据。这个存储数据的地方就是内存。 内存就像一个超大规模的住宅区,里面存储着CPU需要执行的数据。相对CPU来说,其实内存是一个外部寄存器。一般来说,程序都是保存在内存中执行的,CPU必须从内存读入执行数据。 2,内存地址 内存很大,为了识别每个内存,我们就需要给每个内存编一个号,这个号码就是内存地址。 二,指针 1,先来看一个例子: #include<stdio.h> int main(void) { int x=0; printf("%i",&x); } 这个时候程序会输出一个奇怪的数字,不是0。这个数据在每次执行的时候会有一些变化,所以这里就不写出执行后输出的值了。 这里“&”是取地址操作符,表示取变量x的地址。这时候输出的就是x的地址。因为每次重新启动电脑的时候,操作系统都会根据当前内存中空余的地址,重新分配地址,所以每次执行时都会有一些小的变化。 2,什么是指针 先来看一个例子。 #include<stdio.h> int main(void) { int a=10; int *p; p=&a; printf("%i",*p); } 执行后会输出10。这里我们重点看第4行和第5行。 int *p表示声明指针变量p。p存放的就是一个内存地址,用“*”指针操作符就可以得到p存放的内存地址所指向的内容了。也就是说,p=&a表示把a的地址赋值给p,然后此时*p的值就是&a地址对应的值,就是a。 3,用指针取得最大值 现在有2个数,请用指针变量取得这2个数的最大值。 #include<stdio.h> int main(void){ int a,b,*p; scanf("%i%i",&a,&b); if(a>b)p=&a;//p指向a else p=&b;//p指向b printf("%i",*p); } 三,指针的加减运算 1,数组 定义数组,我们一般用如下的语句: int a[10]; 这个时候操作系统就会给数组a分配10个连续的地址。注意是连续的。 2,指针的加减运算 #include<stdio.h> int main(void) { int a[3]={1,2,3}; int *p; p=&a[0]; printf("%i", *(++p) ); } 此时会输出2.我们看一下我们的内存: 设程序是从地址main开始运行的,那么内存如下 main1(a[0]) main+42(a[1]) main+83(a[2]) 我们一开始把p指向a[0]所在的地址。++p,就是p的地址往前一个数,增加了sizeof(int),所以就是main+4,对应的数据就是2. 同理,p--就是往前一个地址,到达前面一个数据的空间。 四,指针和数组 1, #include<stdio.h> int main(void) { int a[5]={10,20,30,40,50}; int *p; p=&a[0]; printf("%i\n",*(p+2)); printf("%i\n",a[2]); } 输出结果应该是两个30.p的地址就是a[0]的地址,那么p+2就是a[2]的地址,所以*(p+2)就是a[2]。 同理我们知道,其实p就是a(数组名就是数组第一个元素的地址),所以, *(p+i)=p[i] 其实根据加法交换率,*(p+i)=*(i+p)=i[p]。你可能不相信,把p[i]写成i[p]这样也没错。说白了数组其实就是指针。 为了写起来简便,我们一般把scanf中,&p[i]写作p+i。应该知道为什么吧。 2,(这个是新版本c++语言的写法,老版本c语言不支持) #include<stdio.h> int main(void){ int n,*a,i; scanf("%i",&n); a=new int [n]; for(i=0;i<n;i++){ scanf("%i",a+i); } for(i=0;i<n;i++){ printf("%i ",a[i]); } } 首先输入一个数组元素个数,然后输入数组中的元素,最后输出数组。 这里a=new int [n]表示a向操作系统申请n个空间,这样a就可以当作数组使用了。否则a刚定义时,只有一个空间,不能往后继续存储(要不然就是数组越界),必须先向操作系统申请才能继续。 new是c++新引入的操作符:申请空间。这样做可以避免定义大数组时空间浪费。 顺便说一句,这是c++新引入的东西,老版本c语言是不支持的,所以编译的时候要注意。用c语言编译会出错。因为它不知道new是什么东西。 [Error] 'new' undeclared (first use in this function) 五,指针函数参数调用 现在要编写一个自定义函数swap(a,b),交换a,b变量的值。 错误示范1: void swap(int a,int b){ a=b; b=a; } 错误:因为在a=b赋值完成后,a已经是b的值了,所以此时执行b=a,a已经不是原来的值了,错误。 错误示范2: void swap(int a,int b){ int tmp; tmp=a;//先把a保存起来 a=b; b=tmp; } 还是错误的。不信你运行一下试试,数值根本没变。 然而这么写却是对的。 int main(void){ int a,b,tmp; scanf("%i%i",&a,&b); int tmp; tmp=a;//先把a保存起来 a=b; b=tmp; printf("%i %i",a,b); } 这是怎么会是呢?我们来了解一下函数的传值机制。 首先执行swap函数,参数把main里面的a和b传递给swap里面的a和b。方便描述,我们这里把main里面的用(main)表示,swap里面的用(swap)表示。 此时(swap)a=(main)a,(swap)b=(main)b。注意此时(swap)a和(main)a,(swap)b和(main)b地址不相等。 然后执行玩函数,(swap)a=(main)b,(swap)b=(main)a 然而(main)a,(main)b一点没变。 所以我们需要换一种传值方法。因为函数按值传递,所以我们传递函数的地址就可以了。 void swap(int *a,int *b){ int tmp; tmp=*a; *a=*b; *b=tmp; } 然后再写main。 错误示范3: int main(void){ int a,b,tmp; scanf("%i%i",&a,&b); swap(a,b); printf("%i %i",a,b); } 首先下面出现了几行warning。 [Warning] passing argument 1 of 'swap' makes pointer from integer without a cast [Warning] passing argument 2 of 'swap' makes pointer from integer without a cast 然后运行时成功崩溃了。 (弹出“xxx已停止工作,windows正在查找该问题的解决方案”) 这又是为什么呢?因为函数中,函数参数传送的是地址,a和b都是地址变量,函数中调用*a和*b才是值,所以参数中应该是地址才对。 基本正确的程序示例: int main(void){ int a,b,tmp; scanf("%i%i",&a,&b); swap(&a,&b); printf("%i %i",a,b); } 运行成功。我们来总结一下: 1,遇到修改变量值的函数,调用时要注意用指针,要不然传送时只修改了自定义函数的值,没有修改main函数中的值。 2,调用时注意加上地址符号“&”。是不是觉得这样写很像我们用scanf的时候, scanf("%i",&a) 其实scanf也运用了指针,后面需要写传送参数的地址。 六,结构体指针 1,结构体指针就是指向结构体的指针,其内存地址就是该结构体的第一个元素的地址。 如: struct student a; struct student *stu; *stu=&a; 此段代码中stu指针变量指向的就是a。 2,引用 (*stu).name 表示*stu的name成员。注意括号虽然不好看,但是不能省略。 *stu.name相当于:*(stu.name)。 当然,还有一种更加方便的写法,就是用箭头记号, stu->name。 也就是说, (*a).b=a->b
赞 (0)