C语言学习-指针基础
C语言指针基础
1.指针的重要性
指针是C语言的重要概念,也是C语言及其扩展语言(C++等)的重要特色。指针可以使程序变得简洁、紧凑、高效。每一个学习C语言的人都应当深入学习和掌握指针。可以说,没有掌握指针,就是没有学会C语言。
指针的概念复杂也比较灵活,因此初学者经常会犯错。请同学们务必多思考、多练习、谨慎使用。
2.基本概念
地址:在计算机内部存储器(简称内存)中,每一个字节单元(8位),都有一个编号,称为地址。(在32位计算机中,地址是由32位(bit)构成的2进制数,占4字节(byte),通常由一个8位的16进制数来表示。)
指针:在C语言中,专门用来保存地址的变量,称为“指针变量”,简称“指针”。在不影响理解的前提下,“指针”、“指针变量”和“地址”不做区分,统称“指针”。
指针的目标:指针的目标变量简称为指针的目标。指针变量用于存放该对象(也可以理解为就是一块内存区域)的首地址。
3.定义指针变量
用法:
<数据类型名> *<指针变量名>
说明:
- <数据类型名>:指针指向的数据类型,是指针的目标的数据类型,而非指针本身的数据类型
- *<指针变量名>:指针变量的变量名。前面加*代表这是一个指针变量
示例:
char *s; //定义一个指向char类型数据的指针变量s
int *p; //定义一个指向int类型数据的指针变量p
float *f; //定义一个指向float类型数据的指针变量f
注意:在C语言的官方文档内,推荐定义指针时指针运算符" * "要紧贴指针变量名,而不要挨着数据类型名。
4.指针变量的引用
4.1 运算符
在这里需要熟练掌握两个与指针有关的运算符:&和*
&:取地址运算符
*:定义指针变量/取指针所指向空间的内容(或称“间接访问运算符”)
两个运算符互为逆运算。例如”&a”就是取变量a的地址,而”*b”就是取指针变量b所指向的内存单元里的内容。通过一个指针访问它所指向的对象的值称为变量的简介访问。
4.2 引用
1)给指针变量赋值
如果我们已经定义了一个int型变量a,和一个int型指针p; 则我们可以使用&给指针变量赋值,即让指针变量p指向变量a
int a;
int *p;
p = &a; //将a的地址取出赋值给指针变量p
2)指针变量的初始化
我们可以在定义指针变量的时候对指针变量进行初始化。
如:
int *p = &a; //初始化指针变量p指向变量a
3)引用指针变量所指向的内容
如果已执行p = &a,表示指针p已指向变量a,则我们可以使用指针p来引用变量a。
如:
printf("%d\n", *p); //*p表示将指针变量p的内容取出
或者可以通过指针对指向的变量进行操作。如:
*p = 1;//相当于a = 1
4)引用变量本身的内容
指针变量也是可以输出的,用格式控制%p
printf("%p",p); //打印指针p所存放的地址
总结:
- p 指针变量,内部存放的是目标的地址;
- *p 取目标的值,目标内存数据;
- &a 取变量的内存地址;
5)指针变量占内存空间大小
指针变量存储的是一个32位2进制数(8位16进制数),因此占内存4字节。
注意:
指针变量存储的是一个地址值,而地址是4字节,因此指针大小就是4字节。指针占内存空间大小与指针类型无关。
5.指针运算
5.1算术运算
注意:
不同数据类型的两个指针实行加减整数运算是无意义的。
px + n 代表指针向地址大的方向移动 n 个 数据。
移动后的地址量是: (px) + sizeof(px的类型) * n
px - n 代表指针向地址小的方向移动 n 个 数据。
移动后的地址量是: (px) - sizeof(px的类型) * n
px++ 指针变量向地址大的方向移动一个数据。
px-- 指针变量向地址小的方向移动一个数据。
px - py 表示两个相同类型指针间相差数据的个数,而不是一个地址量。
px - py的结果是 (px - py) /sizeof(数据类型)
5.2关系运算
指针关系表示两个指针在内存位置的高低关系。
不同数据区域间的指针,关系运算没有意义。(不同数据类型的指针之间关系运算没有意义)
指针和除0外的整数比较没有意义,和0比较可以判定指针是否为空。(标准写法为if (NULL == p) 标识指针不指向任何地方).
5.3赋值运算
赋值运算指的是通过赋值运算符向指针变量传递一个地址值。这个值是地址常量或指针变量(同类型),不能是普通整数(0可以表示空值)。
指针赋值运算常见的有以下几种形式:
1)把一个普通变量的地址赋给一个具有相同数据类型的指针
例如:
double x=15, *px ;
px=&x;
2)把一个已有值的指针变量赋给具有相同数据类型的另一个指针变量,例如,
float a , *px, *py ;
px = &a ;
py = px ;
3)把一个数组的地址赋给具有相同数据类型的指针。例如,
int a[20], *pa;
pa = a; //等价 pa = &a[0]
4)把零赋给一个指针。例如,
int *pa;
pa = NULL; //表示指针不指向任何对象
5)把表达式的值赋给一个具有相同数据类型的指针变量:
int n = 2;
double a[20], *px, *py ;
px = a ;
py = px + n ;
6.const指针
const 表示的使变量常量化,即不可修改。
int const a = 9;
a = 10; //报错,a为const修饰不可改变。
const int a 与 int const a, const可以在int的左右位置。
const 在遇到指针时会发生一些变化
const常用来修饰指针变量,有以下三种用法:
1)常量化的指针的对象
const <数据类型> *<指针变量名称>;
说明:常量化指针目标是限制通过指针改变其对象的值。
2)常量化指针变量
<数据类型> *const <指针变量名称>= <指针运算表达式>;
说明:常量化指针变量,使得<指针变量>的值不能修改(即只能指向同一个对象)。但可以通过 * <指针变量名称>
修改指针所指向的对象的值。
3)常量化指针变量及其对象
const <数据类型> * const <指针变量名> = <指针运算表达式> ;
说明:常量化指针变量及其对象,使得既不可以修改<指针变量>的值,也不可以通过*<指针变量名称>修改指针所指向对象的值。
int a = 9; int b = 12; const int *p = &a; // const 修饰的是*p , p 指向变量a, int const *p = &a; //和上面效果相同, 都表示指针变量p指向a,且*pa不可变 *p = 10 ; // 通过p改变a的值,但*p是const类型,不可改变。 p = &b; //可以改变p的值,(即指向)。 *p = 11;// 同样不可以 int *const q = &a; //const 修饰的是 q, 所以q是不能改变的,即不能改变q的指向 *q = 111; q = &b; // 将q指向b,报错。 |
7.void指针
一般情况下,指针的数据类型取决于该指针指向的对象的数据类型,例如int *。但是有些情况下,我们暂时无法确定指针所指向数据的类型,此时我们可以使用空类型指针void *来暂时表示指针的类型。
指针变量指向不确定数据类型的变量的时候,可以定义为void型指针,因为void类型指针可以赋值给其他任意类型的指针,而其他类型不能相互赋值。
一般形式为:
void *<指针变量名> ;
例如:malloc函数
void * malloc(size_t size);
malloc 函数因为不知道分配空间的具体用途,所以返回void型地址。(后面再详细学习)
8.小结:指针自增与自减
- p++(或 p+=1): 使p指向下一个元素
- *p++:++与* 具有相同优先级且结合方向自右向左, 等价于*(p++), 先取*p的值,然后p再自加,指向下一个元素。
- *(p++) 与 *(++p) 作用不同。 前者是先取*p的值,再使p自加。后者先使p自加,再取自加后指向的内容。
- ++(*p): 表示将p指向的元素的值加1.