C语言:指针入门--曹Sir教育园地

来源:百度文库 编辑:神马文学网 时间:2024/05/01 07:12:43
C语言:指针入门 [ 2009-5-21 15:14:00 | By: 曹金华 ]   6 推荐

一、 指针基础知识入门
1. 内存地址与指针
在计算机中,所有的数据都是存放在存储器中的。 一般把存储器中的一个字节称为一个内存单元, 不同的数据类型所占用的内存单元数不等,如整型量占2个单元,字符量占1个单元等。为了正确地访问这些内存单元, 必须为每个内存单元编上号。 根据一个内存单元的编号即可准确地找到该内存单元。内存单元的编号也叫做地址。 既然根据内存单元的编号或地址就可以找到所需的内存单元,所以通常也把这个地址称为指针。
内存单元的指针和内存单元的内容是两个不同的概念。对于一个内存单元来说,单元的地址即为指针, 其中存放的数据才是该单元的内容。在C语言中, 允许用一个变量来存放指针,这种变量称为指针变量。因此, 一个指针变量的值就是某个内存单元的地址或称为某内存单元的指针。
如我们在定义一个int a=5;时,内存将为a变量分配一个内存,假定分配了一个内存编号为2000的内存单元,那么表示的是在内存单元编号为2000的那个地方,存放着一个内容为5的变量值。只不过,我们平常用的时候,只关心5这个值,而不关心2000这个内存编号罢了,我们只需要知道定义一个变量,系统会给我们分配一定的内存空间,至于是哪个内存空间,就不需要计较罢了。而现在引入指针变量后,我们除了需要知道值,还可以去计较这个内存编号,用一个指针变量的保存这个内存地址。
2. 指针变量定义与赋值
对指针变量的类型说明包括三个内容:
(1)指针类型说明,即定义变量为一个指针变量;
(2)指针变量名;
(3)变量值(指针)所指向的变量的数据类型。
  其一般形式为: 类型说明符 *变量名;
  其中,*表示这是一个指针变量,变量名即为定义的指针变量名,类型说明符表示本指针变量所指向的变量的数据类型。
