C语言入门之指针(2)

本文围绕C语言中数组元素的指针展开,介绍了引用数组元素的三种方法及比较,阐述了数组指针和指针数组的定义、赋值及区别。还讲解了字符串的两种引用方式,以及字符指针作函数参数实现字符串复制,对比了字符指针变量和字符数组的差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 数组元素的指针

  1. 一个变量有地址,一个数组包含若干元素,每个数组元素都有相应的地址
  2. 指针变量可以指向数组元素(把某一元素的地址放到一个指针变量中)
  3. 所谓数组元素的指针就是数组元素的地址

  • 在引用数组元素时指针的运算

在指针指向数组元素时,允许以下运算:

  1. 加一个整数(++=),如p=p+1
  2. 减一个整数(--=),如p=p-1
  3. 自加运算,如p++++p
  4. 自减运算,如p----p
  5. 两个指针相减,如p1-p2 (只有p1p2都指向同一数组中的元素时才有意义)

  • 通过指针引用数组元素

引用一个数组元素,可用下面两种方法:

           (1) 下标法,如a[i]形式

   (2) 指针法,如*(a+i)*(p+i)

  其中a是数组名,p是指向数组元素的指针变量,其初值p=a

例: 有一个整型数组a,有10个元素,要求输出数组中的全部元素。

解题思路:引用数组中各元素的值有3种方法:

(1)下标法

(2)通过数组名计算数组元素地址,找出元素的值

(3) 用指针变量指向数组元素

分别写出程序,以资比较分析。

下标法

