C

7. C语言教程-指针

7. Pointer

Posted by xuepro on October 30, 2017

“教小白精通编程”系列之“C语言教程” (版权所有,不得转载,擅自抄袭转载将承担法律责任)

7. 指针

内存的地址: 内存是内存单元的依次排列,每个内存单元都有唯一的地址。如同宾馆的每个房间都有一个编号。

变量的指针(地址):变量占据内存的起始地址。

指针(地址)实际上可以看成是一个整数量。

#include <stdio.h>

void main(){
    int x = 3;
    printf("x=%d,\t the address is  %d",x &x);  /*其中的&是 “取地址运算符”,用于获得一个变量的地址*/
}

指针变量:存储指针(地址)的变量。

定义指针变量的格式:

类型* 变量名;

或者在定义指针变量时就给它一个地址值。 注意:必须是同一指针类型的值。

类型* 变量名 = 指针值;

例如,

#include <stdio.h>

void main(){
    int x = 3;
    int* px  = &x;   /*将变量i的地址用于初始化 “整型指针类型”的变量px, px 和 &x的类型都是同一个类型: “整型指针类型” */
    printf("x=%d,\t the address is  %d \t %d",x &x,px);  /*其中的&是 “取地址运算符”,用于获得一个变量的地址*/
}

如图所示:

因为px保存的是x的地址,我们通常称px指向x (也可以说px是x的指针)。

通过所谓的“取内容运算符”或“间接运算符” ’*‘

可以得到指针变量指向的那个变量,比如通过

   printf("%d",*px);     /* 其中的*px 就是 x */      

因此,我们可以通过px来这样修改x的值

 *px = 20;
 printf("*px = %d\n",*px); /* 20 */
 printf("x = %d\n",x); /* 20 */

注意

  1)  类型*  和 类型 是两个不同的类型。比如:
      int * : 也是 一个数据类型,称为整型指针类型
      int : 整型

因此,下面的代码是错误的:

    int x = 10;
    int* px = 20; /*错:两边的数据类型不一样,一个是"整型指针类型",一个是“整型” */
    x = &x;  /* 同样的错误*/

指针变量可以初始化为0,表示不指向任何内存,称为空指针。

  int x = 10;
  int* px = 0;  /*也可以用C语言定义好的宏常量NULL来初始化: int* px = NULL;  */
                /* C语言定义NULL就是0: #define NULL 0 */
  px = &x;  

指针的指针

指针变量也占据一块内存,它也有地址(指针)。称为“指针的指针”。

#include <stdio.h>

void main(){
   int x = 10;
   int *p = &x;   /* p 保存的是x的地址,类型是“整型指针类型”*/
   int ** q = &p;  /* q 保存的是p的地址,类型是“整型指针的指针类型”*/
   int y = *p; 
   int z = *(*q);
   printf("%d\t%d\t%d\t%d\t%d\n",x,&x,p,&p,q);
   printf("%d\t%d\t%d\t%d\t%d\t%d\n",q,*q,**q,*p,y,z);
}

数组就是指针

数组名是整个数组内存块的起始地址(第一个数组元素的地址)或者说指针。

int a[10];
int * pa;
pa = &a[0];  /*也可以写成:pa = a; 
             因为 a 和 &(a[0])都表示的是数组的起始地址
             (数组的第一个元素的地址) */

可以测试一下:

#include <stdio.h>

void main(){
   int arr[] = {1,2,3,4,5,6,7};
   int *p = arr;        /*数组名就是数组的起始地址*/
   int *q = &(arr[0]);  /*第一个元素的地址*/
   printf("%d\t%d\t%d\t%d\n",arr,&(arr[0]),p,q);
   printf("%d\t%d\t%d\t%d\n",arr[0],*arr,*p,*q); /* 用下标运算符[]得到一个数据元素
                                              或取内容运算符*得到整型指针指向的那个整形变量
                                                    */
}

下标访问”就是通过“指针偏移”实现的。

   /*   arr[2]实际就是通过指针arr向后偏移2个整型量,然后通过取内容运算符*
      得到偏移后的指针(arr+2)指向的那个整型量。
      即: arr[2]等价于 *(arr+2) */
      
#include <stdio.h>

void main(){
   int arr[] = {1,2,3,4,5,6,7};
   printf("%d\t%d\n",arr[2],*(arr+2));
   *(arr+2) = 30;
   printf("%d\t%d\n",arr[2],*(arr+2));
   arr[2] = 40;
   printf("%d\t%d\n",arr[2],*(arr+2));
   int y = arr[7];  /*错,下标越界了。  
                   7个元素的数组下标只能是:
                      0,1,2,3,4,5,6*/
}

数组作为函数的形参,实际传递的是数组的指针(数组名就是数组的指针(起始地址))。

/*求n个元素的数组arr中的最小值*/

int Min(int arr[],int n){
   int m,i;
   if(n<1) return 0; /*空的数组,就假装最小值为0吧*/
   m = arr[0];  /* 假设最小值开始为第一个元素的值*/ 
   for(i = 1 ; i<n ;i++)
      if(arr[i]<m)  /*发现更小的*/ 
         m = arr[i];
   return m;
}

上述函数的参数arr实际上是一个“整型指针变量”指向某整型数组的起始地址。因此,改参数完全可以定义为“ int * arr ”

/*求n个元素的数组arr中的最小值*/

int Min(int *arr,int n){
   int m,i;
   if(n<1) return 0; /*空的数组,就假装最小值为0吧*/
   m = arr[0];  /* 假设最小值开始为第一个元素的值*/ 
   for(i = 1 ; i<n ;i++)
      if(arr[i]<m)  /*发现更小的*/ 
         m = arr[i];
   return m;
}

