我们先看一段代码
#include <stdio.h>
int main(void)
{
char ac[] = {0,1,2,3,4,5,6,7,8,9,};
char *p = ac;
printf("p =%p\n", p);
printf("p+1=%p\n", p+1);
int ai[] = {0,1,2,3,4,5,6,7,8,9,};
int *q = ai;
printf("q =%p\n", q);
printf("q+1=%p\n", q+1);
return 0;
}
输出结果
p =0062FE8E
p+1=0062FE8F
q =0062FE64
q+1=0062FE68
这里可以看出:
char 类型下,p和p+1只差1个字节
int 类型下,q和q+1却差了4个字节
为什么char类型指针是1个字节,而int类型指针却是4个字节
因为上一节讲到 sizeof(char)占用一个字节,而size(int)占用4个字节
从这里可以看出当我们给一个指针地址+1时,其实是给指针地址+sizeof()指针所指的类型
我们再看另外一段代码,如果我们有一个数组
*p -> ac[0]
*(p+1) -> ac[1] // *是单目运算符运算优先级高,所以(p+1)要加上括号
#include <stdio.h>
int main(void)
{
char ac[] = {0,1,2,3,4,5,6,7,8,9,};
char *p = ac;
printf("p =%p\n", p);
printf("p+1=%p\n", p+1);
printf("*(p+1)=%p\n", *(p+1));
int ai[] = {0,1,2,3,4,5,6,7,8,9,};
int *q = ai;
printf("q =%p\n", q);
printf("q+1=%p\n", q+1);
printf("*(q+1)=%p\n", *(q+1));
return 0;
}
运行输出结果:
p =0062FE8E
p+1=0062FE8F
*(p+1)=00000001
q =0062FE64
q+1=0062FE68
*(q+1)=00000001
这里*(p+1)和*(q+1)都=1,没有任何意义,所以指针+1需要加上sizeof()
**· 给一个指针+1表示要让指针指向下一个变量
int a[10];
int *p = a;
*(p+1) --> a[1]
· 如果指针不是指向一篇连续分配的空间,如数组,则这种运算没有意义
**
*(p+n) <–> ac[n]
在代码里,我们让p(p+n)和ac[n]是一回事
指针计算
· 这些算术运算可以对指针做:
· 加、减一个整数(+,+=,-,-=)
· 递增递减(++/–)
· 两个指针可以相减
int main(void)
{
char ac[] = {0,1,2,3,4,5,6,7,8,9,};
char *p = ac;
char *p1 = &ac[5];
printf("p1-p=%d\n",p1-p);
int ai[] = {0,1,2,3,4,5,6,7,8,9,};
int *q = ai;
int *q1 = &ai[6];
printf("q1-q=%d\n",q1-q); // 指针和指针相减
printf("q =%d\nq1=%d\n",q,q1);
return 0;
}
运行输出结果
p1-p=5
q1-q=6
q =6487644
q1=6487668
从这里可以看出
· p1-p=ac[5]-ac[0]=5-0=5
· q1-q=ai[6]-ai[0]=6-0=6
· q1-q=24
指针和指针相减时,是sizeof()-sizeof()
** p++的含义
· 取出p所指的那个位置的数据,然后把p移到下一个位置
· 的优先级高,但是没有++高
· 常用于数组类的连续空间操作
· 在某些CPU上,剋有直接被翻译成一条汇编指令
遍历数组例子
int main(void)
{
char ac[] = {0,1,2,3,4,5,6,7,8,9,-1};
char *p = ac;
int i;
/*遍历方法1*/
for(i=0;i<sizeof(ac)/sizeof(ac[0]);i++){
printf("%d\n",ac[i]);
}
/*遍历方法1*/
while(*p != -1){
printf("%d\n",*p++);
}
int ai[] = {0,1,2,3,4,5,6,7,8,9,};
int *q = ai;
return 0;
}
执行输出结果
0
1
2
3
4
5
6
7
8
9
-1
0
1
2
3
4
5
6
7
8
9
指针比较
· <,<=,==,>,>=,!=都可以对指针直接操作
· 比较他们内存中的地址
· 数组中的单元的地址肯定是线性递增的
0地址
· 内存中有0地址,但是0地址通常不能随便用的 地址,读0地址也不可以
· 指针不应该具有0值,可以初始化为0
· 可以用0地址表示特殊的事情:
· 返回的指针是无效的
· 指针没有不真正初始化(先初始 化为0)
· NULL是一个预定义的符号,表示0地址
· 有的编译器不愿意用0来表示0地址
指针的类型
· 无论指向什么类型,所有的指针的大小都是一样的,因为指针都是地址
· 但是指针指向不同类型的指针是不饿能直接互相赋值的
· 这是为了避免用错指针
指针的类型转换
· void 可以表示不知道指针什么东西的指针
· 计算时与char相同(但不相通)
· 指针也可以转换类型
· int p = &i; void q = (void)p;
· 这并没有改变p所指的变量类型,而是让后人用不同的眼光通过p看它所指的变量
· 它不再是int了,而是一个void
指针用来做什么
· 需要传入比较大的数据时,用作参数,比如传入数组
· 传入数组后可以用指针对数组可以进行操作
· 函数返回不止一个结果时,可以用指针作参数,让指针带出结果
· 需要用函数修改不止一个变量时,可以用传指针进去,比如swap,让指针进去帮我们修改这个值
· 动态申请的内存的时候