指针
指针就是保存地址的变量,想一下,无论什么类型的指针,因为地址的所占用的空间是一样的,所以指针所占用的字节应该是一样的。后面不妨给出验证。我们常用的写法无外乎向下面这样
int i;
int* p = &i;
int* p,q;
int *p,q;
指针变量
普通变量的值是实际的值
指针变量的值是具有实际值的变量的地址
我们这里不妨就验证一下指针所占的空间到底是不是一样的。
#include<stdio.h>
int main(){
char* p = NULL;
int* q = NULL;
int i = 5;
char c = 6;
q =&i;
p =&c;
printf("p's size is %d\n",sizeof(p));
printf("q's size is %d\n",sizeof(q));
return 0;
}
我们在cpp.sh上编译运行的结果是
Paste_Image.png而在我的电脑上运行的结果是
Paste_Image.png对呀,同样的代码,为什么有不同的结果呢?因为我们的运行环境不一样,在我电脑中指针只占用4的字节,而在cpp.sh服务器上每个指针占用8的字节。但是我们要强调的重点是我们char类型的指针和int类型的指针占用 的字节数在同一机器架构下是一样的,也就验证了我们刚刚所言。
作为参数的指针
void f(int* p);//在调用的时候得到某个变量的地址
int i=0;
f(&i);//在函数里面可以通过这个指针访问外面的变量i
访问对应地址中的元素
*是弹幕运算符,用来访问指针所指地址上的变量
可以作为右值也可以作为左值
int k = *p;
*p = k+1;
指针的应用场景
场景一
交换两个变量的值
我们都写过交换两个变量的swap函数,但是当我们从形参传进来两个值的时候,我们能在函数内交换,但是一旦离开了函数,原本的变量根本没有变化,因而我们要传入地址进来
void swap(int *pa, int *pb){
int temp =*pa;
*pa = *pb;
*pb = temp;
}
这样我们就能交换两个变量的值了
场景二
函数要同时返回多个值
一般情况我我们通过函数的return语句返回值的时候只能返回一个,但是如果我们要同时返回多个值该怎么办呢?这时候我们通过参数传入地址,然后通过指针把要返回的值带回到函数外。举个例子:我们写个函数求数组中的最大最下值。
#include<stdio.h>
void maxmin(int a[], int len, int *max, int *min){
*max = a[0];
*min = a[0];
for (int i = 0; i < len; i++){
if (*max < a[i])
*max = a[i];
if (*min>a[i])
*min = a[i];
}
}
int main(){
int num[] = { 1, 10, 5, 8, 9, 6, 7, 25, 6, 3, 4 };
int max;
int min;
maxmin(num,sizeof(num)/sizeof(int), &max, &min);
printf("max = %d\n", max);
printf("min = %d\n", min);
return 0;
}
Paste_Image.png
在maxmin函数中,我们返回值类型是void,也就是不通过return返回值,但是我们确实得到了max和min,如何做到的?那就是我们穿进去两个地址,在函数内部把地址上的内容改了,所以函数结束以后,我们能得到我们想要的。我们在进一步探索一下,maxmin函数的参数列表中除了我们要传入的数组int a[], 两个指针变量int *max和int *min,还多了一个长度int len 为什么我们不在函数内部做sizeof(a)/sizeof(int) 来得到数组的长度呢?
我们来看一个例子
为什么我们的数组才占了4个字节呢?显然,此时a并不表示数组了,那么我们传进来的到底是什么呢?4个字节刚好是一个指针所占用的字节数,没错此时的a就是一个指针。那么我们能否像操作指针一样操作a呢?我们来试一下:
Paste_Image.png没错我们可以像操作指针一样操作a,所以a其实就是一个指针,所以我们在函数内部是不能通过sizeof(a)/sizeof(int)来求得数组的个数的,那么我们只能再传进来一个参数len。
指针最常见的错误
定义了指针变量,还没有指向任何变量就开始使用指针
当你定义了一个指针变量的时候,由于没有指向任何变量,所以它里面的内容是杂乱无章的,可能是个可以访问的地址,也可能是不可访问的地址,无论哪种都不是我们想要的。
vs提示我们是使用了未初始化的变量,所以连编译都过不去
Paste_Image.png我们在cpp.sh上运行了一下,通过了,能正常运行,但这不总是那么幸运,万一我们的p内的地址是0,这个特殊的地址,我们的程序就会崩掉。
Paste_Image.png我们把p的值改为了0,程序一直得不到结果,在vs下呢
Paste_Image.png一样的结果,因为我们的0地址是无法访问的。
一般我们用NULL来表示0地址。
指针与const
在使用指针的时候我们经常会看到const,一脸懵逼,你能正确分辨下面的几种写法么?
int i;
int *const p1=&i;
const int *p2=&i;
int const *p3=&i;
三种写法有两种意思,const在*之前,表示指针是const,也就是说指针只能指向这个变量,不能改变,所以一般声明和初始化一起。const在*之后表示不能通过该指针来改变变量的值,并不是说变量不可改变,或者指针不可改变,而是通过该指针改变该变量的值是不允许的。
强制类型转换
void* 这种类型的指针,表示某个地址,但是不明确指出所存储是数据类型。一般用在比较底层的操作中,例如动态分配内存的时候,我们只要告诉系统,请求分配多少字节的内存,并不需要告诉它用来存什么数据 ,所以malloc返回值类型为void*
必要的时候我们可以通过强制类型转换来改变指针的类型
void* p = malloc(128);
int * q = (int*) p;
malloc动态分配内存
有时候我们不知道数组的大小,要传入一个变量n到数组中,在C99之前是不允许的,这时候就要动态分配内存了
具体做法
记住不仅仅如此,我们借了内存,还要还回去,不然,有借无还,最终内存会用光的。我们用free函数还回内存
Paste_Image.png指针的运算
指针是地址,地址是可以运算的,有加减运算,比较运算,
那么指针加一是否是地址加一呢?
不是的,指针加一,表示指向下一个单元,如果是int*类型的指针,指针加一,则地址加4,如果是char*类型的指针,指针加一,则地址加一。
网友评论