然后可以在某个程序中调用这个函数,例如:

#include <stdio.h>

void main(){
   int a = {34,1,56,27,8,19};
   int arr = {3,19,56,7,48,23,71,2};
   printf("the min of a is %d\n",Min(a,6));
   printf("the min of arr is %d\n",Min(arr,8));
}

当然,上述的Min函数也可以写成下面的两种之一:

/*求n个元素的数组arr中的最小值*/

int Min(int *arr,int n){
   int m,i;
   if(n<1) return 0; /*空的数组,就假装最小值为0吧*/
   m = arr[0];  /* 假设最小值开始为第一个元素的值*/ 
   for(i = 1 ; i<n ;i++)
      if( *(arr+i) < m )  /*发现更小的*/ 
         m = *(arr+i);
   return m;
}

/*求n个元素的数组arr中的最小值*/

int Min(int *arr,int n){
   int *p = arr,*q = arr+n;
   if(n<1) return 0; /*空的数组,就假装最小值为0吧*/
   m = *p;  /* 假设最小值开始为第一个元素的值*/ 
   for(;p<q; p++)
      if( *p < m )  /*发现更小的*/ 
         m = *p;
   return m;
}

注意:对于一个指针,可以用加减一个整数即运算符+、-或者++、–运算符进行指针(地址)的偏移,以便指向不同的内存位置,存取不同的数据元素。也可以对两个指针变量用比较运算符如<来比较它们的大小。但指针不是整型量,不能进行其他如乘除、取模等运算。

向函数传递数组

可以定义一个能接受数组作为函数参数的函数,然后调用这个函数时,可以将一个实际的数组传递给它。

数组作为函数参数时,其定义方式有下列三种:

/*函数名param后跟一个空的[],说明param可以接受一个数组*/
void myFunction(int param[]) {
   /*函数体 代码*/ 
}
/*函数名param后跟一个非空的[size],说明param可以接受一个大小为size的数组*/
void myFunction(int param[]) {
   /*函数体 代码*/ 
}
/*函数名param前有一个*,说明param可以接受一个数组*/
void myFunction(int *param) {
   /*函数体 代码*/ 
}

上述3个函数中,实际上上面2个函数都会被编译器转化为第3个函数,也就是说“数组作为函数的形参”实际上是一个指针类型的形参,即将来可以接受一个数组名表示的数组的起始地址!而并不是将一个数组的所有元素传给一个函数,仅仅传递的是一个小小的指针变量。

比如,我们可定义下面的函数,第一个参数表示可以接受一个传入的数组

double getAverage(int arr[], int size) {

   int i;
   double avg;
   double sum = 0;

   for (i = 0; i < size; ++i) {
      sum += arr[i];
   }

   avg = sum / size;

   return avg;
}

然后我们可以调用这个数组:

#include <stdio.h>
 
/* 函数声明,表示存在这种格式的函数getAverage,
其第一个参数可以是一个int型数组,第二个参数是这个数组的大小 */
double getAverage(int arr[], int size);

int main () {

   /* 5个元素的int整型数组 */
   int balance[5] = {1000, 2, 3, 17, 50};
   double avg;

   /* 将数组名(实际是数组对应内存块的起始地址)传给getAverage函数 */
   avg = getAverage( balance, 5 ) ;
 
   /* 输出getAverage的返回值 */
   printf( "Average value is: %f ", avg );
    
   return 0;
}

也可以传递多维数组(如二维数组)给函数,在定义接受这种多维数组的函数参数时,和接受一维数组的函数参数一样,其第一维的大小也可以不指定。同样,如果我们省略第一维对应的方括号[],则要在参数名前加一个‘’。 并用圆括号括起来 “(参数名)”。

/*
 * File   : main.c
 * Author : zentut.com
 * Purpose: Demonstrates multidimensional array & function in C
 */
 
#include <stdio.h>

#include <stdlib.h>
 
const int ROW = 2;
const int COLUMN = 3;

/*这2个函数的定义在main函数的调用语句后面,所以要先声明(说明)函数的规范*/
void fill_array(int (*pm)[COLUMN],int row);
void display(int m[][COLUMN],int row);
 
int main()
{
    int i,j;
    int m[ROW][COLUMN];
 
    /* 修改数组元素的值 */
    fill_array(m,ROW);
 
    /* 显示数组的元素 */
    display(m,ROW);
 
    return 0;
}
 
void fill_array(int (*pm)[COLUMN],int row)
{
 
    int i,j;
    printf("Please fill the array's content:\n");
    /* 修改数组元素的值 */
    for(i = 0; i < row; i++)
    {
        for(j = 0; j < COLUMN; j++)
        {
            printf("\nm[%d][%d]:",i,j);
            scanf("%d",&pm[i][j]);
        }
    }
 
}
 
void display(int m[][COLUMN],int row)
{
    int i,j;
    /* 显示数组的元素 */
    printf("Array's content:\n");
 
    for(i = 0; i < row; i++)
    {
        for(j = 0; j < COLUMN; j++)
        {
            printf("%d\t",m[i][j]);
        }
        printf("\n");
    }
 
}

执行程序后的输出:

Please fill the array's content:
 
m[0][0]:1
 
m[0][1]:2
 
m[0][2]:3
 
m[1][0]:4
 
m[1][1]:5
 
m[1][2]:6
Array's content:
1       2       3
4       5       6

关于指针的更多内容(如函数指针)请参考: C语言快速入门之“数组、指针、内存分配”

练习

关注“教小白精通编程”博客微博“教小白精通编程”  


支付宝打赏 微信打赏

您的打赏是对我最大的鼓励!