指针知识点

目录

10.2变量的指针和指向变量的指针变量

10.2.1定义指针变量

10.2.2指针变量的引用

10.2.3指针变量作为函数参数

10.3数组与指针

10.3.2通过指针引用数组元素

10.3.3用数组名作函数的参数

10.3.4多维数组与指针

10.4字符串与指针

10.4.1字符串的表示形式

10.4.2字符指针作函数参数

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

10.5指向函数的指针

10.5.1用函数指针变量调用函数

10.5.2用指向函数的指针作函数参数

10.6返回指针值的函数

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

10.7.1指针数组的概念

10.7.2指向指针的指针

10.7.3指针数组作main()函数的形参

10.8有关指针的数据类型和指针运算的小结

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

10.8.2指针运算小结


 

正确而灵活地运用指针,可以有效地表示复杂的数据结构;能动态分配内存;方便地使用字符串;有效而方便地使用数组;在调用函数时能获得1个以上的结果;能直接处理内存单元地址等。

10.2变量的指针和指向变量的指针变量

10.2.1定义指针变量

如果在程序中定义了一个变量,在对程序进行编译时,系统就会给这个变量分配内存单元。

在程序中一般是通过变量名来对内存单元进行存取操作的。其实程序经过编译以后已经将变量名转换为变量的地址,对变量值得存取都是通过地址进行的。

10.2.2指针变量的引用

*&a     按自右而左方向结合,先进行&a运算,得到a的地址,再进行*运算,即&a所指向的变量,也就是变量a。

(*p)++   括号是必须的,因为++和*为同一优先级别,而结合方向为自右而左。

10.2.3指针变量作为函数参数

eg10.3

10.3数组与指针

10.3.2通过指针引用数组元素

eg10.5

eg10.6

 

引用数组元素可以用下标法(eg. a[3]),也可以用指针法,即通过指向数组元素的指针找到所需的元素。使用指针法能使目标程序质量高(占内存少,运行速度快)。

 

(1)

    int  a[10];

    int  *p;

 

    p = &a[0];     <=>   p = a;

 

(2)

    int  a[10];

    int  *p = &a[0];     <=>     int  *p = a;   

 

(3)指向数组的指针变量也可以带下标,eg. p[i]   <=>  *(p+i)

     p[i]   <=>  *(p+i)   <=>   a[i]   <=>  *(a+i)

 

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

(1)下标法,eg. a[i]

(2)指针法,eg.  *(p+i)  或 *(a+i)。其中,a是数组名,p是指向数组元素的指针变量,其初值p=a。

 

 

指针变量的运算 p指向数组a的首元素(即p=a)

(1)*p++  

         由于++和*同优先级,结合方向为自右向左,因此   *p++  <=>  *p(++)  

(2)*p++  <=>  *p(++)    先得到p所指向的变量的值(即*p),然后再使p+1=>p

          *(++p)   先使p+1,再取*p

          若p初值为a(即&a[0]),则*p(++)为a[0]

                                                    *(++p)为a[1]

(3)++(*p) 表示p所指向的元素值加1

       若p初值为a(即&a[0]),则++(*p)相当于++a[0]。若a[0]=3,则在执行++(*p)后,a[0]的值为4.

       元素值a[0]加1,而不是指针p的值加1.

 

10.3.3用数组名作函数的参数

eg10.7

eg10.8

 

fun(int arr[], int n)     <=>      fun(int *arr, int n)    

实参数组名代表该数组首元素的地址,而形参是用来接收从实参传递过来的数组首元素地址的。因此,形参应该是一个指针变量(只有指针变量才能存放地址)。实际上,C编译都是将形参数组名作为指针变量来处理的。

在该函数被调用时,系统会建立一个指针变量arr,用来存放从主调函数传递过来的实参数组首元素的地址。

 

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

void inv(int *a, int n){

    ...

}

 

 

void main(void){
    int a[5] = {3, 7, 9, 11, 0};

    ...    
    inv(a, 5);
 }

void inv(int a[], int n){

    ...

}

 

void main(void){
    int a[5] = {3, 7, 9, 11, 0};

    ...    
    inv(a, 5);
 }

void inv(int *a, int n){

    ...

}

 

void main(void){
    int a[5] = {3, 7, 9, 11, 0};

    int  *p = a;

    ...    
    inv(p, 5);


 }

void inv(int a[], int n){

    ...

}

 

void main(void){
    int a[5] = {3, 7, 9, 11, 0};

    int  *p = a;

    ...    
    inv(p, 5);


 }

10.3.4多维数组与指针

eg10.11

eg10.12

eg10.13

eg10.14

 

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

 

(1)a是一个数组名。

(2)a数组包含3行,即3个元素:a[0]  a[1]  a[2]。而每一个元素又是一个一维数组,包含4个元素。eg. a[0]所代表的一维数组又包含4个元素:a[0][0]  a[0][1]  a[0][2]  a[0][3],可以认为二维数组是“数组的数组”,即二维数组a是由3个一维数组所组成的。

(3)a代表二维数组首元素的地址,现在的首元素不是一个简单的整型元素,而是由4个整型元素所组成的一维数组,因此a代表的是首行(即第0行)的首地址。a+1代表第1行的首地址。

