8-指针

 

1、概念

在C语言中,指针是一种特殊的变量,它是存放地址的。假设我们定义了一个指针变int *i_pointer。将i的地址(2000)存放到i_pointer中。这时, i_pointer的值就是(2000) ,即变量i所占用单元的起始地址。要存取变量i的值,可以采用间接方式:先找到存放“i的地址”的变量i_pointer ,从中取出i的地址(2000),然后取出i的值3。用来存放整型变量 i 的地址。可以通过语句:i_pointer =&i;

*:这玩意叫做取值操作符

&:而这玩意叫做取址操作符

#include<stdio.h>

int main()
{
	int i = 2000;
	int *pointer;
	pointer = &i;
	printf("%d\n", *pointer);

	system("PAUSE");
	return 0;
}

2、指针与指针变量

知道了一个变量的地址,就可以通过这个地址来访问这个变量,因此,又把变量的地址称为该变量的“指针” 。

C语言中可以定义一类特殊的变量,这些变量专门用来存放变量的地址,称为指针变量

注意:指针变量的值(即指针变量中存放的值)是地址(即指针)。请区分“指针”和“指针变量”这两个概念。

注意:

  1. 指针变量前面的“*”,表示该变量的类型为指针型变量。

其一般形式为:类型说明符  *变量名;

其中,*表示这是一个指针变量,变量名即为定义的指针变量名,类型说明符表示本指针变量所指向的变量的数据类型。

(2)在定义指针变量时必须指定基类型。

需要特别注意的是,只有整型变量的地址才能放到指向整型变量的指针变量中。下面的赋值是错误的∶

float  a;

int  * pointer_1;

pointer_1=&a;       /*将float型变量的地址放到指向整型变量的指针变量中,错误 */

C语言中提供了地址运算符&来表示变量的地址。其一般形式为: &变量名;

#include<stdio.h>

int main()
{
	int a,b;
	int *pointer_1, *pointer_2;
	a=100; b=10;
	pointer_1 = &a;
	pointer_2 = &b;

	printf("%d,%d\n",a,b);
	printf("%d,%d\n",*pointer_1, *pointer_2);

	system("PAUSE");
	return 0;
}

说明:

(1)如果已执行了语句   pointer_1=&a;&* pointer_1的含义是什么?

“&”和“*”两个运算符的优先级别相同,但按自右而左方向结合,因此先进行* pointer_1的运算,它就是变量a,再执行&运算。因此,&* pointer_1与&a相同,即变量a的地址

(2)如果有:pointer_2 =&* pointer_1;

它的作用是将&a(a的地址)赋给pointer_2 ,如果 pointer_2 原来指向b,经过重新赋值后它已不再指向b了,而指向了a。

(3)*&a的含义是什么?

先进行&a运算,得a的地址,再进行*运算。即&a所指向的变量,也就是变量a。

*&a和*pointer_1的作用是一样的,它们都等价于变量a。即*&a与a等价。

(4)(*pointer_1)++相当于a++。

注意括号是必要的,如果没有括号,就成为了*pointer_1++,从附录可知:++和*为同一优先级别,而结合方向为自右而左,因此它相当于*(pointer_1++)。

由于++在pointer_1的右侧,是“后加”,因此先对pointer_1的原值进行*运算,得到a的值,然后使pointer_1的值改变,这样pointer_1不再指向a了。

#include<stdio.h>

int main()
{
	int *p1, *p2, *p, a, b;
	scanf("%d %d", &a, &b);
	p1 = &a;
	p2 = &b;  
	if( a < b)     
	{
		p = p1;
		p1 = p2;
		p2 = p;
	} //此后,p1指向b, p2指向a ^_^   
	printf("a = %d, b = %d\n", a, b);
	printf("max = %d, min = %d\n", *p1, *p2);

	system("PAUSE");
	return 0;
}

3、指针变量作为函数参数

对输入的两个整数按大小顺序输出!这次用函数实现交换功能!

#include <stdio.h>
void swap(int *p1, int *p2);

