C语言基础学习day08

指针

地址和指针的概念

内存区的每一个字节有一个编号,这就是“地址” 。如果在程序中定义了一个变量,在对程序进行编译时,系统就会给这个变量分配内存单元。

在C语言中,对变量的访问有两种方式,直接访问和间接访问。

 

关于直接访问和间接访问

直接访问如:a=5; 系统在编译时,已经对变量分配了地址,例如,若变量a分配的地址是2000,则该语句的作用就是把常数5保存到地址为2000的单元。

间接访问如:scanf("%d",&a); 调用函数时,把变量a的地址传递给函数scanf,函数首先把该地址保存到一个单元中,然后把从键盘接收的数据通过所存储的地址保存到a变量中。

 

初识指针

在C语言中,指针是一种特殊的变量,它是存放地址的。假设我们定义了一个指针变量 int *i_pointer

用来存放整型变量 i 的地址。 可以通过语句:i_pointer =&i;

 

将i的地址(2000)存放到i_pointer中。这时, i_pointer的值就是(2000) ,即变量i所占用单元的起始地址。

要存取变量i的值,可以采用间接方式:先找到存放“i的地址”的变量i_pointer ,从中取出i的地址(2000),然后取出i的值3。

 

初识两个操作符“*”和“&”

*:叫做取值操作符

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

#include<stdio.h>
void main()
{
       int i = 2000;

       int *pointer;

       pointer = &i;

       printf("%d\n", pointer);  //打印地址6487572
 } 
#include<stdio.h>
void main()
{
       int i = 2000;

       int *pointer;

       pointer = &i;

       printf("%d\n", *pointer);//打印这个地址所储存的值2000
 } 

 

指针和指针变量

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

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

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

定义指针变量时要注意两点

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

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

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

       例如: float *pointer_1; 指针变量名是pointer_1 ,而不是* pointer_1 

二、在定义指针变量时必须指定基类型。(指针指向变量的类型)

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

       float a;

       int * pointer_1;

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

 

指针变量的引用 “&”

请牢记,指针变量中只能存放地址(指针), 不要将一个整数(或任何其他非地址类型的数据)赋给一个指针变量,否则编译器也会把该值当成一个地址来处理。

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

 &变量名;

如&a表示变量a的地址,&b表示变量b的地址。当然,变量本身必须预先声明。

#include<stdio.h>
void 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);
}

 

对“&”和“*”运算符再做些说明

如果已执行了语句 pointer_1=&a;

(1)&* pointer_1的含义是什么?

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

     如果有: pointer_2 =&* pointer_1;

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

     

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

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

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

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

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

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

 

例题:输入a和b两个整数,按先大后小的顺 序输出a和b。

#include <stdio.h>

void 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);
}

输出结果

 

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

#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);
      
}

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数组,整型,有10个元素。要输出各元素的值有三种方法:

   (1) 下标法

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

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

#include <stdio.h>

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

 

用数组名作函数参数

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");

}

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");
}

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) 形参和实参都用数组名,如:

void main()

       int a[10];

       … …… f(a,10);

void f(int x[],int n) 

{

..............

}

2) 实参用数组名,形参用指针变量。如:

void main()

{

      int a[10];

      f (a, 10);

}

f (int *a, int n) { …… }

 

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

void main()

int a[10], *p = a;

f (p , 10); }

void f (int *x, int n) { …… }

 

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

void main() { int a[10], *p = a; ……………… f(p,10); }

f (int x[], int n) { ………… }

 

字符串与指针

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

例题一:定义一个字符数组,对它初始化,然 后输出该字符串。

#include <stdio.h>

void main()
{
      char string[] = "I love Fishc.com!";
      
      printf("%s\n", string);
}

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

例题二:可以不定义字符数组,而定义一个字 符指针。用字符指针指向字符串中的 字符。

#include <stdio.h>

void main()
{
      char *string = "I love Fishc.com!I love Fishc.com!";
      
      printf("%s\n", string);
}
char *string = "I love Fishc.com!";
0040D718   mov         dword ptr [ebp-4],offset string "I love Fishc.com!" (00422020)


**********************************************************