(4)a[0]  a[1]  a[2] 既然是一维数组名,而c语言又规定数组名代表数组首元素地址,因此a[0]代表一维数组a[0]中第0列元素的地址,即&a[0][0]。a[1]的值是&a[1][0],a[2]的值是&a[2][0]。

(5)a[0]+0、a[0]+1、a[0]+2、a[0]+3分别是a[0][0]、a[0][1]、a[0][2]、a[0][3]元素的地址(即&a[0][0]、&a[0][1]、&a[0][2]、&a[0][3])。

(6)a[i]  <=> *(a+i)

         a[0]+1  <=>  *(a+0)+1  <=>  &a[0][1] 

         a[1]+2  <=>  *(a+1)+2  <=>  &a[1][2] 

(7) a[0]+1  <=>  *(a+0)+1  <=>  &a[0][1] 

          *(a[0]+1)  <=> *(*(a+0)+1)  <=> *(&a[0][1])  <=>  a[0][1]

          *(a[i]+j)  <=> *(*(a+i)+j)  <=> *(&a[i][j])  <=>  a[i][j]

 

 

 

 

(8)二维数组名(eg. a)是指向行的,因此a+1中的“1”代表一行中全部元素所占的字节数。

        一维数组名(eg. a[0] a[1])是指向列元素的,a[0]+1中的1代表一个元素所占的字节数。

        在指向行的指针前面加一个*,就转换为指向列的指针。

        在指向列的指针前面加一个&,就成为指向行的指针。

(9)在二维数组中,&a[i]、a+i、a[i]、*(a+i)、&a[i][0]的值相等,即它们代表同一地址。

        &a[i]  <=>   a+i    指向行

          a[i]  <=>  *(a+i)  指向列

 

输出某个指定的数值元素

如果要输出某个指定的数值元素,则应事先计算该元素在数组中的相对位置(即相对于数组起始位置的相对 位移量)。计算a[i][j]在数组中的相对位置的计算公式为:i*m+j ,其中m为二维数组的列数。

eg.对上述3x4的二维数组,它的元素a[2][3]对a[0][0]的相对位移量为2*4+3=11个元素。

     如果开始时使指针变量p指向a[0][0],可以用*(p+2*4+3)表示a[2][3]的值。(p+11)是a[2][3]的地址。

     a[i][j]的地址为&a[0][0]+4*(i*m+j)  (一个整数占4个字节)

    

多维数组名做函数参数

1.用指向变量的指针变量

2.用指向一维数组的指针变量

float average(float *s, int n){
  ......
}

void main(void){
    float score[3][4] = {......};
    ......

   av = average(*score, 12);   //*score, 即score[0],即&score[0][0]
    ......
}

void search(float (*s)[4], int n){
    ......
}

 

void main(void){
    float score[3][4] = {......};
    ......
    search(score, ...);
    ......
}

10.4字符串与指针

10.4.1字符串的表示形式

eg10.15

eg10.16

eg10.17

eg10.18

 

10.4.2字符指针作函数参数

eg10.19

 

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

虽然用字符数组和字符指针变量都能实现字符串的存储和运算,但他妈二者之间是有区别的,主要有以下几点:

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

(2)赋值方式。

对字符数组只能对各个元素赋值,不能用以下方法对字符数组赋值:

char  str[14];

str = "I love China!";    //错误

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

char  *a;

a = "I love China!";     //注意:赋给a的不是字符,而是字符串第一个元素的地址

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

char  *a = "I love China!";      <=>      char  *a;

                                                              a = "I love China!";

而对数组的初始化:

char  str[14] = "I love China!";   不等价于      char  str[14];

                                                                            str = "I love China!";

即数组可以在定义时整体赋初值,但不能在赋值语句中整体赋值。

(4)如果定义了一个字符数组,在编译时为它分配内存单元,它有确定的地址。

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

 

想输入一个字符串时,

char  str[10];

scanf("%s", str);

char *a;
scanf("%s", a);
char *a;
char str[10];
a = str;
scanf("%s", a);
 

这种方法比较危险,不提倡。

因为未对a进行初始化,虽然在编译时为a分配了内存单元,a的地址已经指定,但是不知道a的值,不知道a指向哪,会造成危险。

使a有确定值,使a指向一个数组的首元素,然后输入一个字符串,把它存放在以该地址开始的若干单元中。

(5)指针变量的值是可以改变的。

char  *a = "I love China!";  

a = a+7;

数组名虽然代表地址,但它是常量,它的值是不能改变的。

char  str[14] = "I love China!";  

str = str+7;           //错误

 

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

只要改变指针变量format所指向的字符串,就可以改变输入输出的格式。也可以用字符数组实现但不能采用赋值语句对数组整体赋值
char *format;
format = "a=%d, b=%f\n";
printf(format, a, b);
char format[] = "a=%d, b=%f\n";
printf(format, a, b);

char format[] = "a=%d, b=%f\n";

format = "a=%d, b=%f\n";    //错误
printf(format, a, b);

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

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

 

10.5指向函数的指针

