从零开始的数据结构
由于笔者c语言没有学好,于是选择复习一遍指针,结构体,链表等,本期复习指针。
本篇文章提取改编自参考视频
参考视频 浙江大学翁恺老师c语言
指针复习
复习指针,看这一篇就够了。
一、基本概念与语法
指针就是保存地址的变量
int i;
int* p = &i;
此时我们说p指向了 i
上述代码也可以写成
int i;
int* p;
p = &i;
需要注意的是
int* p与int *p
表达含义相同
此时我们说 *p是一个 int (变量值)而 p 是一个指针
变量的值是内存的地址
普通变量的值是实际的值
指针变量的值是具有实际值的变量的地址
二、作为参数的指针
void f(int *p);
在被调用的时候得到了某个变量的地址,即要求传入一个指针
int i = 0;
f(&i);//通过&符号取得i的地址传入
在函数里面可以通过这个指针参数访问外界的 i 变量
#include<stdio.h>
void f(int *p);
int main(void)
{
int i = 6;
printf("&i=%p\n",&i);
f(&i);
g(i);
return 0;
}
void f(int *p)
{
printf("p=%p\n",p);
}
void g(int k)
{
printf("k=%d\n",k);
}
&i与p的printf结果相等,为同一地址值
k的printf为6,与i相等
即p得到的是i的地址,k得到的是i的值
三、访问地址上的变量*
*是一个单目运算符,用来访问指针的值所表示的地址上的变量
void f(int *p)
{
printf("p=%p\n",p); //p是一个指针,指向地址,输出一个地址
printf("*p=%d\n",*p); //*p整体可以看成一个整形数,输出一个整形数
}
若在函数中修改*p,会影响到外部p所指向的变量,
四、指针常见错误
定义了指针变量,还未指向任何变量,就开始使用指针
即只声明未赋值
//下述为错误示范
int *p;
*p = 3;
这种方式是错误的,指针变量此时是一个野指针,任意指定了一个地址。
五、指针与数组
函数参数表中的数组实际上是指针
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];//无需用&取地址
但是数组的单元表达的是变量,需要用&取地址
a == &a[0]
[]运算符可以对数组做,也可以对指针做
即,p[0]<==>a[0]
p[0]是把p所指的地方当作数组(当作只有一个长度的数组)
*运算符可以对指针做,也可以对数组做
*a=25
*a指向数组中的第一个元素
数组变量是const的指针,不能被赋值(指针常量)
int a[] <==> int *const a=…
六、指针与const
指针是const(指针常量)
表示一旦得到了某个变量的地址,不能再指向其他变量
int *const q = &i; //q 是const
*q = 26; //可行
q++; //不可行
数组是指针常量
所指是const(常量指针)
表示不能通过这个指针去修改那个变量(并没有使得那个变量本身成为const)
换言之,指针指向的变量的值不可通过该指针修改,但是指针指向的值可以改变。
const int *p = &i;
*p = 26; //不对!(*p)是const
i =26; //可行
p =&j; //可行
int i;
const int* p1 = &i;//O.o
int const* p2 = &i;//与O.o的形式一样
int *const p3 = &i;
判断哪个被constl的标志是const在*的前面还是在后面
const数组
const int a[]={1,2,3}
这样相当于在*前面加了一个const,表面数组的每个单元都素const int
必须通过初始化赋值,否则无法再改变了
保护数组值
因为把数组传入函数时传递的是地址,所以函数内部可以修改数组的值
为了保护数组不被函数破坏,可以设置参数为const
int sum(const int a[],int length)
七、指针运算
给一个指针+1表示要让指针指向下一个变量
int a[10];
int *p = a;
*(p+1)->a[1]//实际p+1的地址加了4(int类型)
如果指针不是指向一片连续分配的空间,则这种运算没有意义
指针值+1,实际地址+sizeof(那个基础类型)
#include<stdio.h>
int main(void)
{
char ac[] = {0,1,2,3,4};
char *p = ac;
printf("p =%p\n",p);
printf("p+1=%p\n",p+1);//输出的p+1和p的地址差1(char类型)
int ai[] = {0,1,2,3,4};
int *q = ai;
printf("q =%p\n",q);
printf("q+1=%p\n",q+1);//输出的q+1和q的地址差4(int类型)
return 0;
}
指针能做的运算
加减一个整数(+,+=,-,-=)
递增递减(++/–)
两个指针相减,返回的是两个地址相减除以sizeof的差(即两个地址中间能放几个该类型的数)
*p++
取出p所指的那个数据来,然后再把p移到下一个位置去。
*的优先级虽然高,但是没有++高
常用于数组类的连续空间操作
在某些cpu上,这可以直接被翻译成一条汇编指令(可能跑得快)
指针比较
< , <= , == , > , >= , != 都可以对指针做
比较它们在内存中的地址
数组中的单元的地址是线性递增的
0地址(了解)
0地址通常是个不能随便碰的地址,你写的指针不应该有0值
因此可以用0地址来表示特殊的事情:
返回指针无效;
指针没有被真正初始化
NULL是一个预定定义的符号,表示0地址
有的编译器不愿意用0来表示0地址
指针类型
虽然无论什么类型的指针大小都一样,都是地址
但是指向不同类型的指针是不能互相赋值的,目的是为了避免用错指针
//下列为错误示范代码!!!
char ac[] = {0,1,2,3,4};
char *p = ac;
int ai[] = {0,1,2,3,4};
int *q = ai;
q = p;//会报错,因为指针类型不同
指针的类型转换
void*表示不知道指向什么东西的指针
计算时与char*相同(但不相通)
指针也可以转化类型
int *p = &i;
void *q = (void*)p;
这并没有改变p所指的变量的类型,而是改变看法,当作是个void
八、用指针做什么
需要传入较大的数据时
传入数组后对数组做操作
函数返回不止一个结果
需要用函数来修改不止一个变量
动态申请的内存
…