Menu Close

C语言 – 指针和数组

数组元素是组成数组的基本单元。数组元素也是一种变量, 其标识方法为数组名后跟一个下标。下标表示了元素在数组中的顺序号。数组元素通常也称为下标变量。必须先定义数组, 才能使用下标变量。在C语言中只能逐个地使用下标变量,而不能一次引用整个数组。

一般形式
数组元素的一般形式为:
数组名[下标]
其中下标只能为整型常量或整型表达式。如为小数时,C 编译将自动取整。
例如:
a[5]
a[i+j]
a[i++]
都是合法的数组元素。

1.什么是数组元素的指针?

一个变量有一个地址,一个数组包含若干数组元素,每个数组元素都在内存中占用一个存储单元,它们都有相应的地址。所谓数组的指针是指数组的起始地址,数组元素的指针是数组元素的地址。

指向数组元素的指针

一个数组是由连续的一块内存单元组成的。数组名就是这块连续内存单元的首地址。一个数组也是由各个数组元素(下标变量)组成的。每个数组元素按其类型不同占有几个连续的内存单元。一个数组元素的首地址也是指它所占有的几个内存单元的首地址。

定义一个指向数组元素的指针变量的方法,与以前介绍的指针变量相同。例如:

int a[10]; /*定义 a 为包含 10 个整型数据的数组*/
int *p; /*定义 p 为指向整型变量的指针*/

应当注意,因为数组为 int 型,所以指针变量也应为指向 int 型的指针变量。下面是对指针变量赋值:

p=&a[0];

把 a[0]元素的地址赋给指针变量 p。也就是说,p 指向 a 数组的第 0 号元素。

C 语言规定,数组名代表数组的首地址,也就是第 0 号元素的地址。因此,下面两个语句等价:

p=&a[0];
p=a;

在定义指针变量时可以赋给初值:

int *p=&a[0];

它等效于:

int *p;
p=&a[0];

当然定义时也可以写成:

int *p=a;

从图中我们可以看出有以下关系:

p,a,&a[0]均指向同一单元,它们是数组 a 的首地址,也是 0 号元素 a[0]的首地址。

应该说明的是 p 是变量,而 a, &a[0]都是常量。在编程时应予以注意。

数组指针变量说明的一般形式为:


类型说明符 *指针变量名;

其中类型说明符表示所指数组的类型。从一般形式可以看出指向数组的指针变量和指向普通变量的指针变量的说明是相同的。

我们喜欢在程序中使用指针代替数组,因为变量指针可以递增,而数组不能递增,数组可以看成一个指针常量。

C 语言规定:如果指针变量 p 已指向数组中的一个元素,则 p+1 指向同一数组中的下一个元素。

引入指针变量后,就可以用两种方法来访问数组元素了。

如果 p 的初值为&a[0],则:

1) p+i 和 a+i 就是 a[i]的地址,或者说它们指向 a 数组的第 i 个元素

2) *(p+i)或*(a+i)就是 p+i 或 a+i 所指向的数组元素,即 a[i]。例如,
*(p+5)或*(a+5) 就是 a[5]
3) 指向数组的指针变量也可以带下标,如 p[i]与*(p+i)等价

根据以上叙述,引用一个数组元素可以用:

1) 下标法,即用 a[i]形式访问数组元素。在前面介绍数组时都是采用这种方法。
2) 指针法,即采用*(a+i)或*(p+i)形式,用间接访问的方法来访问数组元素,其中 a是数组名,p 是指向数组的指针变量,其处值 p=a。

例1.1 输出全部数组元素(通过数组名计算数组元素的地址,找出数组元素的值)

main()
{
int a[10],i;
for(i=0;i<10;i++)
   *(a+i)=i;
for(i=0;i<10;i++)
   printf("a[%d]=%d\n",i,*(a+i));
}

几个注意的问题:

1) 指针变量可以实现本身的值的改变。如 p++是合法的;而 a++是错误的。因为 a 是数组名,它是数组的首地址,是常量。
2) 要注意指针变量的当前值。

查找以下赋值程序的错误

例1.2 错误的赋值程序