char string[] = "I love Fishc.com!";
0040D718   mov         eax,[string "I love Fishc.com!" (00422020)]
0040D71D   mov         dword ptr [ebp-14h],eax
0040D720   mov         ecx,dword ptr [string "I love Fishc.com!"+4 (00422024)]
0040D726   mov         dword ptr [ebp-10h],ecx
0040D729   mov         edx,dword ptr [string "I love Fishc.com!"+8 (00422028)]
0040D72F   mov         dword ptr [ebp-0Ch],edx
0040D732   mov         eax,[string "I love Fishc.com!"+0Ch (0042202c)]
0040D737   mov         dword ptr [ebp-8],eax
0040D73A   mov         cx,word ptr [string "I love Fishc.com!"+10h (00422030)]
0040D741   mov         word ptr [ebp-4],cx


***********************************************************

char string[] = "I love Fishc.com!I love Fishc.com!";
0040D718   mov         ecx,8
0040D71D   mov         esi,offset string "I love Fishc.com!I love Fishc.co"... (00422fbc)
0040D722   lea         edi,[ebp-24h]
0040D725   rep movs    dword ptr [edi],dword ptr [esi]
0040D727   movs        word ptr [edi],word ptr [esi]
0040D729   movs        byte ptr [edi],byte ptr [esi]

 

字符串中字符的存取方法

对字符串中字符的存取,可以用下标方法,也可以用指针方法!

下标法举例:将字符串a复制为字符串b

#include <stdio.h>

void main()
{
      char a[] = "Fishc.com is a good web site!", b[40];
      int i;
      
      for(i=0; *(a+i) != '\0'; i++)
      {
            *(b+i) = *(a+i);
      }
      *(b+i) = '\0';

      printf("String a is: %s\n", a);
      printf("String b is: ");
      for(i=0; b[i] != '\0'; i++)
      {
            printf("%c", b[i]);
      }
      
      printf("\n\n");
}

指针方法举例:将字符串a复制为字符串b

#include <stdio.h>

void main()
{
      char a[] = "Fishc.com is a good web site!", b[40], *p1, *p2;
      int i;
      
      p1 = a;
      p2 = b;
      
      for( ; *p1 != '\0'; p1++, p2++)
      {
            *p2 = *p1;
      }
      *p2 = '\0';

      printf("String a is: %s\n", a);
      printf("String b is: ");
      for(i=0; b[i] != '\0'; i++)
      {
            printf("%c", b[i]);
      }

      printf("\n");
}

 

 

指向函数的指针

用函数指针变量调用函数 可以用指针变量指向整型变量、字符串、数组,也可以指向一个函数。

一个函数在编译时被分配给一个入口地址。这个函数的入口地址就称为函数的指针。

/****************************/
/* 比较a 和 b的大小,求大值 */
/****************************/

#include <stdio.h>

#if(0)
void main()
{
      int max(int, int);
      int a, b, c;

      scanf("%d %d", &a, &b);
      
      c = max(a, b);
      
      printf("a = %d, b = %d, max = %d\n\n", a, b, c);
}
#endif

int max(int x, int y)
{
      int z;
      
      if( x > y )
      {
            z = x;
      }
      else
      {
            z = y;
      }

      return z;
}

#if(1)
//将 main 函数改写为
#include <stdio.h>

void main()
{
      int max(int, int);
      int (*p)();
      int a, b, c;

      p = max;
      scanf("%d %d", &a, &b);
      
      c = (*p)(a, b);
      
      printf("a = %d, b = %d, max = %d\n\n", a, b, c);
}

#endif

 

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

 

题目: 设一个函数process,在调用它的时候,每次实现不同的功能。(有点类似多态)

输入a和b两个数,第一次调用process时找出a和b中大者,

第二次找出其中小者,

第三次求a与b之和。

/***********************************************************/
/*  设一个函数process,在调用它的时候,每次实现不同的功能。*/
/*  输入a和b两个数,第一次调用process时找出a和b中大者,*/
/*  第二次找出其中小者,第三次求a与b之和。               */
/***********************************************************/

#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);
}

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);

例题:有若干个学生的成绩(每个学生有4门课程),要求在用户输入学生序号以后,能输出该学生的全部成绩。用指针函数来实现。

#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");
}

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

      pt = *(pointer + n);

      return pt;
}

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

这两个概念都是简称: 指针函数是指带指针的函数,即本质是一个函数。

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

 

指针数组和指向指针的指针

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

一维指针数组的定义形式为 类型名 数组名[数组长度];

例如: int *name[4];

 

有关指针的数据类型的小结

 

指针运算小结

一、指针变量加(减)一个整数 例如: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)

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

 

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

 

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

关于 void类型

void真正发挥的作用在于:

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

(2) 对函数参数的限定。 例如:void abc( void );

 

posted @ 2019-01-18 09:21  洛水卿卿  阅读(447)  评论(0编辑  收藏  举报