取地址运算——&
printf("%p",&i);
变量取地址
&后面必须有明确的变量,&(i+k) 和&(i++) 都是不行的
相邻的变量取地址
int i;
int p;
printf("%p",&i);
printf("%p",&p);
就可以看到i的地址是6c,p的地址是68,中间差了4,也就是说他们是挨着存放的 进一步的,其实本地变量在内存中的存储是一个堆栈 
数组取地址
 这里会发现,输出a的地址和以地址格式输出a 和a[0]的地址其实是一回事,而且数组的每个元素也是挨着排列的
数组单元的地址
相邻数组的地址
指针——*
指针变量就是保存地址的变量
指针 *是一个单目运算符,用来访问指针的值所表示的那个地址的变量
int i=6;
int *p=&i;
看如上代码,表示
- 有一个变量,变量名叫 i,这个变量的值为6,这个变量的地址是0x20
- 另外有一个变量,变量名叫p,这个变量的值为0x20,这个变量的地址是0x36(假设)
是非常好理解的,如下图 
int *p;
int* p;
看如上代码,两行表达的意思是一样的,都表示p是一个指针变量,指向的是一个int变量
- *p是一个int
- 而非p是int *类型的变量,不存在int *类型
注意:这里可能会混淆的地方是指针到底是个变量还是个运算,其实这只是看待同一个问题的两个角度,下面分别来阐述
- 指针是一个变量:即指针也是一个普通的变量,只不过放的不是我们平常的值,而是放的地址,即指针变量就是放着地址的变量,侧重的是p
- 指针是一种运算:是p是一个变量,里面放的是一个地址,对这个变量进行指针运算,即执行操作——找到这个变量处放的值,侧重的是*
注:作为参数的指针
- 在函数里面可以通过指针访问外面的变量,拥有了能够访问外面变量的能力
- 仍以上面的例子来说明:此时p=0x20,而*p=6
之前问题的解释——scanf("%d",&i);
重新来审视这样的一句
scanf("%d",&i);
scanf("%d",i);
同样输入6,但是第二句是错的 解释:
- 第一句是告诉scanf,把6给i,即把6放在0x20的位置上,所以6把原先i 中的0x55给覆盖掉了
- 第二句出现了错误是因为,变成了告诉scanf,把6放在0x55的地方上,而如果0x55那个地方恰好不能写,那就崩溃了,如果巧了恰好能写,运行的结果也肯定不对,因为此时把6放在了0x55上,而0x20的位置还是0x55
指针的使用场合
- 函数要返回多个值,某些值就只能通过指针返回
- 传入的参数实际上是需要保存带回的结果的变量
毕竟函数的返回值只能返回一个值,但是python可以返回多个值,所以才没有指针的吗?
例子:交换两个变量的值
以前不用指针的话,是无法用函数来实现交换变量的
void swap(int *a,int*b)
{
int t=*a;
*a=*b;
*b=t;
}
例子:得到最小值最大值
这个例子比较好的体现了参数的作用是为了带出来结果
例子:两个数做除法的函数
函数返回运算的状态,而结果通过指针返回 (其实还是需要返回多个结果)
在这里插入代码片
因此这种指针的应用场景是 程序可能会出错,在C只能这样做,但在后续的语言中采取了异常机制来解决这个问题
指针和数组
函数、指针、数组
如果函数的参数是普通变量,传入的是值 如果函数的参数是指针变量,传入的是也是值,只是这个值是所指向变量的地址 如果函数的参数是数组呢?传入的是什么?
函数参数表里的数组,实际上就是指针,传入的就是数组的首地址
void minmax(int a[],int len,int *min,int *max)
所以就解释的通了
- 这个参数表里为什么只需要写个
a[] - 为什么方括号里写数字没用
- 在函数中用sizeof也不能得到元素个数结果
一切的原因都是因为,传入的是数组的首地址,他是个指针 那你要这么说的话,直接写成指针可以吗?
void minmax(int *a,int len,int *min,int *max)
这样子写当然可以,函数中下面运算a[1] 这种东西也是没问题的
四种函数原型 是等价的
则下面 四种函数原型 是等价的便很好理解的
int sum(int *ar,int n) int sum(int *,int ) int sum(int ar[],int n) int sum(int [],int )
数组是特殊的指针
注意1
- 数据变量本身表达地址
- 但是数组的单元表达的是变量,需要用&取地址
int a[10];
int *p=a
即写一个数组表达的就是这个地址 且a==a[0]
注意2
[] 可以对数组做,但其实也可以对指针做
int *p=a;
比如p[0] 其实也是对的,毕竟p也是指向的那个地址,数组也是指向了一个地址,那p[0] 其实就是a[0] ,本质上来讲还是一回事
- 如果p指向是是个普通的变量,也是可以用
[] 的,要充分的理解
int *p=&i;
这里也是可以用p[0] 的,并且表示的就是i,但是p[1] 就不行了,毕竟你一个变量,从地址的角度来看,也能看成只有一个元素的数组,即只有p[0]
注意3
- *运算符可以对指针做,也可以对数组做
*a=1000;
毕竟表示的都是那个地址,所以也非常好理解
注意4
- 实际上,数组是const的指针
所以之前才说不可以做如下的代码来进行数组的赋值,而是应该利用for循环来对每个单元进行遍历
int b[];
b=a;
int *q=a;
为什么第三句可以,第二句不行呢? 因为数组是const的指针(再次强调)
int b[] 的意思是int * const b ,const在这里来说,这个数组被创建出来了,是这个数组,就不能被修改成别的数组了,const的特性 即b[]是一个常量指针
指针和const
指针是const
和上面的数组是const的指针联系起来了
表示一旦得到了某个变量的地址,不能再指向其他变量
int *const q=&i;
*q=26;
q++;
所指是const
表示不能通过这个指针去修改那个变量(并不能使那个变量变成const) 只是不能通过这个指针来修改那个变量了
const int *p=&i;
*p=26;
i=26;
p=&j;
这些是啥意思??
const int *p1=&i;
int const *p2=&i;
int * const p1=&i;
判断的标准是const在*的前面还是后面
- 如果const在*的前面,则是所指不能被修改
- 如果const在*的后面,则是指针不能被修改
因此前两句是一个意思,是所指不能被修改 第三句是指针不能被修改
const数组来保护数组值
const数组
刚刚已经介绍过,数组变量已经是const的指针了,这里的const表明数组的每个单元都是const,所以必须通过初始化进行赋值
保护数组值
前面的介绍已经发现,由于数组传入函数传的是地址,因此在函数内部可以修改这个数组中的值,但如果我们不能修改呢 可以设置参数为const 即
int sum (const int a[],int length)
const的总结
注意,这一章一共介绍了四种const,分别是
- 指针是const
- 所指是const
- 数组是const的指针
- const数组
其中3是1的其中一种
这一章的本质是在函数 数组 指针的综合
指针运算
注:之前已经说到,把数组赋给指针以后,可以拿数组像指针一样操作,也可以拿指针像数组一样操作
char ac[]={0,1,2,3,4,5,6,7,8,9};
int *p=ac;
int *p1=&ac[5];
*p->ac[0]
*(p+1)->ac[1]
*(p+n)->ac[n]
- 对指针加1的时候,不是在地址上加1,而是在地址上加一个sizeof那个指针所指的类型,即移到下一个单元上
- 但如果指针不是指向一片连续分配的空间,如数组,则这种运算没有意义(好理解)
int *p=ac; 和int *p1=&ac[5]; , int *p=ac; 可以不写取地址符,和int *p=&ac[0]; 是一回事,但是int *p1=&ac[5]; 必须要写,这个要特别注意的*(p+1) 和*p+1 不一样,要理解p1-p 是什么?两个指针的相减,最后结果是5,这是两个值相减,再除以sizeof那个类型,即是说中间差了几个数组单元*p++ 是什么意思?是取出p所指的那个数据来,完事以后顺便把p移到下一个单元,注意,*的优先级虽然高,但是没有++的优先级高,这常用于数组类的连续空间操作 以遍历数组为例
#include<stdio.h>
int main()
{
char ac[]={0,1,2,3,4,5,6,7,8,9};
int i;
for(i=0;i<sizeof(ac)/sizeof(ac[0]);i++)
{
printf("%d\n",ac[i]);
}
char ac1[]={0,1,2,3,4,5,6,7,8,9,-1};
char *p=ac1;
while(*p!=-1)
{
printf("%d\n",*p++);
}
return 0;
}
动态内存分配
动态内存分配在什么时候用? C99以前,确定需要多大的单元的时候
#include<stdio.h>
int main()
{
int number;
printf("输入数量:");
scanf("%d",&number);
int a[number];
return 0;
}
如上代码
但注意:malloc是必须掌握的东西
详细介绍怎么用
三部曲:
-
#include<stdlib.h> 这个头文件要加上 -
a=(int *)malloc(number*sizeof(int)); 这句话的意思是,先讲你需要多少个number,然后乘以你需要的数据类型的大小,就是 你所需要的内存的大小,malloc(你所申请的内存的大小(字节)) ,但是由于只是给你了这片内存,即这篇内存现在没有明确的类型,是void *,因此要再强制类型转换成int * 这一块因为是连续的空间,就可以当成a是数组,进行操作了  -
free(a) 释放的应该是原来的地址 
样例:
#include<stdio.h>
#include<stdlib.h>
int main()
{
int number;
int *a;
int i;
printf("输入数量:");
scanf("%d",&number);
a=(int *)malloc(number*sizeof(int));
for(i=0;i<number;i++)
{
scanf("%d",&a[i]);
}
for(i=number-1;i>=0;i--)
{
printf("%d",a[i])
}
free(a);
return 0;
}
malloc能给我们分配多大的空间
#include<stdio.h>
#include<stdlib.h>
int main()
{
void *p;
int cnt=0;
while((p=malloc(100*1024*1024)))
{
cnt++;
}
printf("分配了%d00MB的空间",cnt);
return 0;
}
这里需要注意的几点
- p为什么是
void * ,因为malloc()本身就是void * ,然后再看你的需要来强制类型转换成你需要的
|