C语言基础-指针
基础概念
1.直接访问与间接访问
通过变量名访问该内存单元值称为对变量的“直接访问”
通过指针变量访问某内存单元值称为对变量的“间接访问“
2.类型标识符(如int)与变量标识符(*)
Elemtype * 指针变量名
(1)类型标识符Elemtype
也叫指针类型
表示指针变量 所指向的变量/所指向的内存空间存放的变量 的数据类型,称为指针变量的基类型
(2)变量标识符 *
* 表示这是定义一个指针变量
(3)变量名即为定义的指针变量名
例如: int * p;
表示p是一个指向整型变量的指针变量,
通过p可以存取一个整型变量。
像使用其他变量一样,可以对p赋值,只是对p所赋予的值只能是地址(用地址变量赋值)。
3.p(地址/指针变量)与*p(基类型值)
(1)p是指针/地址 变量(是个存着地址的变量),其值是(p中存放的)一个变量的地址。
p收到了(放入了)哪个变量的地址,就指向哪个变量。该变量成为p的对象。
(2)*p 是p所指向的对象的值。
类型由定义变量时的类型标识符确定(如int)
→ *p是一个值,为指向变量的值
(3)* 后面只能跟地址(变量)。 *p 表示参数的值
&后面只能跟变量。 &a 表示参数a的地址
(4) ’=‘ 是赋值,默认只允许同类型的参数进行赋值(若不同类也会赋值,但是执行结果会出错)
→只允许,(指针)地址 赋值 给(指针)地址
(变量)值 赋值 给(变量)值
定义指针变量
不赋值
定义基类型指针 Elemtype *elem;
(Elemtype 是指针所指向单元内容的类型)
定义不赋值与定义空指针的区别
int *p; p随机指向一个地址数字
int *p 初始化,是一个野指针。p保存的是一个随机值(有相应的内存空间)
C、C++中NULL默认宏替换为0。用NULL来初始化指针,避免野指针。
int *p=NULL 就是指向一个空指针。值为0,有相应的地址
等同于 int *p=0 ⇒ int *p;p=NULL;
char *p=NULL 就是指向一个空指针。值为(NULL),有相应的地址
赋值
1)定义时赋值
int *p ⇔ p(读作地址变量)
int *p = &a(√)
p存放的地址初始化为&a
★★★定义即int *p时理解成基地址 需要赋值一个地址变量, 因为指针p没有指向变量,需要先赋值一个地址
相当于 int *p,p=&a;
2)定义后再赋值
int *p,p=&a;
定义以后的*p是一个值,需要赋值 一个实值
3)错误赋值
①定义时赋值
int *p = a; ×
不可将值(整形变量)赋值给地址(变量)
相当于int *p;p=k;(×)
②整形变量(相互)赋值地址变量 ×
int a=&p; ×
4)有关指针的赋值总结
&k,p 是地址(变量) *p是基类型变量(如整型变量)
int *p,*q,a=20;
①*p=a; √ 给p指针指向的(地址)单元 赋值
(都是值,实现将*p指向的变量值赋值为a)
②q=p;√ 因为p/q都是指针(变量),该语句实现将p的值赋值给q
③p=q=&a;√(p/q(值)是地址,&a也是地址)
④p=*q;× p=a; ×(p是地址,*q,a是值)
⑤a=*p; p=q; √(都是值)
取值(结构体类型指针):
若定义一个指针指向特定数据类型变量
如elm *p=stu1;
(等同于elem *p;p=&stu1;)
则stu1.name等同于(*p).name
又等同于p→name :p所指结构体变量(结点)的name成员(值)
数组指针与字符型指针
注意:C语言中没有特定的字符串类型,我们通常是将字符串放在一个字符数组中
1.数组指针(数组的指针)
数组指针(数组的指针 即数组被个指针指向,指针中存放的是数组的基地址)
当一个指针变量被初始化成数组名时,就说该指针变量指向了该数组(p中存放str的首地址)
如 char str[20], *p; p=str;
可以改变数组的字符值(可写)
2.指向字符型指针(字符型指针指向一个常量字符串)
char * str = "I love China!";
或 (*位置任意 只要在中间就行)
char * str; str = “I love China!”;
指向的一个固定常量字符串(第一个字符的地址即字符串的首地址) 不可写仅可读
对字符型指针str做输入字符串的命令,就会将常量字符串全部输出。(打印完第一个字符会自动打印完整个字符串)
3.数组指针与字符型指针声明总结
char *p(定义一个字符型指针)
(2)①p=数组名;(将数组的地址/也就是首地址 赋值 给数组指针)
p=字符串;(将字符串的地址/也就是首字符的地址 赋值 给字符型指针)
不可以把一个字符串直接赋给字符型数组的数组名
4.数组与指向字符串指针的取值
*(p+i), p[i]都是第(i+1)个元素的值
因为p是数组首地址,即p==&p[0];
⇒p是地址变量,存放数组基址
(顺序表数组指针L.elem,L.elem存放数组空间基地址)
L.elem+L.length-1 / &L.elem[L.length-1]
是最后一个(表尾)元素的地址
指针的输出(C和C++):
★★★C/C++指针的不同
输出
printf(“%s”,p); →输出字符串(p指向的字符串)
printf(“%d”,p); →输出指针p值
C++
输出:
cout *p →输出字符串(指针指向的内存空间)
cout p →输出指针中存放的地址
函数指针:
1.概念
函数有其存放的内存地方,函数名即为函数内存的首地址(地址变量)
函数指针就是指向函数的指针变量。
2.使用:
(1)函数指针的声明:
(1-1)声明:
函数返回值类型 (* 指针变量名) (函数参数列表);
Status(*p)(int)
(1-2)“函数参数列表”:该指针变量指向 具有什么参数列表的函数。
这个参数列表中只需要写函数的参数类型即可
(1-3)指针的类型:
Status(*)(int)
参数(类型)为int,返回值(类型)为Status的函数指针类型
(1-4)实例:
Status(*p)(int) 定义了一个参数(类型)为int,返回值(类型)为Status的函数指针类型 变量p
(* p)为了保证优先级
(2)赋值:
函数指针名=指向函数名;
p=add;
(函数名即为函数内存空间的首地址,看作地址变量)
也可以直接定义:
函数返回值类型 (* 指针变量名) (函数参数列表)=函数名
void (*p1)(int , int ) = add;
注意,函数void add(int a,int b)的函数名add就是函数的地址。将地址add赋值给指针p1,那么就可以通过函数指针p1直接调用函数了。
(3)调用:
(*p1)(1, 2);
p1(1, 2);
注意!出于历史原因以上2种方式都可以调用
3.原理
函数要执行的命令和普通的变量一样都是放在内存中的,既然放在内存中就会有地址
函数开始的第一条命令也不例外,而它的地址就叫函数的起始地址
现在用一个指针指向这个地址,当间接访问这个指针指向的地址所存放的命令时,这个函数就被启动了
而这个指针就叫函数指针,“函数指针”其实就是“函数的指针”
“指针函数”就是一类函数。什么类呢?是返回值是指针的函数
数组定义
char ss[]=“hello”;正确
在声明数组变量的时候为其开辟空间,并为其初始化(首地址)
char s[20];s=“hello”;错误
s的首地址在声明时已经确定,无法更改为字符串的首地址
不可以把一个字符串直接赋给字符型数组的数组名
int *p="hello";正确
把字符串的首地址赋值给地址变量p
上述语句实际等同于
int * p;p="hello";