void main()
{ 
	int a, b;
	int *pointer_1, *pointer_2;
      
	scanf("%d %d", &a, &b);
      
	pointer_1 = &a;
	pointer_2 = &b;
      
	if(a < b) 
	{
		swap(pointer_1, pointer_2); //swap实现的是交换……
	}
      
	printf("\n%d > %d\n", a, b);

	system("PAUSE");
	return 0;
}

void swap(int *p1, int *p2)
{
      int temp;

      printf("I'm swapping……\n");
      printf("Please wait^_^");
      
      temp = *p1;  //temp = a;
      *p1 = *p2;   //a = b;
      *p2 = temp;  //b = temp; 
}

输入a、b、c 3个整数,按大小顺序输出

#include <stdio.h>

void main()
{
      void  exchange(int *q1, int *q2, int *q3);

      int a, b, c, *p1, *p2, *p3;

      scanf("%d %d %d", &a, &b, &c);
      p1 = &a;
      p2 = &b; 
      p3 = &c;

      exchange(p1, p2, p3);  //确保a > b > c
      printf("%d %d %d\n", a, b, c);

	system("PAUSE");
	return 0;
}


void  exchange(int *q1, int *q2, int *q3) //int *q1 = p1;
{
      void swap(int *pt1, int *pt2); //用于交换&……&%

      if( *q1 < *q2 )
      {
            swap(q1, q2);
      }
      if( *q1 < *q3 )
      {
            swap(q1, q3);
      }
      if( *q2 < *q3 )
      {
            swap(q2, q3);
      }
}
 
void  swap(int *pt1, int *pt2)
{
      int temp;

      temp = *pt1;
      *pt1 = *pt2;
      *pt2 = temp;
}

4、数组指针

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

指针变量既然可以指向变量,当然也可以指向数组元素(把某一元素的地址放到一个指针变量中)。所谓数组元素的指针就是数组元素的地址

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

例如:

int a[10];

 (定义a为包含10个整型数据的数组)

int*p;   

 (定义p为指向整型变量的指针变量)

应当注意,如果数组为int型,则指针变量的基类型亦应为int型。

p=&a[0];

把a[0]元素的地址赋给指针变量p。

引用一个数组元素,可以用:

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

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

其中的a是数组名,p是指向数组元素的指针变量,其初值 p=a。注意:数组名即“翻译成数组的第一个元素的地址!

5、数组名作函数参数

 void f(int arr[ ],int n)

     {

         ……… ………

     }

           void main()

     {

         int array[10];

         …… ……

                f(array, 10);

     }

f (int arr[ ], int n)

但在编译时是将arr按指针变量处理的,相当于将函数f的首部写成  f (int *arr, int n)

以上两种写法是等价的。

需要说明的是:C语言调用函数时虚实结合的方法都是采用“值传递”方式,当用变量名作为函数参数时传递的是变量的值,当用数组名作为函数参数时,由于数组名代表的是数组首元素地址,因此传递的值是地址,所以要求形参为指针变量。

实例:将数组a中n个整数按相反顺序存放

方法一:数组名作函数

#include <stdio.h>

void reverse(int x[],int n);   /*形参x是数组名*/

void main()
{
      int i, a[10] = {3, 7, 9, 11, 0, 6, 7, 5, 4, 2};

      printf("The original array:\n");

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

      reverse(a, 10);  //作用使得数组重新倒序排放

      printf("The array has been inverted:\n");

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

	system("PAUSE");
	return 0;
}

void reverse(int x[], int n)   /*形参x是数组名*/
{
      int temp, i, j, m;

      m = (n-1)/2;    
      
      for( i=0; i <= m; i++)
      {
            j = n-1-i;  //j指向对应的元素

            temp = x[i];
            x[i] = x[j];
            x[j] = temp;
      }
   
}

方法二:指针作函数

#include <stdio.h>

void reserve(int *x, int n);   /*形参x为指针变量*/

void main()
{
      int i, a[10] = {3, 7, 9, 11, 0, 6, 7, 5, 4, 2};

      printf("The original array:\n");

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

      reserve(a, 10);

      printf("The array has benn inverted:\n");

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

	  
	system("PAUSE");
	return 0;
}