main()
{
   int *p,i,a[10];
   p=a;
   for(i=0;i<10;i++)
      *p++=i;
  for(i=0;i<10;i++)
       printf("a[%d]=%d\n",i,*p++);
}

结果

例1.3. 正确的赋值程序

main()
{
   int *p,i,a[10];
   p=a;
   for(i=0;i<10;i++)
     *p++=i;
   p=a;
   for(i=0;i<10;i++)
     printf("a[%d]=%d\n",i,*p++);
}

结果

 

3) 从上例可以看出,虽然定义数组时指定它包含 10 个元素,但指针变量可以指到数组以后的内存单元,系统并不认为非法。

4) *p++,由于++和*同优先级,结合方向自右而左,等价于*(p++)。

5) *(p++)与*(++p)作用不同。若 p 的初值为 a,则*(p++)等价 a[0],*(++p)等价 a[1]。

6) (*p)++表示 p 所指向的元素值加 1。

7) 如果 p 当前指向 a 数组中的第 i 个元素,则

*(p–)相当于 a[i–];

*(++p)相当于 a[++i];

*(–p)相当于 a[–i]。

8)a[i++]和a[++i]的区别:

相同点:都加1,都使i的值变成下一个元素的序号。
异同点:a[i++]中i++是后自增,必须先使用当前元素的值再使用下一个元素的值,a[++i]中++i是前自增,可以直接使用下一个元素的值。

例1.4 a[i++]和a[++i]的区别

#include <stdio.h>
int main ()
{
   int a[3] = {1,2,3};
   int i = 0;
   printf("%d\n",a[i++]);//本输出的值为1,因为是i++,所以是先使用a[0]的值,再加上1,即先输出a[0]的值。
   i = 0;
   printf("%d\n",a[++i]);//本输出的值为2,因为++i,所以直接使i加1,即输出a[1]的值。
   return 0;
}

例1.5.通过指针打印出数组元素的值

#include <stdio.h>
int main()
{
    int a[5]={1,2,3,4,5};   //array initialization
    int *p;     //pointer declaration
               /*the ptr points to the first element of the array*/
 
    p=a; /*We can also type simply ptr==&a[0] */
     
    printf("Printing the array elements using pointer\n");
    for(int i=0;i<5;i++)    //loop for traversing array elements
    {
            printf("\n%x",*p);  //printing array elements
            p++;    //incrementing to the next element, you can also write p=p+1
    }
    return 0;
}

 

例1.6.打印出一个数组每个数组元素的地址

#include <stdio.h>
int main() {
   int x[4];
   int i;

   for(i = 0; i < 4; ++i) {
      printf("&x[%d] = %p\n", i, &x[i]);
   }

   printf("Address of array x: %p", x);

   return 0;
}

结果

&x[0] = 1450734448
&x[1] = 1450734452
&x[2] = 1450734456
&x[3] = 1450734460
Address of array x: 1450734448

数组 x 的两个连续元素之间有 4 个字节的差异。这是因为 int 的大小是 4 个字节(在我们的编译器上)。

请注意,&x[0] 和 x 的地址是相同的。这是因为变量名 x 指向数组的第一个元素。

数组和指针的关系
数组和指针的关系

从上面的例子中,很明显 &x[0] 等价于 x。并且,x[0] 等价于 *x。

相似地,

&x[1] 等价于 x+1,x[1] 等价于 *(x+1)。
&x[2] 等价于 x+2,x[2] 等价于 *(x+2)。

基本上,&x[i] 等价于 x+i,而 x[i] 等价于 *(x+i)。

 

把特定的整数数字添加到指针,会把指针位置移动到通过加法运算获得的值。

例1.7.利用数组和指针打印出六个数组元素的和

#include <stdio.h>
int main() {

  int i, x[6], sum = 0;

  printf("Enter 6 numbers: ");

  for(i = 0; i < 6; ++i) {
  // Equivalent to scanf("%d", &x[i]);
      scanf("%d", x+i);

  // Equivalent to sum += x[i]
      sum += *(x+i);
  }

  printf("Sum = %d", sum);

  return 0;
}

