指针的运算:针对这个我们直接上代码:
1. 先来演示使用数组名来访问数组元素:
/*数组名访问数组元素*/
#include <stdio.h>
#define ARRAY_SIZE 6
int main(){
int list[ARRAY_SIZE]={11,12,13,14,15,16};
int i;
for(i=0;i<ARRAY_SIZE;i++)
printf("地址:%#p 值:%d 值:%d\n",(list+i),*(list+i),list[i]);
return 0;
}
数组名list等价于数组首元素的地址&list[0], list为指向数组元素list[0]的指针。当一个指针指向数组中的某个元素时,可以对指针进行算术运算。因此,list+1等价于&list[1],list+1为指向小 数组元素 list[1]的指针;list+2等价于&list[2],list+2为指向数组元素list[2]的指针;list+i等价于&list[i],list+i为指向数组元素list[i] 的指针。
可以通过下标法来访问数组元素:list[i]。也可以通过指针法来访问数组元素,list+i为指向数组元素list[i]的指针,使用解引用运算符“*”,*(list+i)就是list[i]。
程序第5行声明了整型数组list,第7、8行的for循环通过(list+i)输出数组元素的地址,通过*(list+i)和list[i]输出数组元素的值。
上面是通过数组名来访问数组元素,也可以通过指针变量来访问数组元素。
2.演示使用指针变量来访问数组元素:
/*指针变量访问数组元素*/
#include <stdio.h>
#define ARRAY_SIZE 6
int main(){
int list[ARRAY_SIZE]={11,12,13,14,15,16};
int *p=list;
int i;
for(i=0;i<ARRAY_SIZE;i++)
printf("地址:%#p 地址:%#p 值:%d 值:%d 值:%d 值:%d\n",(list+i),(p+i),*(list+i),list[i],*(p+i),p[i]);
return 0;
}
程序第五行声明了整型数组list;第六行声明了整型指针变量p并指向list数组。
下面两行代码是等价的:
int *p=list;
int *p=&list[0];
数组名是数组首元素的地址。这样指针变量p就指向了数组元素list[0];
从上面的程序可见,使用指针变量也可以通过下标法或指针法来访问数组元素。p[i]=list[i](下标法),*(p+i)就是list[i](指针法)。同样,p+1是指向下一个元素的指针。
指针与一维数组:
对数组list中元素求和的程序代码:
#define ARRAY_SIZE 10;
int list[ARRAY_SIZE],sum,i;
...
sum=0;
for(i=0;i<ARRAY_SIZE;i++)
sum+=list[i];
数组允许通过指针变量来访问数组元素,上面的程序代码改写如下:
#define ARRAY_SIZE 10;
int list[ARRAY_SIZE],sum,*p;
...
sum=0;
for(p=&list[0];p<=&list[ARRAY_SIZE-1];++p)
sum+=*p;
在for循环中,指针p的初始值为数组list首元素的地址;每次执行循环时,p进行
自增操作,指向下一个数组元素。p先指向list[0];*p即为list[0],然后指向list[1];
*p即为list[1],以此类推,直至p指向最后一个元素list[ARRAY_SIZE-1],循环结束。
数组名时数组首元素的地址,list等价于&list[0],list+ARRAY_SIZE-1等价于&list[ARRAY_SIZE-1]。
上面的程序代码可以进一步改写如下:
#define ARRAY_SIZE 10;
int list[ARRAY_SIZE],sum,*p;
...
sum=0;
for(p=list;p<=list+ARRAY_SIZE-1;++p)
sum+=p;
上面的代码还可以写成:
#define ARRAY_SIZE 10;
int list[ARRAY_SIZE],sum,i,*p;
...
sum=0;
for(i=0,p=list;i<=ARRAY_SIZE-1;++1)
sum+=p[i];
指针和数组的关系是非常密切的,在处理数组元素时,既可以使用下标法,也可以采用指针法。
注意:数组名是一个常量指针,不能给数组名赋值。 在用指针处理数组元素时,经常会组合使用解引用运算符“* ”和自增运算符“++ ”。
p=list;
*p++=88;
由于后缀"*"的优先级高于"*",上述语句被视为:
*(p++)=88;
也就是:
*p=88;
p++;
将88赋值给p所指向的数组元素list[0],然后p自增,指向下一个数组元素list[1]。
根据这种写法,上面的程序也可以写成:
#define ARRAY_SIZE 10;
int list[ARRAY_SIZE],sum,*p;
...
p=list;
sum=0;
while(p<=list+ARRAY_SIZE-1)
sum+=*p++
假设p指向数组元素list[0],list[0]的当前值为88;
(*p)++;
等价于:
list[0]++;
list[0]的值加1,变成89。
当数组的元素类型为指针类型时,该数组就是一个指针数组。声明指针数组的一般形式如下:数据类型 *数组名[常量表达式]
数组名首先与后面的[]结合,表明是数组,再与数组的指针运算符"*"结合,说明数组的元素类型是指针类型。
指针与二维数组:理清指针对二维数组中元素的访问。
二维数组list的逻辑结构:
数组名list表示数组第一行 list[0] 的地址 (&list[0]) , list+i 表示数组第i行 list[i] 的地址 (&list[i])。
list[0] 表示数组第一行首元素 list[0][0] 的地址 (&list[0][0])。list[i]表示数组第i行 list[i] 的地址(&list[i])。
list[0]+1 表示数组第一行第二个元素 list[0][1] 的地址 (&list[0][1])。list[i]+j 表示数组第i行第j列的地址 (&list[i][j])。*list([i]+j) 表示数组第i行第j列元素 (list[i][j])。
下面我们来了解动态存储分配:
为了解决数组长度是固定的这一缺点,C语言提供与一种“动态存储分配”机制,使程序在运行过程中能根据需要分配内存空间。动态存储分配的内存空间通常称为堆。
为了动态存储分配内存空间,C语言提供了4个函数:malloc()函数、calloc()函数、realloc()函数和free()函数。使用这些函数时必须包含stdlib.h头文件。这几个函数在学数据结构时也会经常用到。
下面一一来介绍这几个函数:
malloc()函数:
void *malloc(size_t size);
malloc()函数分配指定大小(size个字节)的内存空间,但不对分配的内存空间进行初始化,并返回指向该内存空间的通用指针。
eg. int *p;
p=malloc(10 * sizeof(int));
分配了可以存放10个整数的内存空间。应该通过长度运算符sizeof来计算需要分配的内存空间大小,
而不要直接用整数,以免出现不必要的错误。
在赋值时,会把malloc()函数返回的通用指针(void*)自动转换为整型指针(int*)。
当然也可以明确使用强制类型转换。
eg. p=(int *)malloc(10 * sizeof(int));
如果不能够成功分配指定大小的内存空间,malloc()函数会返回空指针NULL。
可以使用如下方式测试malloc()函数的返回值:
p=(int *)malloc(10 * sizeof(int));
if(o==NULL){
printf("malloc函数分配失败!\n");
exit(EXIT_FALLURE);
}
calloc()函数:
void *calloc(size_t nmemb,size_t size);
分配指定大小(nmemb * size字节)的内存空间,对分配的内存空间进行初始化,并返回指定该内存空间的通用指针。
如果不能够成功分配指定大小的内存空间,calloc()函数会返回空指针NULL。
eg. p=(int *)calloc(10,sizeof(int));
if(p==NULL){
printf("calloc函数分配失败!\n");
exit(EXIT_FALLURE);
}
malloc函数最常用。因为malloc()函数不对分配的内存空间进行初始化,因此比calloc()函数更高效。
realloc()函数:
void *realloc(void *ptr,size_t size);
调整先前分配的内存空间大小,并返回指向该内存空间的通用指针。
指针ptr指向先前由malloc()、calloc()或realloc()分配的内存空间,
size表示新内存空间大小(大于或小于原有内存空间大小)。
如果不能够成功调整先去分配的内存空间大小,realloc()函数会返回空指针NULL,先前内存
空间中的数据不会发生改变。
eg.
int *q;
q=(int *)realloc(p,20 * sizeof(int));
if(q==NULL){
printf("realloc函数调整失败!\n");
exit(EXIT_FALLURE);
}
如果realloc()函数的第一个参数为空指针NULL,其功能就像malloc()函数一样。
eg.
p=(int *)realloc(NULL,10 * sizeof(int));
等价于:
p=(int *)malloc(10 *sizeof(int));
如果realloc()函数的第二个参数是0,释放先前分配的内存空间。
eg.
realloc(p,0);
释放指针p所指向的内存空间。
free()函数:
void free(void *ptr);
free()函数释放先前由malloc()函数或calloc()函数分配的内存空间。指针ptr指向要释放的内存空间。
eg.
int *p=(int *)malloc(sizeof(int));
int *q=(int *)malloc(sizeof(int));
指针p和q分别指向各自的动态存储分配的内存空间。
p=q;
把q赋值给p后,指针p和q都指向同一个内存空间。
现在没有指针指向原来指针p指向的内存空间,该内存空间就无法访问了。这种无法再访问到的内存空间称为垃圾。
程序中如果留有垃圾,往往会发生内存泄露问题。动态存储分配的内存空间一旦使用完毕应立即释放。
因此应该在q赋值给p之前,释放不再需要的内存空间。
eg.
free(p);
p=q;
调用free()函数释放指针p所指向的内存空间。此内存空间被释放后,可以重新分配使用。
最后来演示动态存储分配数组:
/*动态存储分配数组*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void printArray(const int list[],int arraySize);
int main(){
int i,arraySize;
int *array;
printf("请输入数组元素个数:");
scanf("%d",&arraySize);
array=(int *)malloc(arraySize * sizeof(int));
if(array==NULL){
printf("malloc()函数分配失败!\n");
exit(EXIT_FAILURE);
}
srand(time(NULL));
for(i=0;i<arraySize;i++)
array[i]=rand()%100;
printArray(array,arraySize);
free(array);
return 0;
}
void printArray(const int list[],int arraySize){
int i;
for(i=0;i<arraySize-1;i++)
printf("%d ",list[i]);
printf("%d\n",list[arraySize-1]);
}
每次运行的结果可能不同。
程序第6~22行是main()函数,第23~28行是printArray()函数。
在main()函数中,第8行声明了整型指针变量array,要使用动态存储分配首先要声明指针变量; 第10行从键盘输入需要的数组长度并存放在变量arraySize中; 第11行动态存储分配数组长度为arraySize的数组 array,它可以容纳 arraySize个整数;一旦array指向动态存储分配的内存空间,就可以把array视为数组名; 第16行使用当前时间初始化随机数“种子”值; 第17、18行产生arraySize个0~99范围内的随机数并存放在数组array中; 第19行调用printArray()函数输出数组array的值;第20行释放array所指向的内存空间。
有关指针与数组的笔记就分享到这啦!若对小伙伴们有帮助的话请留个👍哇!!!