void reserve(int *x, int n)   /*形参x为指针变量*/
{
      int *p, temp, *i, *j, m;

      m = (n-1)/2;
      i = x;         //i指向数组的第一个元素
      j = x-1+n;     //j指向的是数组的最后一个元素
      p = x+m;       //指向中间,配对……
      
      for( ; i <= p; i++, j--)
      {
            temp = *i;
            *i = *j;
            *j = temp;
      }
}

归纳起来,如果有一个实参数组,想在函数中改变此数组中的元素的值,实参与形参的对应关系有以下4种情况:

(1)形参和实参都用数组名

(2)实参用数组名,形参用指针变量

(3)实参形参都用指针变量。

(4)实参为指针变量,形参为数组名。

6、多维数组与指针

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

则二维数组a是由3个一维数组所组成的。设二维数组的首行的首地址为2000 

第一行地址a[0]

2000  

a[0][0]

2004

a[0][1]

2008

a[0][2]

2012

a[0][3]

存放数据

1

3

5

7

第二行地址a[1]

2016

a[1][0]

2020

a[1][1]

2024

a[1][2]

2028

a[1][3]

存放数据

9

11

13

15

第三行地址a[2]

2032

a[2][0]

2036

a[2][1]

2040

a[2][2]

2044

a[2][3]

存放数据

17

19

21

23

含义

a

二维数组名,指向一维数组a[0],即0行首地址

2000

a[0],

*(a+0),

*a

0行0列元素地址

2000

a+1,&a[1]

1行首地址

2016

a[1],*(a+1)

1行0列元素a[1][0]的地址

2016

a[1]+2,

*(a+1)+2,

&a[1][2]

1行2列元素a[1][2] 的地址

2024

*(a[1]+2),

*(*(a+1)+2),

a[1][2]

1行2列元素a[1][2]的值

元素值为13

把二维数组a分解为一维数组a[0],a[1],a[2]之后,设p为指向二维数组的指针变量。可定义为:  int (*p)[4]

它表示p是一个指针变量,它指向包含4个元素的一维数组。若指向第一个一维数组a[0],其值等于a,a[0],或&a[0][0]等。而p+i则指向一维数组a[i]。

从前面的分析可得出*(p+i)+j是二维数组i行j 列的元素的地址,而*(*(p+i)+j)则是i行j列元素的值。

二维数组指针变量说明的一般形式为:类型说明符  (*指针变量名)[长度]

其中“类型说明符”为所指数组的数据类型。“*”表示其后的变量是指针类型。“长度”表示二维数组分解为多个一维数组时,一维数组的长度,也就是二维数组的列数。

7、字符串与指针

(1) 用字符数组存放一个字符串,然后输出该字符串。

(2) 用字符指针指向一个字符串

8、指向函数的指针

对使用字符指针变量和字符数组的讨论

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

(2)赋值方式。对字符数组只能对各个元素赋值,不能用以下办法对字符数组赋值。

     char  str[20];

     str=″I love Fishc.com!″;

 

  而对字符指针变量,可以采用下面方法赋值:

     char*a;

     a=″I love Fishc.com!″;

  但注意赋给a的不是字符,而是字符串第一个 元素的地址。

(3)对字符指针变量赋初值:

    char *a=″I love Fishc.com!″;

       等价于

           char*a;

    a=″I love Fishc.com!″;

    而对数组的初始化:

    char str[20]={″I love Fishc.com!″};

    不能等价于

    char str[20];

    str[ ]=″I love Fishc.com!″;

(4)如果定义了一个字符数组,在编译时为它分配内存单元,它有确定的地址。而定义一个字符指针变量时,给指针变量分配内存单元,在其中可以放一个字符变量的地址也就是说,该指针变量可以指向一个字符型数据,但如果未对它赋予一个地址值,则它并未具体指向一个确定的字符数据。 