10.5.1用函数指针变量调用函数

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

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

可以用一个指针变量指向函数,然后通过该指针变量调用此函数。

 

eg10.22

 

函数的调用

可以通过函数名调用也可以通过函数指针调用(即用指向函数的指针变量调用)

#include<stdio.h>

 

int max(int a, int b);

 

int max(int a, int b){
    return (a>b) ? a : b;
}

 

void main(void){
    int a, b, c;
    
    printf("请输入两个整数:");
    scanf("%d%d", &a, &b);
    
    c = max(a, b);
    printf("%d和%d中的最大值是:%d\n", a, b, c); 
}

#include<stdio.h>

 

int max(int a, int b);

 

int max(int a, int b){
    return (a>b) ? a : b;
}

 

void main(void){
    int a, b, c;
    int (*p)(int, int);     
    
    printf("请输入两个整数:");
    scanf("%d%d", &a, &b);
    
    p = max;
    c = (*p)(a, b);

    printf("%d和%d中的最大值是:%d\n", a, b, c); 
}

 

int (*p)(int, int); //定义

用来定义p是一个指向函数的指针变量,它不是固定指向哪一个函数的,而只是表示定义了这样一个类型的变量,它是专门用来存放函数的入口地址的。在程序中把哪一个函数的地址赋给它,它就指向哪一个函数。在一个程序中,一个指针变量可以先后指向同类型的不同函数。

*p两侧的括号不可省略,表示p先与*结合,是指针变量,然后再与后面的()结合,表示此指针变量指向函数。

 

 p = max;      //赋值

在给函数指针变量赋值时,只需给出函数名而不必给出参数,因为这是将函数的地址赋给p,不涉及实参与形参的问题。

将函数max的人口地址赋给指针变量p

这时,p是指向函数max的指针变量

调用*p就是调用max函数

(注)p是指向函数的指针变量,它只能指向函数的入口地址而不可能指向函数中的某一条指令处。对指向函数的指针变量,像p+n、p++、p--等运算都是无意义的。

 

c = (*p)(a, b);     //调用

<=>c = max(a, b); 

用函数指针变量调用函数时,只需将(*p)代替函数名即可,在(*p)后面的括号中根据需要写上实参。

 

10.5.2用指向函数的指针作函数参数

这样可以增加函数使用的灵活性

eg10.23

10.13

int max(int a, int b);
int min(int a, int b);
int add(int a, int b);
void process(int (*p)(int, int), int x, int y);

 

void main(void){
    int a, b, c;
    
    printf("请输入两个整数:");
    scanf("%d%d", &a, &b);
    
    printf("%d和%d中的最大值是:", a, b); 
    process(max, a, b);
    
    printf("%d和%d中的最小值是:", a, b); 
    process(min, a, b);
    
    printf("%d和%d之和是:", a, b); 
    process(add, a, b);

 

10.6返回指针值的函数

一个函数可以返回一个整型值、字符值、实型值等,也可以返回指针型的数据,即地址。

eg10.24

eg10.25

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

10.7.1指针数组的概念

https://blog.csdn.net/weixin_42072280/article/details/83210655  指针数组与数组指针

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

为什么要用指针数组呢?

因为它比较适合于用来指向若干个字符串,使字符串处理更加方便灵活。

eg.图书馆有若干本书,想把书名放在一个数组中,然后要对这些书名进行排序和查询。按一般方法,字符串本身就是一个字符数组。因此要设计一个二维的字符数组才能存放多个字符串。但在定义二维数组时,需要指定列数,也就是说二维数组中每一行中包含的元素个数(即列数)相等。而实际上各字符串(书名)长度一般是不相等的。如按最长的字符串来定义列数,则会浪费许多内存单元。

可以分别定义一些字符串,然后用指针数组中的元素分别指向各字符串。如果想对字符串排序,不必改动字符串的位置,只需改动指针数组中各元素的指向。这样,各字符串的长度可以不同,而且移动指针变量的值(地址)y要比移动字符串所花的时间少得多。

 

char *name[] = {"Follow me", "BASIC", "The Great Wall", "FORTRAN", "Computer Design"};

这些字符串是不等长的,并不是按同一长度定义的。

eg10.26 比较字符串   类似于10.12

10.12

 

10.7.2指向指针的指针

char  **p;

*的结合方向为自右向左,char  **p;  <=>  char  *(*p);

 

eg10.27

eg10.28

10.7.3指针数组作main()函数的形参

https://blog.csdn.net/weixin_42072280/article/details/83514441

 

 

 

 

10.8有关指针的数据类型和指针运算的小结

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

定义含义
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是一个指针变量,它指向一个指向整型数据的指针变量

10.8.2指针运算小结

说明:

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

其中NULL是整数0,它使p的存储单元中所有二进制位为0,也就是使p指向地址为0的单元。系统保证使该单元不做他用(不存放有效数据),即有效数据的指针不指向0单元。

(注)p的值为NULL和p未赋值是两个不同的概念。

(2)两个指针变量可以相减

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

(3)两个指针变量比较

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

安安csdn

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

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

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

打赏作者

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

抵扣说明:

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

余额充值