指针的概念
要点归纳
1.指针变量
在C语言中提供两种指针运算符:
* :取指针目标运算符
例如,在定义int x,*px之后,以下赋值语句均成立:
提示:
2. 指针的定义和初始
由于指针是一个变量,所以具有和普通变量一样的属性,在使用之前也需要定义,在指针定义的同时也可以进行初始化。
指针定义的一般形式如下:
数据类型 *指针名
指针初始化的一般形式如下:
double x;
double *px = &x;
3. 指针运算
p+n(或p-n)*sizeof(type)
指针和数组
要点归纳
1. 指针和一维数组
int a[10] ,*p;
则
p = &a[0];
p = a;
数组名[下标]
进一步得到访问数据运算的一般形式为:
在程序中,使用指针处理内存中连续存储的数据时,可以使用以下形式:
*(指针变量+i)
归纳起来,在定义了int a[10],*p=a;的情况下:
- p+i或 a+i就是 a[i]的地址。地址值都要进行 a+i*d(d为a中元素对应的数据类型的字长)的运算。
- *(p+i)或*(a+i)就是 p+i或 a+i所指向的数组元素 a[i]。数组元素中的“[]”是变址运算符,相当于*(+),a[i]相当于*(a+i)。
- 指向数组元素的指针变量也可带下标,如p[i]与*(p+i)等价。所以,a[i]、*(a+i)、p[i]、 *(p+i)四种表示法全部等价。
- 注意p与a的差别。p是变量,a是符号常量,不能给a赋值,语句a=p;和a++;都是错误的。
注意:
例如,有一下程序:
#include <stdio.h>
int main(int argc, char const *argv[])
{
int a[] = {1, 2, 3}, *p = a;
int i;
for (i = 0; i < 3; i++)
{
printf("%d ", *(a + i)); //通过*(a+i)操作
}
putchar(10);
for (i = 0; i < 3; i++)
{
printf("%d ", a[i]); //通过a[i]操作
}
putchar(10);
for (i = 0; i < 3; i++)
{
printf("%d ", *(p + i)); //通过*(p+i)操作
}
putchar(10);
for (i = 0; i < 3; i++)
{
printf("%d ", p[i]); //通过p[i]操作
}
putchar(10);
return 0;
}
上述程序每次for循环输出的结果都是123,说明*(a+i)、a[i]、*(p+i)和p[i]是等价的。另外,几种指针混合运算方式如下:
- *p++,由于++和*同优先级,结合方向为自右向左,而这里++为后缀++,因此它等价于*p(返回其值),p++。
- *++p而这里++为前缀++,它等价于++p,*p(返回其值)。
- (*p)++,由于括号优先,它等价于*p(返回其值),然后将*p的结果加1。
例如,以下程序的输出结果见其中的注释:
#include <stdio.h>
int main(int argc, char const *argv[])
{
int a[] = {10, 20, 30}, *p = a;
printf("%d,", *p); /*输出:10*/
printf("%d,", *p++); /*输出:10*/
printf("%d,", *p); /*输出:20*/
p = a;
printf("%d,", *p); /*输出:10*/
printf("%d,", *++p); /*输出:20*/
printf("%d,", *p); /*输出:20*/
p = a;
printf("%d,", (*p)++); /*输出:10*/
printf("%d\n", *p); /*输出:11*/
return 0;
}
2. 字符指针和字符串
字符指针指的是指向char型数据的指针。显然,字符指针也是一个指针变量。字符指针变量和字符数组有如下区别。
②)赋值方式。对字符数组只能对各个元素赋值,但不能直接给字符数组进行整体赋值(可使用strcpy()函数),例如,一下赋值是错误的:
char str[10];
str = "Hello world";
C语言编译系统提供了动态分配和释放存储单元的函数。
- malloc(size):在内存的动态存储区中分配一个长度为 size 的连续空间,此函数的返回值是一个指向分配域起始地址的指针,如果此函数未能成功地执行,则返回值为0。
- calloc(n,size):在内存的动态存储区中分配n个长度为size 的连续空间,此函数的返回值是一个指向分配域起始地址的指针,如果此函数未能成功地执行,则返回值为 0。
- free(ptr):释放由 ptr 指向的内存区,ptr 是最近一次调用 calloc或 malloc 函数时返回的值。
上面三个函数中,参数n和size 均为整型,ptr 为字符型指针。
3. 指针和二维数组
以二维数组为例,设二维数组a有3行5列,定义如下:
int a[3][5]={{1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15}};
其中,a是数组名,它的各元素是按行顺序存储的。a数组有3行,将它们看成3个一维数组元素,即a={a[0],a[1],a[2]},每个一维数组元素又含5个元素。这种降维的思路可以扩展到三维或三维以上的数组。
提示:
数组元素中的“[ ]”是变址运算符,相当于*(+),对于一维数组b,b[i]相当于*(b+j)。对二维数组元素a[i][j],将分数组名a[i]当作b代入*(b+j)得到*(a[i]+j),再将其中的a[i]换成*(a+i)又得到*(*(a+i)+j)。a[i][j]、*(a[i]+j)、*(*(a+i)+j)三者相同,都表示第i行j列元素。根据以上分析,对于图5.2所示的二维数组,可得到表5.1。
4. 数组指针
因为数组名是常量,不能像变量那样操作,为此可以设计指向数组的指针变量,以便于数组的操作。
一维数组的内存空间是连续的且大小在定义时已指定,所以可以定义一个同类型的指针对其元素进行操作。
例如,以下程序定义一个数组a和指针pa,通过数组名a和指针pa操作输出所有元素:
#include <stdio.h>
int main(int argc, char const *argv[])
{
int i;
// pa作为一维数组a的指针
int a[5] = {1, 2, 3, 4, 5}, *pa = a;
/*①:通过a[i]方式输出所有元素------*/
for (i = 0; i < 5; i++)
printf("%d ", a[i]);
printf("\n"); // 输出结果:1 2 3 4 5
/*②:通过*(a+i)方式输出所有元素----*/
for (i = 0; i < 5; i++)
printf("%d ", *(a + i));
printf("\n"); // 输出结果:1 2 3 4 5
/*③:通过*(pa+i)方式输出所有元素---*/
for (i = 0; i < 5; i++)
printf("%d ", *(pa + i));
printf("\n"); // 输出结果:1 2 3 4 5
/*4:通过*pa方式输出所有元素------*/
for (i = 0; i < 5; i++)
printf("%d ", *pa++);
printf("\n"); // 输出结果:1 2 3 4 5
return 0;
}
上述程序中的4种输出方式的结果相同,只是第④种方式中,当输出完毕后pa指向a[4]元素的后一个位置。
二维数组可以看成是一维数组作为元素的一维数组,对每个一维数组元素可以采用前面介绍的一维数组指针的方式进行操作。
例如,以下程序定义了一个二维数组a,a[1]是它的1号元素,也是一个一维数组,通过指针p输出a[1]的所有元素:
#include <stdio.h>
int main(int argc, char const *argv[])
{
int i;
int a[3][2] = {{0, 1}, {2, 3}, {4, 5}};
int *p = a[1];
for (i = 0; i < 2; i++)
printf("%d ", *p++);
printf("\n"); // 输出2 3
return 0;
}
上述程序的思路是对a的每个一维数组元素分别操作,其中p指针是一维数组指针,C语言提供了二维数组指针的概念。二维数组指针变量的一般的定义格式如下:
基类型 (*指针变量) [整型表达式]
其中,“整型表达式”指出二维数组中的列大小,即对应数组定义中的“下标表达式2,例如,有如下定义:
int a[2][3], (*pa)[3] = a;
在(*pa)[3]中,由于存在一对圆括号,所以“*”首先与pa结合,表示pa是一个指针变量,然后再与“[]”结合,表示指针变量pa的基类型是一个包含有3个int元素的数组,也就是说,pa为一个二维数组的指针变量,该数组中每列有3个元素。
一旦定义了二维数组的指针变量,该数组指针变量可以像数组名一样使用,且可以在数组元素中移动。在前面定义二维数组指针变量pa并初始化后,有:
- pa[i]:引用 a[i]元素。
- pa++:让pa指向数组a的后一个一维数组元素。
- pa--:让 pa指向数组a的前一个一维数组元素。
- pa+1 等价于 a+1。
当pa指向a数组的开头时,可以通过以下形式来引用 a[i][j]:
- *(pa[i]+j)对应于*(a[i]+j)
- *(*(pa+i)+j)对应于*(*(a+i)+j)
- (*(pa+i))[j]对应于(*(a+i))[j]
- pa[i][j]对应于 a[i][j]
提示:
数组指针pa与对应的二维数组a的差别是:二维数组a是一个常量,而数组指针pa是一个变量。
例如,以下程序定义一个二维数组a和该数组的指针pa,通过数组名a和指针pa操作输出所有元素:
#include <stdio.h>
int main(int argc, char const *argv[])
{
int i, j;
// pa作为一维数组a的指针
int a[2][3] = {{1, 2, 3}, {4, 5, 6}}, (*pa)[3] = a;
/*①:通过a[i][j]方式输出所有元素------*/
for (i = 0; i < 2; i++)
for (j = 0; j < 3; j++)
printf("%d ", a[i][j]);
printf("\n"); // 输出结果:1 2 3 4 5 6
/*②:通过*(*(a+i)+j)方式输出所有元素----*/
for (i = 0; i < 2; i++)
for (j = 0; j < 3; j++)
printf("%d ", *(*(a + i) + j));
printf("\n"); // 输出结果:1 2 3 4 5 6
/*③:通过*pa[i]+j方式输出所有元素---*/
for (i = 0; i < 2; i++)
for (j = 0; j < 3; j++)
printf("%d ", *(pa[i] + j));
printf("\n"); // 输出结果:1 2 3 4 5 6
/*4:通过*(*(pa+i)+j)方式输出所有元素------*/
for (i = 0; i < 2; i++)
for (j = 0; j < 3; j++)
printf("%d ", *(*(pa + i) + j));
printf("\n"); // 输出结果:1 2 3 4 5 6
return 0;
}
三维数组指针变量的一般的定义格式如下:
基类型 ((*指针变量)[整型表达式1])[整型表达式2]
其中,“整型表达式1”对应数组定义中的“下标表达式2’“整型表达式2”对应数组定义中的“下标表达式3”。例如,有如下定义:
int a[2][3][2]={1,2,3,4,5,6,7,8,9,10,11,12},((*pa)[3])[2]=a;
pa即为三维数组a的指针变量。其定义是分两步考虑的,先定义二维数组a[2][3]的指针变量,为(*pa)[3],再定义三维数组a的指针变量为((*pa)[3])[2]。
三位数组指针的使用与二维数组指针类似。
5. 指针和数组的对比
指针和数组的对比如表 5.2 所示。
指针数组和多级指针
要点归纳
1. 指针数组
当一系列有次序的指针变量集合成数组时,就形成了指针数组。指针数组是指针的集合,它的每个元素都是一个指针变量,并且指向相同的数据类型。指针数组的定义形式如下:
数据类型 *指针数组名[元素个数];
int *p[3];
由于“[]”比“*”优先级高,因此p先与[3]结合,形成p[3]的数组形式,它有3个元素。然后再与p前面的“*”结合,表示是指针类型的数组,该数组的每个元素都是整数的指针,所以每个元素都具有指针的特性。
2. 多级指针
在C语言中,除了允许指针指向普通数据之外,还允许指针指向另外的指针,这种指向指针的指针称为多级指针。其定义形式如下:
int *p,**pp,***ppp;
其中,p为一级指针,pp为二级指针,ppp为三级指针。
一般地,p用于指向普通的整数,或整型数组的元素;当指向整型数组的元素时,p++表示指向该数组的下一个元素;
pp用于指向一个指针,p为整数的指针。大多数情况下,pp作为一个指针数组的指针,这时,pp++表示指向该指针数组的下一个元素;
ppp用于指向一个二级指针的指针。大多数情况下,ppp作为一个二维数组指针的指针。
温馨提示:
对于指针有大量练习题,如有需要,请留言……
希望能对您的学习有所帮助!