(5) 指针变量的值是可以改变的,如:

         改变指针变量的值

    另外需要说明的是,若定义了一个指针变量,并使它指向一个字符串,就可以用下标形式引用指针变量所指的字符串中的字符。

        下标形式引用指针变量

9、指针与函数

可以用指针变量指向整型变量、字符串、数组,也可以指向一个函数。一个函数在编译时被分配给一个入口地址。这个函数的入口地址就称为函数的指针。

(1)函数指针变量常用的用途之一是把指针作为参数传递到其他函数。

(2)前面介绍过,函数的参数可以是变量、指向变量的指针变量、数组名、指向数组的指针变量等。

(3)现在介绍指向函数的指针也可以作为参数,以实现函数地址的传递,这样就能够在被调用的函数中使用实参函数。

#include <stdio.h>

void main()
{
      int max(int, int);            /* 函数声明 */
      int min(int, int);            /* 函数声明 */
      int add(int, int);            /* 函数声明 */
    
      void process( int, int, int(*fun)() );    /* 函数声明 */
      
      int a, b;

      printf("Endter a and b: ");
      scanf("%d %d", &a, &b);
      
      printf("max = ");
      process(a, b, max);

      printf("min = ");
      process(a, b, min);

      printf("sum = ");
      process(a, b, add);

	  system("pause");
	  return 0;
}

int max(int x, int y)              /* 函数定义 */
{
      int z;
      
      if( x > y )
      {
            z = x;
      }
      else
      {
            z = y;
      }

      return z;
}

int min(int x, int y)              /* 函数定义 */
{
      int z;

      if( x < y )
      {
            z = x;
      }
      else
      {
            z = y;
      }

      return z;
}

int add(int x, int y)
{
      int z;
      
      z = x + y;
      return z;
}

void process( int x, int y, int(*fun)() )    /* 函数定义 */ 
{
      int result;

      result = (*fun)(x, y);
      printf("%d\n", result);
}

一个函数可以带回一个整型值、字符值、实型值等,也可以带回指针型的数据,即地址。其概念与以前类似,只是带回的值的类型是指针类型而已。

这种带回指针值的函数,一般定义形式为

 类型名  *函数名(参数表列);

例如:int *a(int x,int y);

#include <stdio.h>

void main()
{
      double score[][4] = {{60.0, 70.0, 80.5, 90.5}, {56.0, 89.0, 67.0, 88.0}, {34.2, 78.5, 90.5, 66.0}};
      double *search(double(*pointer)[4], int n);
      double *p;
      int i, m;

      printf("Please enter the number of student: ");
      scanf("%d", &m);

      printf("The scores of No.%d are: \n", m);

      p = search(score, m);

      for( i=0; i < 4; i++)
      {
            printf("%5.2f\t", *(p + i));
      }

      printf("\n\n\n");
	  
	  system("pause");
	  return 0;
}

double *search(double (*pointer)[4], int n)
{
      double *pt;

      pt = *(pointer + n);

      return pt;
}

10、指针函数和函数指针的区别

(1)指针函数是指带指针的函数,即本质是一个函数。

(2)函数指针是指向函数的指针变量,因而函数指针本身首先应是指针变量,只不过该指针变量指向函数。

11、指针数组

指针数组的概念:一个数组,若其元素均为指针类型数据,称为指针数组,也就是说,指针数组中的每一个元素都相当于一个指针变量。一维指针数组的定义形式为

类型名数组名[数组长度];

例如:int*name[4];

#include <stdio.h>

void main()
{
      int a[5] = {1, 3, 5, 7, 9};
      int *name[5] = {&a[0], &a[1], &a[2], &a[3], &a[4]};
      int i;

      for( i=0; i < 5; i++ )
      {
            printf("%d   ", *name[i]);
      }
      printf("\n\n");

	  system("pause");
	  return 0;
}

指向指针的指针:

怎样定义一个指向指针数据的指针变量呢?

形式可以如: char**p;

p的前面有两个*号。*运算符的结合性是从右到左,因此**p相当于*(*p),显然*p是指针变量的定义形式。如果没有最前面的*,那就是定义了一个指向字符数据的指针变量。

