指针学习笔记——内容多来自中国大学mooc翁恺老师C语言入门视频
一 .指针学习笔记
9.1-1 取地址运算:&运算符取得变量的地址
(1)&运算符
- 这里的&指的就是scanf(“%d”,&i)里面的&
- 获得变量的地址,它的操作数必须是变量
- 地址的大小是否与int相同取决于编译器
- 我们用要printf出一个变量的地址要这样干
//得用printf(“%p”,&i);而不是用printf(“%x”,&i)//
#include <stdio.h>
int main(void)
{
int i=0;
printf("%p",&i);
return 0;
}
(2)相邻变量的地址
相邻两个变量相差4,原因就在于每个变量占四个字节,并且先定义的变量是更大的,究其原因就在于在堆码中内存分配是自上而下的
如图:
(3)数组的地址
int a[10];
printf(“%p\n",&a)会等于printf(“%p\n”,a)也会等于a[0]的地址,同理数组中每相邻的两个变量的地址也会相差四。
#include <stdio.h>
int main(void)
{
int a[10];
printf("%p\n",&a);
printf("%p\n",a);
printf("%p\n",&a[0]);
printf("%p\n",&a[1]);
return 0;
}
9.1-2 指针 :指针变量就是记录地址的变量
(1)指针
-
作用:就是用来保存地址的变量
-
int i;
int* p=&i;
这个 * 的意思就是告诉你p是一个指针,然后我们把i变量的地址交给了p这个指针(也可以叫做p指向了i),这个 “*”可以远离int,也可以靠近int,都是一个意思哒。 -
还有一个要注意的点就是定义两个指针时不能这样写
int *p,q;
这样q是没有被定义到的,应该这样写
int *p,*q;
(2)指针变量
- 普通变量的值是实际的值
- 而指针变量的值是具有实际值的变量的地址,指针变量指向存有地址的普通变量
(3)作为参数的指针
- void f(int *p) int i=0; f(&i)
我们可以把变量 i 的值交给 f 函数中的 p 指针,这样在 f 函数里面我们就可以通过这个指针访问外面的这个 i 了,而通过简单的将 i 的值交给某一个 g 函数就做不到这些功能。
我们用图像来描述会更加形象
(4)访问那个地址上的变量*
- *是一个单目运算符,用来访问指针的值所表示的地址上的变量
- 其既可以做左值也可以做右值
eg.int k=*p;
*p=k+1;
(5)错误传入地址而没有报错
原因就在于scanf把你传入的实际值误认成了那个变量的地址,虽然编译没有报错,但运行一定会出错!!!
9.1-3指针的使用:指针有什么用?
(1)指针应用场景一——交换两个变量的值
eg.
#include <stdio.h>
void swap(int *pa,int *pb);
int main(void)
{int a=3;
int b=9;
swap(&a,&b);//把下面调换的ab的地址重新调用到上面来//
printf("a=%d b=%d\n",a,b);
return 0;
}
void swap(int *pa,int *pb)
{ int t=*pa;
*pa=*pb;
*pb=t;
}
(2)指针引用场景二
- 函数返回多个值时,某些值只能通过指针返回
- 传入的参数实际上是需要保存带回的结果的变量
eg.
利用指针来遍历出一个数组中的最大最小值
#include <stdio.h>
void minmax(int a[],int length,int *max ,int *min);
int main(void)
{int min,max;
int a[]={2,5,7,8,45,76,890,56};
minmax(a,sizeof(a)/sizeof(a[0]),&max,&min);
printf("min=%d max=%d",min,max);
return 0;
}
void minmax(int a[],int length,int *max ,int *min)
{
int i;
*max=*min=a[0];
for (i=1;i<length;i++)
{
if(a[i]>*max)
{*max=a[i];
}
if(a[i]<*min)
{*min=a[i];
}
}
}
(3)指针引用场景三——返回值
在平时我们都是让函数返回一些特殊值(像是-1,1,0)来让函数返回运算状态,
但当所有的值都有可能成为运算结果时,就得利用指针来进行函数返回了。
eg.当除法运算中分母为一时也能返回运算
#include <stdio.h>
int divide(int a,int b,int *result);
int main(void)
{int a=0;
int b=0;
scanf("%d %d",&a,&b);
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)
{ret=0;
}
else
{*reslut=a/b;
}
return ret;
}
(4)指针使用的常见错误
在一个指针还未取得某个变量的地址时就对其赋值会导致程序崩溃
9.1-5 指针与const
(1)指针是const
-
指针一旦得到了每个变量的地址,就不能再指向其他变量了
eg.就像是int const q =&i,那么q就不能再接受其他的变量的地址了 -
但是q=26等访问是可以进行的,因为q所指的int i并不是const
-
再者q++,q–等运算就不能再进行了
(2)所指是const
- 表示:const int *p=&i;
- 它可以去指向其他的变量,获得其他变量的地址
- int i 变量也可以去进行改变,比如说 i++,i=27;
- 唯一不能做的就是通过p去改变 i 的值,即p=26 是不可以做的(通过指针不可修改)
(3)辨析题
int i;
- const int*p1=&i;
- int const *p2=&i;
- int const p3=&i;
技巧:只要在const的后面就是(1)情况,如果在后面的话就是(2)情况,指针不可修改。
(4)const数组
eg.const int a[]={1,2,3,4,5,6,7};
- 数组变量已经是const的指针了,这里的const表明数组的每个单元都是const int
- 这样const数组之后就表明数组中的每个int都被const了,像是a[0],a[1]这些都是不可以改变的。
- 所以这个样子的数组就必须通过初始化来进行赋值。
(5)保护数组
在把数组传入函数的时候我们传入的是数组的地址,所以在函数中是可以改变数组的值的,所以为了保证数组的值不被改变我们就可以通过const数组(设置参数为const)
eg.int sum( const int a[ ] , int length);
9.2-1 指针运算
(1).先导(有关知识点)
-我们再指针上加 1 并不是在地址上加 1 ,而是在地址上加了个指针类型,你指针是int,你就在地址上加4,你是char的话就在上面加个 1
{ 注意:sizeof(char)=1,而sizeof (int)=4;}
#include <stdio.h>
int main (void)
{
char ac[]={1,2,3,4,5,6,};
char *p=ac;
printf("p=%p\n",p);
printf("p+1=%p\n",p+1);
int ai[]={1,2,3,4,5,6,7,};
int *q=ai;
printf("q=%d\n",q);
printf("q+1=%d\n",q+1);
return 0;
}
用以上代码就可证明此结论
注意点:* 这个的运算级高于±号,所以我们在表示数组中的下一个数是要加一个括号呐
eg.*p——a[0]
*(p+1)——a[1]
可以用以下代码进行检验
#include <stdio.h>
int main (void)
{
char ac[]={1,2,3,4,5,6,};
char *p=ac;
printf("p=%p\n",p);
printf("p+1=%p\n",p+1);
printf("*(p+1)=%p\n",*(p+1));
int ai[]={1,2,3,4,5,6,7,};
int *q=ai;
printf("q=%d\n",q);
printf("q+1=%d\n",q+1);
printf("*(q+1)=%d\n",*(q+1));
return 0;
}
(2). 指针计算
- 可以给指针加减一个整数
- 指针也可以进行递增递减
- 两个指针还能相加减
#include <stdio.h>
int main (void)
{
char ac[]={1,2,3,4,5,6,};
char *p=ac;
printf("p=%p\n",p);
printf("p+1=%p\n",p+1);
printf("*(p+1)=%p\n",*(p+1));
printf("*(p+1)-*p=%d\n",*(p+1)-*p);//此行可证两个指针可以相减//
int ai[]={1,2,3,4,5,6,7,};
int *q=ai;
printf("q=%d\n",q);
printf("q+1=%d\n",q+1);
printf("*(q+1)=%d\n",*(q+1));
return 0;
}
但要注意的是相减得到的不是地址数的差值,而是 差值/每个变量对应的字节数
(3).有关*p++的内容
- 可以先去除p所指的那个数据,完事之后顺便把p移到下一个位置上去
- *星号的运算级虽然高,但是没有++运算级高
- 常用于数组类的连续操作(也就是有了一种新的遍历数组的方法)
如下
#include <stdio.h>
int main (void)
{
char ac[]={1,2,3,4,5,6,-1};//可以在数组的最后放上一个不可能取到的数//
char *p=ac;
while(*p!=-1)
{printf("%d ",*p++);
}
return 0;
}
(4).指针比较
- <,<=,==…这些都可以对指针进行
- 但比较的是他们在内存中的大小(就是比较地址的数值)
- 还有就是数组中的单元肯定是线性递增的
(5). 0地址
-
0地址一般是不能碰的,也不干活的,所以可以用0地址来干一些特殊的事情
eg.返回的指针是无效的;指针没有被真正初始化(先初始化为0) -
NULL是一个预先定义的符号,表示)地址,所以以后尽量用NULL(必须大写)来表示0地址
指针的类型
- 无论是什么类型的指针,其大小都是一样的,因为都是地址
- 但是指向不同的指针是不可以相互赋值的,这是为了避免用错指针
就比如说你让
char *a;
int *b;
*a=*b;
这样程序是运行不了的,原因就在于指针的大小是统一的,但是每一种变量的字节是不一样的呀!!!
(6).指针类型转换(初学者不建议这么干)
- void*可以用来表示不知道指向什么的指针(计算时与char相同,但是不相通哈)
- 指针类型转换
像这个样子呐 int *p=&i;void q=(void)p;
(7).小结 -指针可以用来干神马
-
需要传入较大的数据是用作参数
-
传入数组后对数组做操作
-
函数返回不止一个结果
- 需要用函数来修改不止一个变量
-
动态申请的内存…
9.2-2 动态内存分配
(1).malloc(做的事情就是跟系统要块内存来)
- 要这个头文件
#include <stdlib.h>
向malloc申请空间的大小是以字节为单位的
void * malloc(size_t size);
返回的结果是void*,需要类型转换为自己需要的类型
(int*)malloc(n*sizeof(int)) - 空间不足的话就会输出NULL(0位置)
#include<stdio.h>
#include <stdlib.h>
int main(void)
{
int count =0;
void *p;
while((p=malloc(100*1024*1024)))
{count++;
}
printf("分配了%d00MB的空间\n",count);
return 0;
}
//可以看一下用自己的电脑能分配到多少空间//
(2).free()
- 得把申请得来的空间还给系统
- 只能还申请来地空间的首地址
(3).常见问题
- 申请了没有free()——长时间运行内存逐渐下降
- free过了再free
- 地址变过了,直接去free
//啊累死了累死了//