@TOC
9.1指针
9.1-1取地址运算:&运算符取得变量的地址
运算符&
- scanf("%d",&i);里的&
- 获取变量的地址,它们操作数必须是变量
- int i;printf("%x",&i);
- 地址的大小是否与int相同取决于编译器
- int i;printf("%p",&i); &不能取的地址
- 不能对没有地址的东西取地址
- &(a+b) &(a++) &(++a)这些都不能取地址 试试这些&
- 变量的地址
#include <stdio.h>
int main()
{
int a=1;
printf("0x%p",&a);
return 0;
}
- 相邻变量的地址
#include <stdio.h>
int main()
{
int a=1;
int b=2;
printf("0x%p\n",&a);
printf("0x%p\n",&b);
return 0;
}
- &的结果的sizeof
#include <stdio.h>
int main()
{
int a=1;
int b=2;
printf("0x%p\n",&a);
printf("0x%p\n",&b);
printf("%lu\n",sizeof((&a)));
return 0;
}
- 数组的地址
- 数组单元的地址
- 相邻的数组单元的地址
#include <stdio.h>
int main()
{
int a[10];
printf("%p\n",a);
printf("%p\n",&a);
printf("%p\n",&a[0]);
printf("%p\n",&a[1]);
}
&数组名,数组名,数组名[0],都表示数组的首地址。
9.1.2指针:指针变量就是记录地址的变量
scanf
- 如果能够将取得的变量的地址传递给一个函数,能否通过这个地址在那个函数内访问这个变量
- scanf("%d",&i);
- scanf()的原型应该是怎样的?我们需要一个参数能保存别的变量的地址,如何表达能够保存地址的变量? 指针 就是保存地址的变量 int i; int* p = &i; int* p,q; int p,q; 这里边的 号只是表示p是存储地址的变量,不是代表int这种类型,也没有int这种类型。 int *p=&a,应为int *p中p是要储存一个地址,所以给int *p赋值时必须是一个变量的地址 int *p,q这里边只有p是存储地址的变量,而q还是一个int型的变量。 指针变量
- 变量的值是内存的地址
- 普通变量的值是实际的值
- 指针变量的值是具有实际意义的变量的地址 作为参数的变量
- void f(int *p);
- 在被调用的时候得到了某个变量的地址;
- int i = 0;f(&i);
- 在函数里面可以通过这个指针访问外面的这个i
# include <stdio.h>
int f(int *p);
int main()
{
int a=1;
int *p=&a;
printf("&a=%p\n",&a);
}
int f(int *p)
{
printf("p=%p\n",p);
}
访问那个地址上的变量*
- *是一个单目运算符,用来访问指针的值所表示的地址上的变量
- 可以做右值也可以做左值
- int k = *p;
- *p = k+1;
# include <stdio.h>
int f(int *p);
int main()
{
int a=1;
int *p=&a;
printf("&a=%p\n",&a);
f(&a);
}
int f(int *p)
{
int i= *p;
printf("%d\n",i);
}
** 左值之所以叫左值*
- 是因为出现在赋值号左边的不是变量,而是值,是表达式计算的结果
- a[0] = 2;
- *p =3;
- 是特殊的值,所以叫做左值 **指针的运算符& * **
- 互相反作用
- *&yptr -> *(&yptr) -> *(yptr的地址)-> 得到那个地址上的变量 -> yptr
- &*yptr -> &(*yptr) -> &(y) -> 得到y的地址,也就是yptr -> yptr
# include <stdio.h>
int f(int *p);
int k(int a);
int main()
{
int a=1;
int *p=&a;
printf("&a=%p\n",&a);
f(&a);
k(a);
}
int f(int *p)
{
int i= *p;
*p=3;
}
int k(int a)
{
printf("%d",a);
}
这个代码中将a的地址传给指针p通过f函数将a的值修改,在k函数中将a的值输出,发现a的值被改变,说明指针可以将地址中的值修改。
9.1-3指针的使用:指针有什么用呢?
指针应用场景一 交换两个变量的值
#include <stdio.h>
int main()
{
int a=5;
int b=2;
jiaohuan(&a,&b);
printf("a=%d,b=%d",a,b);
}
void jiaohuan(int *a,int *b)
{
int t=*a;
*a=*b;
*b=t;
}
取数组中的最大值
#include <stdio.h>
void maxmin(int a[5],int len,int *max,int *min);
int main()
{
int a[5]={34,12,56,23,78};
int len=(sizeof(a)/sizeof(a[0]));
int max;
int min;
maxmin(a,len,&max,&min);
printf("max=%d,min=%d",max,min);
}
void maxmin(int a[5],int len,int *max,int *min)
{
*max=*min=a[0];
for(int i=1;i<len;i++)
{
if(*max<a[i])
{
*max=a[i];
}
if(*min>a[i])
{
*min=a[i];
}
}
}
指针应用场景二
- 函数返回多个值,某些值就只能通过指针返回
- 传入的参数实际上是需要保存带回的结果的变量
- -1或0(在文件操作会看到大量的例子)
- 但是当任何数值都是有效的可能结果时,就得分开返回了
- 后续的余元(C++,Java)采用了异常机制来解决这个问题
#include <stdio.h>
int jisuan(int a,int b,int *c);
int main()
{
int a;
int b;
int c;
printf("请输入被除数\n");
scanf("%d",&a);
printf("请输入除数\n");
scanf("%d",&b);
int count=jisuan(a,b,&c);
if(count==1)
{
printf("%d/%d=%d\n",a,b,c);
}
else
{
printf("除数不能为零");
}
}
int jisuan(int a,int b,int *c)
{
int count=1;
if(b==0)
{
count=0;
}
else
{
*c=a/b;
}
return count;
}
指针常见的错误 定义了指针变量,还没有指向任何变量的地址,就开始使用指针
9.1-4指针与数组
传入函数的数组组成了什么?
- 函数参数表中的数组实际上是指针
- sizeof(a) == sizeof(int*)
- 但是可以用数组的运算符[]进行运算 数组参数
- 以下四种函数原型是等价的:
- int sum(int *ar,int n);
- int sum(int *,int);
- int sum(int ar[].int n);
- int sum(int [],int); 数组变量是特殊的指针
- 数组本身表达地址,所以
- int a[10];int*p=a;//无需用&取地址
- 但是数组的单元表达式是变量,需要用&取地址
- a == &a[0]
- []运算符可以对指针做,也可以对数组做;
- p[0]<==>a[0]
- *运算符可以对指针做,也可以对数组做
- *a = 25;
- 数组变量是coust的指针,所以不能被赋值
- int a[]<==>int *count a=....
9.1-5指针与const:指针本身和所指的变量都可能const
指针与counst 指针 -- 可以是const 值 -- 可以是const 指针是counst
- 表示一旦得到了某一个变量的地址,不能再指向其他变量
- int * const q =&i;//q是 const
- *q =26;//OK
- q++;//ERROR 所指是counst
- 表示不能通过这个指针去修改那个变量(并不能使得那个变量成为 const)
- const int *p =&i;
- *p =26;//ERROR!(*p)是const
- i = 26//OK
- p = &j;//OK 这些是啥意思? int i; const int *p1 = &i; int const *p2 = &i; int *const p3 = &i; 判断哪个被const了的标志是const在 *的前面还是后面 转换
- 总是可以把一个非const的值转换成const的 void f(const int* x);//这个函数的意思是保证在函数f中不会修改x所指的地址 int a = 15; f(&a);//ok const int b = a;
f(&b);//ok b = a+1;//Error! 当要传递的参数的类型比地址大的时候,这是常用的手段:既能用比较少的字节数传递给参数,又能避免函数对外面的变量的修改 const数组
- const int a[] = {1,2,3,4,5,6};
- 数组变量已经是const的指针了,这里的const表明数组的每个单元都是const int
- 所以必须通过初始化进行赋值 ** 保护数组值**
- 因为把数组传入函数时传递的是地址,所以那个函数内部可以修改数组的值
- 为了保护数组不被函数破坏,可以设置参数为const
- int sum(const int a[],int length);
9.2 指针运算
9.2-1指针运算
1=1=2?
- 给一个指针加1表示要让指针指向下一个变量 int a[10] int *p = a; *(p+1)==>a[1]
- 如果指针不是指向一片连续分配的空间,如果数组(是连续的空间),则这种运算没有意义 ** 指针运算**
- 这些算数运算可以对指针做
- 给指针加,减一个整数(+,+=,-,-=)
- 递增递减(++/--)
- 两个指针相减 ** p++*
- 取出p所指的那个数据来,完事之后顺便把p移到下一个位置去
- *的优先级虽然高,但是没有++高
- 常用于数组类的连续空间操作
- 在某些cpu上,这可以直接被翻译成一条汇编指令 指针比较
- <,<=,==,>,>=,!=都可以对指针做
- 比较它们在内存中的地址
- 数组中的单元的地址肯定是线性递增的 0地址
- 当然你的内存中有0地址,但是0地址通常是个不能随便碰的地址
- 所以你的指针不应该具有0值
- 因此可以用0地址来表示特殊的事情;
- 返回的指针是无效的
- 指针没有被真正初始化(先初始化为0)
- NULL是一个预定定义的符号,表示0地址
- 有点编译器不愿意你用0来表示0地址 指针的类型
- 无论指向什么类型,所有的指针的大小都是一样的,因为都是地址
- 但是指向不同类型的指针是不能直接互相赋值的
- 这是为了避免用错指针 指针的类型转换
- void* 表示不知道指向什么东西的指针
- 计算时与char*相同(但不想通)
- 指针也可以是转换类型
- int p = &i;voidq = (void*)p;
- 这并没有改变p所指的变量的类型,而是让后人用不同的眼光通过p看它所指的变量
- 我不再当你是int啦,我认为你就是个void! 用指针来做什么
- 需要传入较大的数据时用作参数
- 传入数组后对数组做操作
- 函数返回不止一个结果
- 需要用函数来修改不止一个变量
- 动态申请的内存......
9.2-2动态内存分配
输入数据
- 如果输入数据时,先告诉你个数,然后再输入,要记住录入每个数据
- C99可以用变量做数组定义的大小,C99之前呢?
- int * a = (int*)malloc(nsizeof(int)) malloc #include<stdlib.h> void malloc(size_t size);
- 向malloc申请的空间的大小是以自己为单位的
- 返回的结果是viod*,需要类型转换为自己需要的类型
- (int*)malloc(n*sizeof(int))
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *a;
int n;
scanf("%d",&n);
a=(int *)malloc(n*(sizeof(int)));
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
for(int i=n-1;i>=0;i--)
{
printf("%d",a[i]);
}
free(a);
}
没空间了?
- 如果申请失败则返回0,或者叫做NULL
- 你的系统能给你多大的空间
#include <stdio.h>
#include <stdlib.h>
int main()
{
void *p;
int a=0;
while((p=malloc(100*1024*1024)))
{
a++;
}
printf("分配了%d00MB内存",a);
return 0;
}
这个代码可以告诉你,电脑分配了多少MB内存 其中10010241024表示100MB。 free()
- 把申请得来的空间还给“系统”
- 申请过的空间,最终都应该要还
- 混出来的,迟早要还
- 只能还申请来的空间的首地址 常见问题
- 申请了内free-->长时间运行内存逐渐下降
- 新手:忘了
- 老手:找不到合适的free的时机
- free过了再free
- 地址变过了,直接去free 例如:
#include <stdio.h>
#include <stdlib.h>
void main()
{
int *p;
p=(int *)malloc(100*1024*1024);
p++;
free(p);
}
这里的p的地址变了所以无法直接free