一、从零了解指针
- 就是存放地址的 变量
- 无论指向什么类型,所有的指针的大小都是一样的4个字节。(32位的平台上)。
- 且指向不同类型的指针不能相互赋值。
怎么定义指针变量 ?
-
int p; 或者 int p; int* p; 和 int *p; 两者意思相同
int* a,b,c;
int *a,*b,*c;
int i ;
int* p = &i ;
值得注意的一点
变量的地址与变量的值
- 我个人认为学习下面的知识点得明白变量的地址与变量的值有什么区别。
- 先假设一下,内存就是酒店;酒店里有很多房间,其中房间号就是:内存地址;房间里的人就是:储存的数据;
- 以下面为例:
? 定义一个变量a,变量a的地址假设为0x123,就当作房间号是0x123
? 赋给a值,就相当于人。就当作变量的值
-
int a = 10;
-
假设变量的地址为0x123
用指针作为参数
- 指针作为变量,在被调用时得到某个变量的地址
- 在函数里可以通过这个指针访问到函数外面的 i(如何操作看下面的《访问指针》)
访问指针
-
访问(读、写)-------访问即我可以获取它,也可以修改它。 -
*是一个单目运算符(不是乘号的意思)。用来 访问指针的值所表示的地址上的变量的值(访问指针所指向变量的值。 -
可以做左值,也可以做右值 -
比如,int i = 6; int* p = &i; 则p的值就是 i 的地址。而我用 *p,则是访问p的值所表示的地址上的变量的值(即我可以获得 i 的值,或者我可以修改 i 的值。 也可以将*p看成一个整体,当做整数。
void f(int* p);
int main() {
int i=6;
printf("&i = %p\n", &i);
f(&i);
return 0;
}
void f(int* p) {
printf("p = %p\n",p);
printf("*p = %d\n",*p);
*p = 60;
}
二、指针有什么用
交换两个变量的值
为什么要用指针交换?因为不用指针交换你传入到方法参数后你得到的只是传过来变量的值,你如果得到的是它的值的话,你就不能够交换它的那个的值。但是如果它传入的是变量的地址的话。那就可以。
void swap(int* pa, int* pb);
int main() {
int a = 11;
int b = 88;
swap(&a, &b);
printf(" a = %d,b = %d", a, b);
return 0;
}
void swap(int* pa, int* pb) {
int t;
t = *pa;
*pa = *pb;
*pb = t;
}
题外话:
int a ;
int b ;
a = b = 100;
函数返回多个值
由于函数不能返回多个值,这个时候用指针可以返回多个值。
因为将目标变量(min和max)的地址传入参数,并将最小值和最大值传给min和max。
从而实现了返回多个值的目的。
注:数组也行。
#include <stdio.h>
void minmax(int a[], int len, int* min, int* max);
int main() {
int a[] = {1,2,3,4,5,6,7,1,234,34,2,1,45,32,3,32,4,5,567,23,0,4,34};
int min, max;
minmax(a,sizeof(a)/sizeof(a[0]),&min,&max );
printf("min = %d\nmax = %d\n",min,max);
}
void minmax(int a[], int len, int* min, int* max) {
int i;
*min = *max = a[0];
for (i = 1; i < len; i++) {
if (a[i] < *min) {
*min = a[i];
}
if (a[i] > *max) {
*max = a[i];
}
}
}
有一种情况是这样的:
- 函数本身返回运算的状态,结果通过指针返回(就是函数和指针分开返回)
- 一般是让函数返回特殊的不属于有效范围的值,来表示出错(0 或者 -1)
- 但是当任何数值都有可能是有效数值的话。就得分开返回了。
注:这个可以用异常解决,我也不知道为啥要列出这个情况。但是还是了解下比较好。
#include <stdio.h>
int main() {
int a = 10;
int b = 2;
int c;
if (divide(a, b, &c)) {
printf("%d / %d = %d\n",a,b,c);
}
return 0;
}
int divide(int a ,int b ,int *result) {
int ret = 1;
if (b == 0) return ret = 0;
else {
*result = a / b;
}
return ret;
}
指针常见的错误
int *p;
(无论哪种说法都是说明不能犯这种错误)
数组与指针
函数参数里的数组
**结论:**函数参数表中的数组,实际上就是指针,但是我们可以用数组的运算符 [] 进行运算
void jkl(int a[]);
int main() {
int a[] = {1,2,5,2,3,5,9};
printf("传入前 a[0] = %d\n",a[0]);
jkl(a);
printf("传入后 a[0] = %d\n",a[0]);
return 0;
}
void jkl(int a[]) {
a[0] = 10000;
}
- 可得数组可以和指针一样 ,而且想到前面要用sizeof获取数组的个数的时候,不能直接获取,而单个数组单元获取是4个字节,和指针一样大。(所以有没有一种可能是数组的本质就是指针呢)
- 我们再做一个实验,将 jkl 的参数改成指针。并将传入的数组当成指针
? 看看结果能否和参数为数组的时候一样。
void jkl(int a[]);
int main() {
int a[] = {1,2,5,2,3,5,9};
printf("传入前 a[0] = %d\n",a[0]);
jkl(a);
printf("传入后 a[0] = %d\n",a[0]);
return 0;
}
void jkl(int *a) {
a[0] = 10000;
}
- 事实证明:数组传入指针变量参数时,我们可以将其当作数组来使用
- 所以函数参数表中的数组,实际上就是指针,但是我们可以用数组的运算符 [] 进行运算
数组变量是特殊的指针
-
因为数组变量本身表达地址,所以我们要取数组地址的时候不需要 & 符号 -
但是数组的**单元(就是数组[0],数组[1]之类的)**表达的是变量,要获取其地址还是需要用 & 符号的; int arr[100];
int *p ;
p = arr;
- **需要注意的一点是:**上面直接将数组地址给p,给的是首地址.不是首元素地址
? **数组的首地址:**表示整个数组的地址。
? **数组的首元素地址:**表示数组的首个元素的地址。即数组[0]的地址
指针变量可以当作数组
- 指针当作数组,指针指向的变量当作数组的首元素 p[0]
啥意思呢??
看代码就懂了:
int jjj[100] = { 1000,12 };
int kkk = 100;
int* p = &kkk;
printf("*p = %d \n",*p);
printf("p[0] = %d\n",p[0]);
p[0] = 999;
printf("p[0] = %d\n",p[0]);
printf("kkk = %d\n",p[0]);
- 反过来也成立。可以将数组当成指针来使用。同样只有首元素起作用
int j[100] = { 1000,12,213,432 };
printf("j[0] = %d\n", j[0]);
printf("j[0] = %d\n", *j);
*j = 999;
printf("j[0] = %d\n", j[0]);
printf("j[0] = %d\n", *j);
- 虽然我感觉这个知识点没啥用,但是还是学一下好,起码以后看到不至于不知道啥意思。
指针与const
-
const 在星号*前面则表示,指针所表示的东西不可修改。 -
可这样写 -
int const *p;//和下面的意思一样 -
const int*p; 啥意思呢?看代码 int i = 100;
int const* p = &i;
printf("i = %d\n", i);
*p = 999;
i= 888;
printf("i = %d\n", i);
-
const 在星号*后面,则表示指针不可修改 -
int *const p ; 看代码: int i = 100;
int a = 299;
int *const p = &i;
printf("p = %p\n", p);
printf("i = %p\n", &i);
p = &a;
const数组:
- const int arr[] ={1,2,3,4};
- 则数组变量已经是const的指针了,并且const表示的是,数组里的每个单元都是const int.
- 所以必须通过初始化进行赋值.
将非const的值转换成const的(在结构体会用到)
顾名思义,就将非const的值转化成const,保证传进来的参数的值不会变。
const的也可以转成const,不影响。
什么时候需要这样做?
- 当要传递的参数的类型比地址大的时候,这是常见的手段:既能用比较少的字节数传递值给参数,又能避免函数对外面的变量的修改。(后面会学)
void f(const int* a){
int c = 888;
f(&c);
}
指针运算
-
如果给指针加上**(运算符,+,+=,-,-=)** -
指针+1(单元往后挪一位) -
结果会是输出首元素的后一个单元的数值(注意:这里不一定非要是首元素,看你怎么用); -
注意:无论是double还是char的数组都是如此; -
int arr[] = { 1,2,3,4,5 };
int* p = arr;
printf("arr[0] = %d\n",arr[0]);
printf("arr[0] = %d\n",*p);
printf("arr[1] = %d\n", *(p+1));
-
如果指针相减会发生什么? -
先说结果:相减他们地址的十六进制(地址的最后两位),会相减(直接化成十进制再相减也行)。然后化成十进制再/4。就是输出的整数结果。 int a[] = { 1,2,3,4,5 };
int* p1 = &a[1];
int* p2 = &a[2];
printf("p1 = %p\n",p1);
printf("p2 = %p\n",p2);
printf("p2-p1 = %d",p1-p2);
**{p++,指的是 (p++),但是因为++的优先级比星号高,所以可以不用写括号}
指针比较
- < ,<= ,== ,> ,>= ,!= 。都是可以对指针做的。比较他们在内存中的地址。
0地址(了解一下就行)
-
任何程序都有零地址 -
|