结果

Enter 6 numbers: 2
3
4
4
12
4
Sum = 29

在这里,我们声明了一个包含 6 个元素的数组 x。为了访问数组的元素,我们使用了指针。

在大多数情况下,数组名称可以被转换为指针。这就是您可以使用指针访问数组元素的原因。但是,您应该记住,指针和数组并不相同。

例1.8.把指针指向第三个数组元素的地址

结果

*ptr = 3
*(ptr+1) = 4
*(ptr-1) = 2

在这个例子中,&x[2],第三个元素的地址,被分配给 ptr 指针。因此,当我们打印 *ptr 时显示 3。

而且,打印 *(ptr+1) 给了我们第四个元素。类似地,打印 *(ptr-1) 给了我们第二个元素。

 

2.两个指针变量之间的运算

只有指向同一数组的两个指针变量之间才能进行运算,否则运算毫无意义

A) 两指针变量相减:两指针变量相减所得之差是两个指针所指数组元素之间相差的元素个数。实际上是两个指针值(地址)相减之差再除以该数组元素的长度(字节数)。

例如 pf1 和 pf2 是指向同一浮点数组的两个指针变量,设 pf1 的值为 2010H,pf2的值为 2000H,而浮点数组每个元素占 4 个字节,所以 pf1-pf2 的结果为(2000H-2010H)/4=4,表示 pf1 和 pf2 之间相差 4 个元素。两个指针变量不能进行加法运算。 例如,pf1+pf2 是什么意思呢? 毫无实际意义。

B) 两指针变量进行关系运算:指向同一数组的两指针变量进行关系运算可表示它们所指数组元素之间的关系。

pf1==pf2 //表示 pf1 和 pf2 指向同一数组元素;
pf1>pf2 //表示 pf1 处于高地址位置;

指针变量还可以与 0 比较
设 p 为指针变量,则 p==0 表明 p 是空指针,它不指向任何变量;
p!=0 表示 p 不是空指针。空指针是由对指针变量赋予 0 值而得到的。

例如:

#define NULL 0
int *p=NULL;

对指针变量赋 0 值和不赋值是不同的。指针变量未赋值时,可以是任意值,是不能使用的。否则将造成意外错误。而指针变量赋 0 值后,则可以使用,只是它不指向具体的变量而已。

例2.1 两个指针变量的运算

int main()
{
   int a=10,b=20,s,t,*pa,*pb; /*说明 pa,pb 为整型指针变量*/
   pa=&a; /*给指针变量 pa 赋值,pa 指向变量 a*/
   pb=&b; /*给指针变量 pb 赋值,pb 指向变量 b*/
   s=*pa+*pb; /*求 a+b 之和,(*pa 就是 a,*pb 就是 b)*/
   t=*pa**pb; /*本行是求 a*b 之积*/
   printf("a=%d\nb=%d\na+b=%d\na*b=%d\n",a,b,a+b,a*b);
   printf("s=%d\nt=%d\n",s,t);
   return 0;
}

 

例2.2 由两个指针变量求出用户输入整数的最大值和最小值

main()
{
    int a,b,c,*pmax,*pmin; /*pmax,pmin 为整型指针变量*/
    printf("input three numbers:\n"); /*输入提示*/
    scanf("%d%d%d",&a,&b,&c); /*输入三个数字*/
    if(a>b){ /*如果第一个数字大于第二个数字...*/
       pmax=&a; /*指针变量赋值*/
       pmin=&b;} /*指针变量赋值*/
   else{
      pmax=&b; /*指针变量赋值*/
      pmin=&a;} /*指针变量赋值*/
  if(c>*pmax) pmax=&c; /*判断并赋值*/
  if(c<*pmin) pmin=&c; /*判断并赋值*/
  printf("max=%d\nmin=%d\n",*pmax,*pmin); /*输出结果*/
}

 

 

 

除教程外,本网站大部分文章来自互联网,如果有内容冒犯到你,请联系我们删除!
评论区评论发表10个有质量的评论,可以联系我们 申请作者权限.

发表评论

Posted in C语言教程

Related Posts