C语言指针与数组笔记2
8.3通过指针引用数组
8.3.1数组元素的指针
一个变量有地址,一个数组包含若干个元素,每个数组元素都在内存中占用储存单元,它们都有相对应的地址。指针变量可以指向变量,也可以指向数组元素。数组元素的指针就是数组元素的地址。
可以用一个指针变量指向数组元素,例如:
int a[10] = {1,3,4,6,7,9};//定义一个整型数组
int * p;//定义p为指向整型变量的指针变量
p = &a[0];//把a[0]的地址赋值给了指针变量
//p = a等价于p = &a[0]
????注意:程序中的数组名不代表整个数组,只代表数组首元素的地址。
定义指针变量初始化:
int * p = a;//或者int * p =&a[0]
它的作用是将数组a的首元素地址赋值给指针变量p。
8.3.1在引用数组时的指针的运算
在一定条件下允许对指针进行加和减的运算。在指针指向数组元素时,可以对指针进行以下运算:
加一个整数(用+或+=),如p+1;
减一个整数(用-或-=),如p-1;
自加运算,如p++, ++p;
自减运算,如p–,--p。
两个指针相减,如p1-p2(只有p1和p2都指向同一个数组中的元素时才有意义)
分别说明如下:
(1)如果指针变量p已指向数组中的一个元素,则p+1指向同一个数组中的下一个元素,p-1指向同一个数组中的上一个元素。执行p+1时并不是将p的值(地址)简单地加1,而是加上一个数组元素所占用的字节数。例如,数组元素是float型,每个元素占用4个字节,则p+1意味着使p的值加4个字节,以使它指向下一个元素。
(2)如果p的初值为&a[0],则p+i和a+i就是数组元素a[i]的地址,或者说它们指向a数组序号为i的元素。
(3)(p+i)和(a+i)是p+i或a+i所指向的数组元素,即a[i]。例如,(p+5)或(a+5)就是a[5],即这三者等价。
(4)如果指针变量p1和p2都指向同一数组,如执行p2-p1,结果是p2-p1的值(两个地址之差)除以单个数组元素的长度。表示p1和p2所指元素的相对距离,两个地址不能相加,如p1+p2是无实际意义的。
8.3.3通过指针引用数组元素
(1)下标法,如a[i]类型。
(2)指针法,如*(a+i)或*(p+i)。其中a是数组名,p是指向数组元素的指针变量。
例如:用指针变量p来访问数组元素。
#include<stdio.h>
int main()
{
int arry[] = {56,67,98,45,23};
int i;
int * ptr_arry = arry;//赋值完毕就可以使用指针访问数组元素了
for(i = 0;i < 5;i++)
{
printf("第%d个元素的值是%d\t地址是%p\n",i,*ptr_arry,ptr_arry);
ptr_arry ++;
}
}
⭐插入一个小提示:*(p++)和 *(++p)的作用是不相同的。
*(p++)先取 *p值,再使p加1。
(++p)先使p加1,再取p值。
8.3.4用数组名作函数参数
在用数组名作为函数实参时,既然实际上相应的形参是指针变量,为什么还允许使用形参数组的形式呢?
这是因为在C语言中用下标法和指针法都可以访问一个数组(如果有一个数组a,则a[i]和*(a+i)无条件等价),用下
标法表示比较直观,便于理解,因此许多人愿意用数组名作形参,以便与实参数组相对应。从应用的角度看,用户可以认为
有一个形参数组,它从实参数组那里得到起始地址,因此形参数组和实参数组共占同一段内存单元,在调用函数期间,如果
改变了形参数组的值,也就改变了实参数组的值。
注意:实参数组名代表的是一个固定的地址,或者说是一个指针常量,但形参数组名并不是一个固定的地址,而是按指针变量处理。
例题:将数组a中n个整数按相反顺序存放
这时,我们可以用指针变量作形参。
#include<stdio.h>
int main()
{
void inv(int *x,int n);//inv函数申明
int i,arry[10],*p = arry;//指针变量P指向arry[0]
printf("please input the original number:\n");
for(i = 0;i < 10;i++)
scanf("%d",p);//输入arry数组的元素,也可以用printf("%d",arry[i])
printf("\n");
p = arry;//指针变量重新指向arry[0]
inv(p,10);//调用inv函数,实参P是指针变量
printf("the arry has inverted:\n");
for(p = arry;p < arry + 10;p++)
{
printf("%d",*p);
printf("\n");
return 0;
}
}
void inv(int *x,int n) //定义inv函数,形参x是指针变量
{
int * p,m,temp,* i,* j;
m = (n - 1)/2;
j = x + n - 1;p = x + m;
for(i = x;i <= p;i++,j--)
{
temp = *i;
*i = *j;
*j = temp;
return ;
}
}
运行结果
8.3.5通过指针引用多维数组
int a[3][4] = {{1,3,5,7},{9,11,13,15},{17,19,21,23}};
表示形式 含义 地址
a 二维数组名,指向一维数组a[0],即0行首地址 2000
a[0], * (a+0), * a 0行0列元素地址 2000
a+1,&a[1] 1行首地址 2016
a[1], *(a+1) 1行0列元素a[1][0]的地址 2016
a[1]+2, * (a+1)+2, &a[1][2] 1行2列元素a[1][2]的地址 2024
*(a[1]+2), ((a+1)+2), a[1][2] 1行2列元素a[1][2]的值 元素值为13
如果用一个指针变量指示一维数组,应该是这样定义的:
int (*p) [4];
p表示为指向由4个整型元素组成的一维数组。
指向多维数组元素的指针变量
例:有一个34的二维数组,要求用指向指针元素的指针变量输出二维数组各元素的值。
解题思路:二维数组的各元素都是整型,它相当于整型变量。所有可以用int型指针变量指向它。可以用一个指向整型元素的指针变量,依次指向各元素。
#include<stdio.h>
int main()
{
int a[3][4] = {1,3,5,7,9,11,13,15,17,19,21,23};
int * i;//定义一个指针变量i
for(i = a[0];i < a[0] + 12;i++)
{
if((i -a[0])%4 == 0)
printf("\n");
printf("%4d",*i);
}
printf("\n");
return 0;
}
运行结果
8.5通过指针引用字符串
用指针方式与用字符数组方式操作字符串的区别:
(1)存储方式不同。字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串首个字符的地址),决不是将字符串放到字符指针变量中。
(2)赋值方式不同。
对字符数组只能对各个元素赋值,不能用以下办法对字符数组赋值。
char str[20];
str="Good luck!";
而对字符指针变量,可以采用下面方法赋值:
char *str;
str="Good luck!";
赋给str的不是字符,而是字符串第一个元素的地址。
(3)输入方式不同。
对字符数组可用如下方式赋值:
char str[20];
scanf("%s",str); //ok
但对字符指针变量则不建议用此方式,因为会发生指针的异常指向,正确做法为:
char *str,a[20];
str=a; //使指针有明确的指向
scanf("%s",str);
(4)字符指针变量的值是可以改变的,而字符数组名的值是不可以改变的。
例1:#include <stdio.h>
void main()
{
char *str="Good luck!";
puts(str);
str+=5; //ok
puts(str);
}
例2:
#include <stdio.h>
void main()
{
char str[20]="Good luck!";
puts(str);
str+=5; //error!
puts(str);
}
????指针的指针的指针
指向指针的指针也就是“二级指针”。
定义方式如下:类型说明 **指针变量名
即定义一个二级指针变量,类型说明是它指向的指针变量所指向的变量的数据类型。它所指向的指针变量称为一级指针变量。
赋值形式为:二级指针变量=&一级指针变量;
这类似于张三有李四的地址,而王五有张三的地址,这样王五通过张三找到李四。这样张三是一级指针,而王五是二级指针。
例如:
#include <stdio.h>
void main()
{char *name[]={ "Follow me","BASIC","Great Wall″,
"FORTRAN","Computer design"};
char **p;int i;
for(i=0;i<5;i++)
{ p=name+i; printf(″%s\n″,*p);}
}