#include <stdio.h>
int main()
{ 
   int a[10];  
   int i;
   printf(“enter 10 integer numbers:\n");
   for(i=0;i<10;i++)
     scanf("%d",&a[i]);
   for(i=0;i<10;i++)  
     printf(“%d ”,a[i]); 
   printf("%\n");
   return 0;
 }

 通过数组名计算数组元素地址,找出元素的值

#include <stdio.h>
int main()
{ 
   int a[10];  
   int i;
   printf(“enter 10 integer numbers:\n");
   for(i=0;i<10;i++)  
     scanf("%d",a+i);
   for(i=0;i<10;i++)
     printf(“%d ”,*(a+i)); 
   printf("\n");
   return 0;
 }

用指针变量指向数组元素

#include <stdio.h>
int main()
{ 
   int a[10];  
   int *p,i;
   printf(“enter 10 integer numbers:\n");
   for(i=0;i<10;i++)  
     scanf("%d",&a[i]);
   for(p=a;p<(a+10);p++)
     printf(“%d ”,*p); 
   printf("\n");
   return 0;
}

3种方法的比较:

① 第(1)和第(2)种方法执行效率相同

              C编译系统是将a[i]转换为*(a+i)处理的,即先计算元素地址。

               因此用第(1)和第(2)种方法找数组元素费时较多。

② 第(3)种方法比第(1)、第(2)种方法快

             用指针变量直接指向元素,不必每次都重新计算地址,像p++这样的自加操作是比较快的

             这种有规律地改变地址值(p++)能大大提高执行效率

③ 用下标法比较直观,能直接知道是第几个元素。

              用地址法或指针变量的方法不直观,难以很快地判断出当前处理的是哪一个元素。

用数组名作函数参数

  1.    用数组名作函数参数时,因为实参数组名代表该数组首元素的地址形参应该是一个指针变量
  2. C编译都是将形参数组名作为指针变量来处理的

  •  通过指针引用多维数组

指针变量可以指向一维数组中的元素,也可以指向多维数组中的元素。

例: 二维数组的有关数据(地址和值)

int a[3][4]={1,3,5,7,9,11,13,15,                17,19,21,23};

分析下例情况:

  1. a,*a, a+1,*a+1
  2. a[0],*(a+0), a[0]+1,*(a+0)+1
  3. &a[0],&a[0][0], &a[0]+1,&a[0][0]+1
  4. a[1],a+1, a[1]+1,(a+1)+1
  5. &a[1][0],*(a+1)+0, a[1][0]+1,*(a+1)+0+1
  6. a[2],*(a+2), a[2]+1,*(a+2)+1
  7. &a[2],a+2, &a[2]-1,a+2-1
  8. a[1][0],*(*(a+1)+0), a[1][0+1],*(*(a+1)+0+1)
  9.  *a[2],*(*(a+2)+0), *(a[2]+1),*(*(a+2)+0+1)
  • 数组指针

数组指针(也称行指针)

数组指针变量的定义

类型  (*数组指针变量名)[常量表达式];

例如:int n= 4;

         int (*p)[n]; 

()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度

如要将二维数组赋给一指针,应这样赋值:
int a[3][4];
int (*p)[4]; //
该语句是定义一个数组指针,指向含4个元素的一维数组。
 
p=a;        //将该二维数组的首地址赋给p,也就是a[0]&a[0][0]
 p++;       //
该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]

所以数组指针也称指向一维数组的指针,亦称行指针。

  • 指针数组

指针数组变量的定义

类型  *指针数组变量名[常量表达式];

例如:int n= 4; int a[3][4];

         int *p[n];

[]优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1时,则p指向下一个数组元素,这样赋值是错误的:p=a;因为p是个不可知的表示,只存在p[0]p[1]p[2]...p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 *p=a; 这里*p表示指针数组第一个元素的值,a的首地址的值。

如要将二维数组赋给指针数组:

Øint *p[3];int a[3][4];
p++; //
该语句表示p数组指向下一个数组元素。注:此数组每一个元素都是一个指针
for(i=0;i<3;i++) p[i]=a[i]
这里int *p[3] 表示一个一维数组内存放着三个指针变量,分别是p[0]p[1]p[2]所以要分别赋值。

数组指针和指针数组的区别:数组指针只是一个指针变量,似乎是C语言里专门用来指向二维数组的,它占有内存中一个指针的存储空间。指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间。
还需要说明的一点就是,同时用来指向二维数组时,其引用和用数组名引用都是一样的。
比如要表示数组中ij列一个元素:
*
(p[i]+j)、*(*(p+i)+j)(*(p+i))[j]p[i][j]

#include <stdio.h>
int main(void)
{
 int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
 int (*p1)[4] = a; //定义数组指针p1,指向一个含有4个
			  //元素的一维数组,现指向二维数组
                            //a的第0行的一维数组的首地址
 for(int i=0;i<3;i++)	
   for(int j=0;j<4;j++)		
    printf("%d,",p1[i][j]);
 printf("\n");
 return 0;
}
#include <stdio.h>
int main(void)
{    
   int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
   int *p2[3]; //定义指针数组p2,数组长度为3,可以保
		//存3个指针(地址)元素
   p2[0] = a[0];      //数组p2的第0个指针元素保存a[0]的地址
   p2[1] = a[1];   //数组p2的第1个指针元素保存a[1]的地址
   p2[2] = a[2]; //数组p2的第2个指针元素保存a[2]的地址
   for(int i=0;i<3;i++)	
    for(int j=0;j<4;j++)		
      printf("%d,",p2[i][j]);
   printf("\n");
  return 0;
}

& 符号的意思是取地址,也就是返回一个对象在内存中的地址。* 符号的意思是取得一个指针所指向的对象。 也就是如果一个指针保存着一个内存地址,那么它就返回在那个地址的对象。所以当你这么写时 *ipp = ip2,实际上是把 ipp 存的地址所对应的对象,也就是 ip1 取到,然后把 ip2 存的值赋值给 ip1,也就是 j 的地址。简单点就是:&:取址。* :取值。

有一个指针数组,其元素分别指向一个整型数组的元素,用指向指针的指针变量,输出整型数组各元素的值。

  • 字符串的引用方式

字符串是存放在字符数组中的。引用一个字符串,可以用以下两种方法。

(1) 用字符数组存放一个字符串,可以通过数组名和格式声明“%s”输出该字符串,也可以通过数组名和下标引用字符串中一个字符。

                 (2) 用字符指针变量指向一个字符串常量,通过字符指变量引用字符串常量。

例: 定义一个字符数组,在其中存放字符串“I love China!”,输出该字符串和第8个字符。

解题思路:

  1. 定义字符数组string,对它初始化,由于在初始化时字符的个数是确定的,因此可不必指定数组的长度。
  2. 用数组名string和输出格式%s可以输出整个字符串。
  3. 用数组名和下标可以引用任一数组元素。(方法1
  4. string改成字符指针变量的形式定义(方法2

  • 字符指针作函数参数

  1. 如果想把一个字符串从一个函数“传递”到另一个函数,可以用地址传递的办法,即用字符数组名作参数,也可以用字符指针变量作参数。
  2. 在被调用的函数中可以改变字符串的内容
  3. 在主调函数中可以引用改变后的字符串。

例:用函数调用实现字符串的复制。

解题思路:

定义一个函数copy_string用来实现字符串复制的功能,在主函数中调用此函数,函数的形参和实参可以分别用字符数组名或字符指针变量。分别编程,以供分析比较。

(1) 用字符数组名作为函数参数

#include <stdio.h>
void copy_string(char from[],char to[]);
int main()
{
  char a[]="I am a teacher.";
  char b[]="you are a student.";
  printf(“a=%s\nb=%s\n",a,b);
  printf("copy string a to string b:\n");
  copy_string(a,b); 
  printf(“a=%s\nb=%s\n",a,b);  
  return 0;
}

void copy_string(char from[], char to[])  
{ 
   int i=0;
   while(from[i]!='\0')
   {   
     to[i]=from[i];
     i++;
   }
   to[i]='\0';
}

(2)用字符型指针变量作实参

copy_string不变,在main函数中定义字符指针变量fromto,分别指向两个字符数组a,b

仅需要修改主函数代码 

#include <stdio.h>
void copy_string(char from[], char to[]);
int main()
{ 
  char a[]=“I am a teacher.”; 
  char b[]=“you are a student.”; 
  char *from=a,*to=b;  
  printf(“a=%s\nb=%s\n",a,b);
  printf("\ncopy string a to string b:\n");
  copy_string(from,to); 
  printf(“a=%s\nb=%s\n",a,b);  
  return 0;
}

void copy_string(char from[], char to[])  
{ 
   int i=0;
   while(from[i]!='\0')
   {  
      to[i]=from[i];
      i++;
   }
   to[i]='\0';
}

(3)用字符指针变量作形参和实参

#include <stdio.h>
void copy_string(char *from, char *to);
int main()
{
  char *a=“I am a teacher.”; 
  char b[]=“You are a student.”; 
  char *p=b; 
  printf(“a=%s\nb=%s\n”,a,b); 
  printf("\ncopy string a to string b:\n");
  copy_string(a,p); 
  printf(“a=%s\nb=%s\n”,a,b); 
  return 0;
}

void copy_string(char *from, char *to) 
{ 
   for(  ;*from!='\0'; from++,to++)
   {  
        *to=*from; 
   }
   *to='\0';
} 

使用字符指针变量和字符数组的比较

用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈,主要有以下几点。

(1) 字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串第1个字符的地址),决不是将字符串放到字符指针变量中。

(7) 引用数组元数

  对字符数组可以用下标法地址法引用数组元素a[5],*(a+5)。如果字符指针变量p=a,则可以用指针变量带下标的形式地址法引用p[5],*(p+5)

char *a=I love China!;

a[5]的值是第6个字符,即字母’e’

(8) 用指针变量指向一个格式字符串,可以用它代替printf函数中的格式字符串。

char *format;

format=”a=%d,b=%f\n”;

printf(format,a,b);

相当于

                 printf(“a=%d,b=%f\n”,a,b);

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

仙剑情缘

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

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

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

打赏作者

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

抵扣说明:

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

余额充值