【20180806】【C/C++基础知识】指针与一维数组(访问、输出数组元素,指针与数组的首地址),指针与二维数组(行指针、列指针,访问、输出二维数组元素),指针数组处理多个字符串

C编译程序用数组名存放数组在内存中的首地址。

指针访问内存比变量名访问速度更快,数组采用指针加减1的方式访问数组,增加了访问内存的灵活性。

指针与一维数组

  • 指针移动方式访问数组元素:
int a[5], *p;     // a存放了数组的首地址
p=a;     // 数组首地址赋给p
p++;     // 向下移动4个字节

指针与数组名都是地址,因此可以混合使用访问数组元素。

int *pa=a; 和 int *pa=&a[0];(// a的首地址)两者完全等价!

pa和a可以混合使用的图片说明,前面一列是地址(a和pa都表示地址),加*表示里面的元素。指针还可以移动,如图2。

  • 数组元素的等价引用形式:

a[i]

pa[i]   // pa和a可以混合使用,pa和a都表示首地址

*(a+i)   // 表示地址的前面加一个*,就代表该地址里面的元素

*(pa+i)   // pa和a混合使用

  • 对应的数组元素的地址表现形式:

&a[i]    // 加取地址符号就表示地址

&pa[i]

(a+i)    // *和&互为逆运算,因此前面没有了符号

(pa+i)  

注意(区别):

数组名是指针常量,不是指针变量,因此不能给数组名赋值!但指针可以。例如:pa=pa+1这是可以的,但是不能有a=a+1这种操作,任何修改数组名的操作都是错误的!

程序1:用指针访问数组,计算数组的元素之和。

/* 用指针访问数组,计算数组的元素之和 */
#include<stdio.h>
#include<stdlib.h>
int main()
{
    int iarray[10]={0,2,4,6,8,10,12,14,16,18},*p=iarray;
    int i,sum=0;
    for(i=0;i<10;i++)
    {
        sum+=*p;
        p++;        // sum+=*iarray; iarray++是错误的!数组名不能进行自增操作!
    }
    printf("sum is %d\n",sum);
    system("pause");
    return 0;
}

注意:p++并不是简单的加1,而是加了它的基类型所占的字节数,即移动了sizeof(int)=4个字节。sizeof()宏定义可获取所占字节数。

指针快速访问数组,但也只能逐个处理,不能一次性处理所有元素。

  • 用指针进行数组元素的输入输出(同样只能一次处理一个元素,用for循环):

for(i=0;i<4;i++)

{

       scanf_s(“%d”,&a[i]);   // 输入

       printf(“%d”,a[i]);   // 输出

}

for(p=a;p<(a+4);p++)

{

       scanf_s(“%d”,p);   // 用指针输入,p本身是个地址,不用再加取地址符号

       printf(“%d”,*p);   // 用指针输出

}

  • 访问数组元素的方法(五种,可混合使用):

void main()    // 定义和初始化

{

       int sum1,sum2,sum3,sum4,sum5;

       int iarray[]={1,3,2,5,2,56,34,2,7,60};

       int *iptr;

       int size,n;

 

       sum1=sum2=sum3=sum4=sum5=0;

       size=sizeof(iarray)/sizeof(*iarray);

}

第一种:数组名和下标的方式

for(n=0;n<size;n++)

{

       sum1+=iarray[n];

}

第二种:通过移动指针,逐个元素的访问

iptr=iarray;

for(n=0;n<size;n++)

{

       sum2+=*iptr;

}

第三种:通过指针和间接地址的方式,访问数组的所有元素

iptr=iarray;

for(n=0;n<size;n++)

{

       sum3+=*(iptr+n);

}

第四种:通过指针,引用下标的方式,按照数组的方式访问数组所有元素

iptr=iarray;

for(n=0;n<size;n++)

{

       sum4+=iptr[n];

}

第五种:通过数组名加偏移量的方式

for(n=0;n<size;n++)

{

       sum5+=*(iarray+n);

}

  • 求数组的首地址(两种):

第一种:通过数组名获得首地址(数组名)

第二种:通过数组的第一个元素来获得首地址(首个元素地址),这里指针变量p是通过数组名获得指向数组a的第一个元素。

  • 通过数组首地址访问数组元素的方式:

第一种:直接访问(数组名+下标变量,如a[1])

第二种:指针加偏移量的间接地址访问,如*(p+i)

第三种:用数组名做地址值(指针值)的间接地址访问,如与a[i]等价的语句为*(a+i)

第四种:将指针变量看做数组名,再加下标变量,如p[i]。

总结:

数组名是表示数组元素的连续空间的首地址,可以看做一个“常量”指针,它的值不能被修改,即不能修改其指向。数组元素可通过 下标法来引用,也可通过指针来引用,还可以混合使用。同样地,指针也可以当做一个数组名来使用。(方便、灵活、易混淆、易出错)

指针与二维数组

C语言是按照行优先的原则存储数组的。

int a[2][3];

这样理解:我们把每一行看成一个数据元素,那么就有a[0]和a[1]两个元素(两个一维数组),地址分别为a+0和a+1。

注意:这里a[i]不表示元素,还是表示地址,是这一行一维数组的首地址,那么a[i]+j就表示第i行第j个元素的地址。即a[i]+j和&a[i][j]完全等价(都表示第i行第j列元素的地址),a[i][j]和*a[i]+j完全等价(都表示第i行第j列的元素)。

  • 二维数组与行:

(1) a代表二维数组的首地址,也是第0行的首地址;

(2) a+i代表第i行元素的首地址,即&a[i];

(3) a+i或&a[i]表示行地址,每次加一表示会移动到下一行;

(4) *(a+i)即a[i],不代表具体元素,依然是个地址(第i行的首地址)。

  • 二维数组与列:

(1) 对于二维数组,第i个元素依然是一个一维数组,即*(a+i)即a[i]依然是一个地址;

(2) 对于每一个一维数组a[i]或*(a+i),a[i]+j或*(a+i)+j表示该一维数组a[i]中第j个元素的地址,即&a[i][j];

(3) *(a[i]+j)或*(*(a+i)+j)代表一维数组a[i]中第j个元素的值,即a[i][j];

(4) a[i]或*(a+i)表示列地址,每次加一会移动一个元素。

  • 二维数组的理解(行地址、列地址):

例:int a[2][4];

(1) 有两行,a[0]和a[1]分别为两行的首地址;

(2) 每一行有四个元素;

(3) 对于每一行的地址是a+0,a+1,或&a[0],&a[1];

(4) a+0或&a[0]对这一行是首地址,因为我们叫它“一维数组名a[0]”,后面的中括号就是该一维数组名的下标。如下图:

问题:指针增一怎么判断是行增一还是列增一?

答:由指针类型决定!

  • 行指针、列指针

1. 行指针:

int a[3][4];

int (*p)[4];  // 定义一个行指针,指向一个数组,有四个元素。

p=a;   // 用行地址初始化

理解:p—>*—>[4]—>int。先表示它是一个指针,然后让它指向一个长度为4的数组,最后说明是整型指针。因为后面括号是4,因此每次移动p++就移动4个整数,就是一行。即指针移动多少,与我们定义时指向的数组长度有关系。

区分:int *p[4];   // 长度为4的指针数组!与行指针不同!

规律:定义行指针一定要先把(*p)用括号括起来!后面跟的中括号[4]表示每移动一次,移动多少个元素,即二维数组的列数是多少!(指针数组定义见后面)

问题:怎么通过行指针访问数组的每个元素?

int (*p)[4];

p=a;

for(i=0;i<m;i++)

for(j=0;j<n;j++)

printf(“%d”,p[i][]j);   // 下标的索引方式

理解:p指向数组的首地址,因此它和数组名a完全一样,因此我们可以用指针p或数组名a访问元素。也可以用其他方式访问。但要注意的是数组名不能做自增自减操作!除此之外,指针和数组名是完全一样的!

2. 列指针:

int *p=*a;    // 用列地址初始化。

列指针就是对一个一维数组的操作,因此它和普通指针是一样的!注意:不能让p指向a,因为a是二维数组的首地址,是行地址!*a是第0行的首地址。

问题:怎么用列指针访问数组元素?

int *p=*a;   // 即p=*a; *a是第0行的首地址

for(i=0;i<m;i++)    // 行

for(j=0;j<n;j++)    // 列

printf(“%d”,*(p+i*n+j));    // 或下标索引:p[i*n+j]; 它是一个元素一个元素处理的。

总结:

(1) 二维数组首地址是行地址;

(2) 行指针指向二维数组名,行指针加减一,就移动一行;

(3) 行指针+i表示第i行的行地址,取*就转换为列地址,转为对一维数组的处理;

 

指针数组

int a[3][4];

int *p[4];

p=a;    // 错误!指针数组中没有这种初始化形式!只有行指针才能指向行地址!这里的p不是行指针!

 

int (*p)[4];

p=a;    // 行指针指向二维数组行地址

 

int *p[4];    // 指针数组

 

指针数组:一个数组中若每个元素都是一个指针,那么称它为指针数组。

(1) 由基类型相同的指针构成;

(2) 每个元素都是一个指针;

(3) 使用前必须初始化;

(4) 常用于对多个字符串进行处理。(一维字符数组可以处理一个字符串,那么二维字符数组可以存储和处理多个字符串)

例如:

char *proname[]={“FORTRAN”,”C”,”C++”};   // 定义一个指针数组并初始化

表示:有三个字符串,因此指针数组的长度为3,即这个数组有3个元素,每个元素都是一个指针。字符串常量是存放在数据区的const存储区中,可能连续也可能不连续。proname存储了字符串常量的首地址。

在内存中的表示:

可以看出,指针数组可以处理多个字符串。指针数组的长度是多少,就可以最多处理多少个字符串。

问题:指针数组和二维字符数组有什么区别?

#define N 100   // 最多处理100个字符串

char *str[N];    // 指针数组

 

#define N 100   // 最多处理100个字符串

#define M 50   // 每个字符串长度不超过50

char str[N][M];    // 二维字符数组

 

例如:

假设有100个字符串,90个字符串的长度为2,10个字符串的长度为50。

解释:

(1) 一个是字符指针数组,每个元素都是用来存储字符串的地址。

(2) 字符串的长度可能不同,并且字符串之间可能没有关系,存储空间可能连续也可能不连续。

(3) 字符指针数组所需空间大小:100*4+90*2+10*50=1080(100个字符串的地址,每个地址占4个字节+字符串本身占据的空间)。二维字符数组所需空间:要求字符串长度最大为M,那么如果字符串长度超过M会发生越界,如果小于M,不会出错,但所需空间(1080)远小于我们产生的空间大小(5000),造成浪费。

(4) 字符指针数组元素指向的字符串可以是不规则的长度。字符二维数组的每个元素的长度必须相同,在定义时已确定。

(5) 因此可以看出,字符指针数组对空间的需求更少,更适合处理多个字符串。

  • 10
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Satisfying

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值