如:int *p; 表示定义了一个指针类型的变量,这个指针变量的值应该是可以用来存放整数型变量的内存地址的。
main()
{
   int *p;
   int a=5;
   p=&a;
   printf("%d %d",p,a);

程序说明:
先定义了一个指针型变量。
再定义了一个整型变量。
把整型变量的地址赋值给指针变量。
最后把这个地址和a变量的值输出。 
程序也可以这样写:
main()
{
   int a=5,*p=&a;
   printf("%d %d",p,a);
}
这样称为变量赋初值。
不允许把一个数赋予指针变量,故下面的赋值是错误的: int *p;p=1000; 被赋值的指针变量前不能再加“*”说明符,如写为*p=&a 也是错误的。
3. 指针变量运算
(1) & 取地址运算符
在以前我们所讲的scanf语句里面出现过&地址符,如
int a; 定义了一个整型变量,系统为这个整型变量分配一个内存地址。
scanf(“%d”,&a); 把读入的数据放在这个内存地址中。
printf(“a=%d”,a); 把这个变量的值(内存地址的值)输出。
而如果改用指针,程序可以改为这样:
int a,*p=a;
scanf(“%d”,p);
printf(“a=%d”,a);
(2) * 取(指针)内容运算符
我们也可以通过一定的操作把指针中保存的地址所保存的内容提取出来。
如:int a,*p=a;
scanf(“%d”,p);
printf(“a=%d”,*p);
注意第一行 int *p;中的*号仅仅是为了说明p变量是一个指针变量。
第三行的*p是指把指针变量p所保存的内存单元地址中所保存的那个值输出。
如:输入2,输出a=2.

需要注意的是,我们定义好一个指针变量后,原则上会跟普通变量相联系,如:
#i nclude "stdio.h"
void main()
{
   int *p;
   int a=5;
   printf("%d %d\n",p,a);
   printf("%d %d",*p,&a);
}   
需要注意的是,虽然定义了p指针变量,但是并没有把p=&a这样的赋值,所以只能输出
输出: 1245068 5
       4394640 1245048
第一行第一个数系统分配给p的一个地址(整数),第二个是a变量的值;
第二行是p指针中保存那个内存中地址的值,此处也是系统随机给的值;后一个是系统分给a变量的地址。
#i nclude "stdio.h"
void main()
{
   int *p;
   int a=5;
   p=&a;
   printf("%d %d\n",p,a);
   printf("%d %d",*p,&a);

输出: 1245048 5
       5 1245048
第一行第一个数是指针变量保存的那个内存地址,即系统分配给a变量的内存地址,第二个数是a变量的值。
第二行第一个数是p变量所保存的那个内存地址的值,即a的值。后一个数输出的是系统分配给a变量的内存地址。
即此时:p==&a  *p==a都是成立的。

二、 数组与指针
我们知道,定义一个数组时,系统将分配一个连续的内存地址,如:
int a[5]则会分配一段连续的空间分别用来表示a[0] a[1] a[2] a[3] a[4] a[5]的地址。
#i nclude "stdio.h"
void main()
{
   int a[5],i=0;
   for(i=0;i<5;i++)
     printf("%d ",&a[i]);
   printf("\n%d",a);

本例通过C/C++试验系统编译。
输出:
1245036 1245040 1245044 1245048 1245052
1245036
可以看出,for循环一次输出a数组中各项的地址,是连续的。本编译环境支持一个int变量占用四个字节。
最后输出的是数组名,因为数组名表示的是这个数组的首地址,即a==&a[0]是成立的。            
此时如果用指针来表示这个地址,直接保存这个数组的首地址就可以了。如
#i nclude "stdio.h"
void main()
{
   int a[5]={11,12,13,14,15},*p,i;
   for(i=0;i<5;i++)
     printf("%d ",a[i]);
   printf("\n");
   p=a;//或写成p=&a[0];
   printf("%d\n",*p);
   printf("%d\n",*(p+2));
   p=p+1;
   printf("%d\n",*p);

本例通过C/C++试验系统编译。

输出:
11 12 13 14 15  //for循环输出整个数组的值
11  //输出a[0]的值
13  //输出a[2]的值
12  //输出a[1]的值

程序功能:完成数组的输入输出.  
本例通过C/C++试验系统编译。
#i nclude "stdio.h"
void main()
{
   int a[3],i=0;
   //读入数据到数组中
   for(i=0;i<3;i++)
     scanf("%d",&a[i]);
   //输出数组数据
   for(i=0;i<3;i++)
     printf("%d ",a[i]);

#i nclude "stdio.h"
void main()
{
   int a[3],i=0;
   int *p=a;
   //读入数据到数组中
   for(i=0;i<3;i++) scanf("%d",p++);
   p=a; 
   //输出数组数据
   for(i=0;i<3;i++)
     printf("%d ",*p++);

#i nclude "stdio.h"
void main()
{
   int a[3],i=0;
   int *p=a;
   //读入数据到数组中
   for(i=0;i<3;i++,p++)
     scanf("%d",p);
   p=a; 
   //输出数组数据
   for(i=0;i<3;i++,p++)
     printf("%d ",*p);
}
注意p++的作用:当p目前保存的是&a[0],即p指向数组的第0号单元时,执行一次p++,可以使p指针指向数组的第1号单元,即此时p保存的是&a[1].
再分析一下下面一个程序的结果
#i nclude "stdio.h"
void main()
{
   int a[3],i=0;
   int *p=a,*q;
   q=p;
   //读入数据到数组中
   for(i=0;i<3;i++,p++)
     scanf("%d",p);
   p=a; 
   //输出数组数据
   for(p;p-q<3;p++)
     printf("%d ",*p);
   printf("\n");
   while(--p>=q)
     printf("%d ",*p);
} 随时注意p,q指针的变化

本例通过C/C++试验系统编译。
输入:1 2 3
输出:1 2 3
      3 2 1
  设有实数组a,指向a的指针变量为pa,可以得出有以下关系:int a[5],*pa=a; 则
pa,a,&a[0]均指向同一单元,它们是数组a的首地址,也是0 号元素a[0]的首地址。pa+1,a+1,&a[1]均指向1号元素a[1]。类推可知pa+i,a+i,&a[i]指向i号元素a[i]。
特别应该说明的是pa是变量,而a,&a[i]都是常量。即我们可以完成pa++或pa=pa+2,但是不能执行a=a+1这样的操作。

三、 函数与指针
函数中,我们强调了值传递和地址传递两种情况,考虑到指针是一个地址,如果实参使用的是指针类型,则属于地址传递的情况。如:
#i nclude "stdio.h"
void change(int *m,int *n)
{
    int temp;
    while(*m!=0 && *n!=0)
    {
      if(*m>*n)
      {
       temp=*m;*m=*n;*n=temp;
      } 
      m++;n++;
    }
}
void main()
{
   int a[6]={10,13,5,122,51,0},*p=a,i;
   int b[6]={1,43,6,17,99,0};  
   int *q=b;
   for(i=0;i<5;i++)
     printf("%d ",a[i]);
   printf("\n");
   for(i=0;i<5;i++)
     printf("%d ",b[i]);
   printf("\n");
   change(p,q);
   for(i=0;i<5;i++)
     printf("%d ",a[i]);
   printf("\n");
   for(i=0;i<5;i++)
     printf("%d ",b[i]);    

#i nclude "stdio.h"
void change(int *m,int *n)
{
    int temp;
    if(*m>*n)
    {
     temp=*m;*m=*n;*n=temp;
    } 
}
void main()
{
   int a[6]={10,13,5,122,51,0},*p=a,i;
   int b[6]={1,43,6,17,99,0};  
   int *q=b;
   for(i=0;i<5;i++)
     printf("%d ",a[i]);
   printf("\n");
   for(i=0;i<5;i++)
     printf("%d ",b[i]);
   printf("\n");
   for(i=0;i<6;i++)
    {
      change(p,q);
      p++;q++;
    }
   for(i=0;i<5;i++)
     printf("%d ",a[i]);
   printf("\n");
   for(i=0;i<5;i++)
     printf("%d ",b[i]);    
}

#i nclude "stdio.h"
void change(int *m,int *n,int k)
{
    int temp,i=0;
    while(i    {
      if(*m>*n)
      {
       temp=*m;*m=*n;*n=temp;
      } 
      m++;n++;
      i++;
    }
}
void main()
{
   int a[6]={10,13,5,122,51,0},*p=a,i;
   int b[6]={1,43,6,17,99,0};  
   int *q=b;
   for(i=0;i<5;i++)
     printf("%d ",a[i]);
   printf("\n");
   for(i=0;i<5;i++)
     printf("%d ",b[i]);
   printf("\n");
   change(p,q,6);
   for(i=0;i<5;i++)
     printf("%d ",a[i]);
   printf("\n");
   for(i=0;i<5;i++)
     printf("%d ",b[i]);    
}

三个程序相同结果,输出
10 13 5 122 51
1 43 6 17 99
1 13 5 17 51
10 43 6 122 99