现在它前面又有一个*号,表示指针变量p是指向一个字符指针变量的。*p就是p所指向的另一个指针变量。

12、指针数组作main函数的形参

指针数组的一个重要应用是作为main函数的形参。在以往的程序中,main函数的第一行一般写成以下形式:

    1. void  main()

括弧中是空的。实际上,main函数可以有参数。

例如:void  main(int  argc,char  *argv[ ])

argc和argv就是main函数的形参。

main函数是由操作系统调用的。那么,main函数的形参的值从何处得到呢?

显然不可能在程序中得到。实际上实参是和命令一起给出的。也就是在一个命令行中包括命令名和需要传给main函数的参数。

命令行的一般形式为:

命令名 参数1 参数2……参数n

#include <stdio.h>

void main(int argc, char *argv[])
{
      while( argc > 1 )  
      {
            ++argv;
            printf("%s\n", argv);
            --argc;
      }

	system("pause");
	return 0;
}

13、小结

定义

含义

int i;

定义整型变量i

int *p;

p为指向整型数据的指针变量

int a[n];

定义整型数组a,它有n个元素

int *p[n];

定义指针数组p,它由n个指向整型数据的指针元素组成

int (*p)[n];

p为指向含n个元素的一维数组的指针变量

int f();

f为带回整型函数值的函数

int *p();

p为带回一个指针的函数,该指针指向整型数据

int (*p)();

p为指向函数的指针,该函数返回一个整型值

int **p;

p是一个指针变量,它指向一个指向整型数据的指针变量

(1)指针变量加(减)一个整数

例如:p++、p--、p+i、p-i、p+=i、p-=i等。

指针变量赋值

将一个变量地址赋给一个指针变量。如:

p=&a;  (将变量a的地址赋给p)

p=array; (将数组array首元素地址赋给p)

p=&array[i];(将数组array第i个元素                                的地址赋给p)

p=max;(max为已定义的函数,将ma                x的入口地址赋给p)

p1=p2;(p1和p2都是指针变量,将                     p2的值赋给p1)

(2)指针变量可以有空值,即该指针变量不指向任何变量,可以这样表示:p=NULL;

两个指针变量可以相减

如果两个指针变量都指向同一个数组中的元素,则两个指针变量值之差是两个指针之间的元素个数

若两个指针指向同一个数组的元素,则可以进行比较。指向前面的元素的指针变量“小于”指向后面元素的指针变量。

 

14、Void指针和const指针

void真正发挥的作用在于:

(1) 对函数返回的限定;

(2) 对函数参数的限定。

ANSI C新标准增加了一种“void”指针类型,即不指定它是指向哪一种类型数据的指针变量。

例如:void *p;

表示指针变量p不指向一个确定的类型数据,它的作用仅仅是用来存放一个地址。

void指针它可以指向任何类型数据。也就是说,可以用任何类型的指针直接给void指针赋值。但是,如果需要将void指针的值赋给其他类型的指针,则需要进行强制类型转换。

#include <stdio.h>

void main(int argc, char *argv[])
{
     const char *str= "Welcome to Fishc.com!\n\n";
      // 这个语句的含义是:声明一个名为str的指针变量,
      // 它指向一个字符型常量,初始化str为指向字符串
      // "Welcome to Fishc.com!\n\n"
      printf("\n\n%s", str);
#if (1)
      str[0] = 'w';       //这条语句是错误的,但可以改变str指针的值 
#endif

      str = "I love Fishc.com!\n\n";   //合法!

      printf("\n\n%s", str);

	system("pause");
	return 0;
}

#include <stdio.h>

void main(void)
{
      char * const str = "Welcome to Fishc.com!\n\n";
      // 常量指针是一个固定的指针,不可以改变它的值,但它所指的数据可以改变。


      str[0] = 'w';       //合法!

#if( 1 )
      str = "I love Fishc.com!\n\n";   //非法!!
#endif

      printf("\n\n%s", str);

	  system("pause");
	  return 0;
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值