为什么要引入指针

  • 指针为函数提供修改实参的手段
  • 指针为动态内存分配提供支持
  • 指针为动态数据结构(链表,队列等)提供支持
  • 指针使程序更高效,简洁

    1 地址和指针的概念

  • 1.1 地址:

    • 内存区的每一个字节(内存单元)有一个编号,这就是“地址”。如果在程序中定义了一个变量,在对程序进行编译时,系统就会给这个变量分配内存单元。
    • 在C语言中对内存数据(如变量、数组元素等)的存取有两种方法:
      • (1)直接存取
        • 指在程序执行过程中需要存取变量值时,直接用变量名存取所占内存单元中的内容。例:下列俩个语句所构成的程序段中:int x=3,y; y=2*x+3;
      • (2)间接存取
        • 指为了要存取一个变量值,首先从存放变量地址的指针变量中取得该变量的存储地址,然后再从该地址中存取该变量值。简单地说,在这种存取方式中,存取变量的值是通过存取指针变量所“指向”的内存单元中的内容。在此,所谓“指向”,是通过地址来体现的。
  • 1.2 指针:
    • 一个变量的地址称为该变量的“指针”。如 &x 值称为变量 x 的指针。
    • 由于一个变量要占多个内存单元,因此,所谓变量的地址一般均指首地址(即变量所占内存单元中第一个单元的地址)。
  • 1.3 指针变量

    • 专门用于存放其它变量地址的变量称为指针变量。例如,s为指针变量,它既可以存放整型变量x的地址,也可以存放整型变量y的地址。
    • 由上可知,指针变量的值为地址,普通变量的值为数据。
      • 指针(2021.05.28) - 图1
    • 变量的指针就是变量的地址,即指针变量专门用于存放变量的地址(即指向变量)。
      • 指针(2021.05.28) - 图2

        2 变量的指针和指向变量的指针变量

  • 指针变量:专门存放地址的变量

  • 变量的指针:就是变量的地址
  • 为了表示指针变量和它所表示的变量之间的关系,用“ * ” 表示指向的对象。
    • 设 i 为一般变量、i_point为指向 i 的指针变量,则*i_point就是i_point所指的对象 i 。
      • 指针(2021.05.28) - 图3
    • 若有 int i,i_point=&i; 下面两个语句作用相同:① i=3; ②i_point=3;
  • 2.1 怎样定义指针变量

    • 定义指针变量的一般形式为:基本类型标识符 *指针变量名;
    • 在程序中定义了指针变量后,就可以用取地址运算符“&”将同类型变量的地址赋给它,然后就可以间接存取该同类型变量的值。
    • 2.1.1 在定义指针变量时要注意以下几点:
      • ⑴ 指针变量名前的“ ”只表示该变量为指针变量,以便区别于普通变量的定义,而指针变量名不包含该“ ”。(注意:* 不作为指针变量的一部分,只用作区分指针变量与一般变量)
        • 例如: int x,*s;
          • 说明了 s 是一个指针变量,但不能说 *s 是指针变量。
        • 在表达式中,变量前加“*”表示间接存取。
          • 例如:赋值语句 *s=3;表示将整型数据3赋给由指针变量s所指向的变量。
      • ⑵ 定义指针变量时必须指明基类型。一个指针变量不能忽而指向整型,忽而又指向实型。因为,不同类型的变量所占的字节数是不同的。(注意:指针变量的类型要与指向的变量的类型一致)
        • 例如,下面的赋值是错误的∶
          • float a, * pointer_1; //声明了指向实型变量的指针变量
          • int b, * pointer_2; //声明了指向整型变量的指针变量
          • pointer_1=&b; //指向实型变量的指针变量里存放了整型变量的地址
          • pointer_2=&a; //指向整型变量的指针变量里存放了实型变量的地址
    • 2.1.2 在对指针变量赋值时要注意以下几点:

      • (1) 指针变量中只能存放地址,而不能将数值型数据赋给指针变量。
      • (2) 赋给指针变量的变量地址不能是任意的类型,而只能是与指针变量的基类型具有相同类型的变量的地址。
        • 例如,下面的赋值是错误的∶
          • float a, * pointer_1; /声明了指向实型变量的指针变量/
          • int b, * pointer_2; /声明了指向整型变量的指针变量/
          • pointer_1=&b; /指向实型变量的指针变量里存放了整型变量的地址/
          • pointer_2=&a; /指向整型变量的指针变量里存放了实型变量的地址/
      • (3) 与一般的变量一样,也可以对指针变量进行初始化。
        • 指针(2021.05.28) - 图4

          2.2 怎样引用指针变量

    • 2.2.1 声明了 int a,*p; 则在引用指针变量时,可能有三种情况:

      • (1)给指针变量赋值。如:p=&a;
      • (2) 引用指针变量的值(a的地址)。如:printf(“%p”,p);
      • (3) 引用指针变量指向的变量(a的值)。 如:printf(“%d”,*p);
        • 注意:
          • (1)利用指针变量访问内存中的数据,一次访问的内存单元的大小,取决于指针变量的类型:
            • char p ——-> p 访问p指向的内存空间一个字节的数据
            • int p ——-> p 访问p指向的内存空间一个字节的数据
          • (2)指针变量所占内存单元,不取决于指针变量的类型,取决于操作系统:32位操作系统,占4个字节;64位操作系统,占8个字节。
    • 2.2.2 两个有关运算符的使用:
      • (1)& 取地址运算符。 &a是变量a的地址。
      • (2) 指针运算符 (或称“间接访问”运算符),p 是指针变量 p 指向的对象的值。
    • 例2.2.2.1:通过指针变量访问整型变量。
      • 指针(2021.05.28) - 图5
    • 例2.2.2.2:指针变量应用。输入a、b两个整数,按先大后小的顺序输出a和b。

      • 指针(2021.05.28) - 图6

        2.3 指针变量作为函数参数

    • 函数的参数不仅可以是整型、实型、字符型等数据,还可以是指针类型。

    • 利用指针变量作为函数的形参,其作用是将一个变量的地址传送到另一个函数中。
    • 例2.3.1:对例2.2.2.2要求用函数处理,用指针变量作函数的参数。
      • 指针(2021.05.28) - 图7
      • 指针(2021.05.28) - 图8
      • 指针(2021.05.28) - 图9
    • 例2.3.2:输入3个整数a,b,c,要求按大小顺序将它们输出。用函数实现改变这3个变量的值。 其C程序如下:
      • 指针(2021.05.28) - 图10
    • 在用指针变量作为函数参数时,是通过改变形参指针所指向的地址中的值来改变实参指针所指向的地址中的值,因为它们所指向的地址是相同的。但如果在被调用函数中只改变了形参指针值(即地址),则也不会改变实参指针的的值(即地址),即形参指针值的改变是不能改变实参指针值的。
    • 例:2.3.3 若将例2.3.1的程序改成如下:
      • 指针(2021.05.28) - 图11
  • 输出型参数:指针变量可以作输出型参数

    3 通过指针引用数组

    3.1 数组元素的指针

    • 数组的指针是指数组的首地址。
    • 数组元素的指针是指数组元素的地址。因此,同样可以用指针变量来指向数组或数组元素。
    • 在C语言中,由于数组名代表数组的首地址,因此,数组名实际上也是指针。
    • 例3.1.1 :

      • 可以用一个指针变量指向一个数组元素。
        • int a[10]; (定义a为包含10个整型数据的数组)
        • int *p; (定义p为指向整型变量的指针变量)
        • p=&a[0]; 或 p=a; (把a[0]元素的地址赋给指针变量p)
        • 也就是使p指向a数组下标为0的元素(第一个元素) 。
          • 指针(2021.05.28) - 图12

            3.2 指针的运算

    • C语言规定在指针指向数组元素时,可以对指针进行以下运算:

      • 加一个整数(用+或+=),如p+1
      • 减一个整数(用-或-=),如p-1
      • 自加运算,如p++,++p
      • 自减运算,如p—,—p
      • 两个指针相减,如p1-p2 (只有p1和p2都指向同一数组中的元素时才有意义)。(指针不能相加)
    • 分别说明如下:

      • (1) 如果指针变量p已指向数组中的一个元素,则p+1指向同一数组中的 下一个元素,p-1指向同一数组中的上一个元素。即p+1或p-1也表示地址。但要注意的是,虽然指针变量p中存放的是地址,但p+1并不表示该地址加1,而表示在原地址的基础上加了该数据类型所占的字节数d 。
      • (2) 如果p原来指向a[0],执行++p后p的值改变了,在p的原值基础上加d,这样p就指向数组的下一个元素a[1]。d是数组元素占的字节数。
      • (3) 如果p的初值为&a[0]则p+i 和a+i 就是数组元素a[i+1]的地址,或者说,它们指向a数组的第 i+1 个元素 。
      • (4) (p+i) 或 (a+i) 是 p+i 或 a+i 所指向的数组元素,即a[i]。
      • (5) 如果指针变量p1和p2都指向同一数组,如执行p2-p1,结果是两个地址之差除以数组元素的长度d。

        3.3 通过指针引用数组元素

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

      • (1)下标法,如a[i] 形式;
      • (2)指针法,如(a+i)或(p+i)。其中a是数组名,p是指向数组元素的指针变量,其初值p=a。
    • 例: 有一整型数组a,有10个元素。输出数组中的全部元素。
      • 分析:要输出各元素的值有三种方法:1.下标法:通过改变下标输出所有元素;2.通过数组名计算出数组元素的地址,找出数组元素的值;3.用指针变量指向数组元素。
        • 指针(2021.05.28) - 图13
        • 指针(2021.05.28) - 图14
        • 指针(2021.05.28) - 图15
        • 指针(2021.05.28) - 图16
    • 注意:使用指针变量指向数组元素时,注意以下几个问题:

      • (1)可以通过改变指针变量p的值来指向不同的元素。p++使p指向不同的元素,而a++是无法实现的,因为a是数组的首地址,是常量。
      • (2) 要注意指针变量的当前值。
      • (3) 使用指针变量指向数组元素时,应切实保证指向有效的元素,注意不要越界(超过最大元素),越界系统不提示。
      • (4) 指向数组的指针变量也可以带下标,如p[i]。若p=&a[0],则p[i]、*(p+i) 代表a[i]。
      • (5)
        • ① p++;*p; 先使p指向下一元素,然后再取其值;
        • p++ 相当于 (p++),即先求*p,再作p++
        • (++p) 先使p++ ,再取p
        • ④ (*p)++ 使所指向的元素值加1,而不是指针值加1
        • ⑤ 若p指向a数组中第i个元素,则:
          • 指针(2021.05.28) - 图17

            3.4 数组名作为函数参数

    • 可以用数组名作函数的参数。例如:

      • 指针(2021.05.28) - 图18
    • 例:将数组a中n个整数按相反顺序存放。
      • 指针(2021.05.28) - 图19
    • 分析: 算法: 将a[0]与a[n-1]对换; a[1]与a[n-2]对换; …… 直到a[(n-1)/2]与 a[n-(n-1)/2]-1] 用循环,设两个变量i、j。 i 从0变到(n-1)/2 j 从n-1开始,到i结束时停止。
    • 代码:
      • 指针(2021.05.28) - 图20
    • 如果有一个实参数组,想在函数中改变此数组中的元素的值,实参与形参的对应关系有以下4种情况:

      • (1)形参和实参都用数组名,如:
        • 指针(2021.05.28) - 图21
      • (2) 实参用数组名,形参用指针变量。如:
        • 指针(2021.05.28) - 图22
      • (3)实参形参都用指针变量。例如:
        • 指针(2021.05.28) - 图23
      • (4)实参为指针变量,形参为数组名。如:
        • 指针(2021.05.28) - 图24

          3.5 通过指针引用多维数组

    • 指针变量可以指向一维数组元素,也可以指向多维数组元素。

    • 3.5.1 多维数组元素的地址
      • 多维数组的性质,可以认为二维数组是“数组的数组”,例 :定义inta[3][4]={{1,3,5,7},{9,11,13,15},{17,19,21,23}};则二维数组a是由3个一维数组所组成的。设二维数组的首行的首地址为2000 ,则二维数组的有关指针如下表:
        • 指针(2021.05.28) - 图25
    • 3.5.2 指向多维数组元素的指针变量

      • (1)指向数组元素的指针变量
        • 指针(2021.05.28) - 图26
      • (2)指向由m个元素组成的一维数组的指针变量
        • 指针(2021.05.28) - 图27
      • (3)用指向数组的指针作函数参数
        • 指针(2021.05.28) - 图28

          4 通过指针引用字符串

          4.1 字符串指针

    • 在C语言中,表示一个字符串有以下两种形式:

      • 1)用字符数组存放一个字符串。
        • 指针(2021.05.28) - 图29
      • 2)用字符指针指向一个字符串。
        • 指针(2021.05.28) - 图30
    • 字符数组和字符指针变量都能实现字符串的存储与运算。
    • 联系与区别
      • 字符数组由元素组成,每个元素中存放一个字符,而字符指针变量中存放的是地址,也能作为函数参数。
      • 只能对字符数组中的各个元素赋值,而不能用赋值语句对整个字符数组赋值。例如,以下语句是错误的:
        char s[20; s=”How are do you do!”; ×
      • 而对字符指针变量可以直接赋值,此时赋的是字符串首地址。例如,下列程序段是合法的:
        char *s; s=”How are do you do!”; √
      • 字符数组名虽然代表地址,但数组名的值不能改变。例如,下列用法是错误的:char s[ ]=”How are do you do!”;s=s+4;× 因数组名是常量printf(“%s\n”,s);
        • 但字符指针变量的值可以改变。例如,下列用法是合法的:
          char *s=”How are do you do!”;s=s+4; √printf(“%s\n”,s);
      • 对于字符串中字符的存取,可以用下标法,也可以用指针法。例如,
        char s=”How are do you do!”;printf(“%c\n”,s[4]); (相当于 (s+4))其运行结果为 a
      • 例 4.1.3 将字符串a复制为字符串b
        • 指针(2021.05.28) - 图31
      • 例4.1.4 用指针变量处理 例 4.1.3
        • 指针(2021.05.28) - 图32
    • !!!注意:
      char *p = "I love China."; *(p+2) = 'L'; ×

      4.2 字符指针作为函数参数

    • 将一个字符串从一个函数传递到另一个函数,可以采用地址传递的方法,即可将字符数组名或字符指针变量作为函数参数。采用以下四种形式:

      • 1)实参与形参都用字符数组名
      • 2)实参用字符数组名,形参用字符指针变量
      • 3)实参与形参都用字符指针变量
      • 4)实参用字符指针变量,形参用字符数组名
        • 指针(2021.05.28) - 图33
      • 例4.2.1 用函数调用实现字符串复制以及计算字符串长度。
        • 指针(2021.05.28) - 图34
      • 在上述程序中,函数str_copy()中的两个形参均定义为字符指针变量。也可以定义为字符数组,即函数str_copy()变为
        • 指针(2021.05.28) - 图35

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

    • 字符数组和字符指针变量二者之间的区别主要有以下几点:

      • (1) 字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串第1个字符的地址),决不是将字符串放到字符指针变量中。
      • (2) 赋值方式。对字符数组只能对各个元素赋值,不能用以下办法对字符数组赋值。char str[14];str=″I love China!″;×
        • 而对字符指针变量,可以采用下面方法赋值:
          char*a;a=″I love China!″; √
      • (3) 对字符指针变量赋初值:
        char a=″I love China!″; 等价于 char a; a=″I love Chian!″;
      • 而对数组的初始化:
        char str[14]={″I love China!″};不能等价于 char str[14]; str[ ]=″I love China!″;
      • (4) 定义一个字符数组,在编译时为它分配内存单元,它有确定的地址。而定义一个字符指针变量时,给指针变量分配内存单元,在其中可以存放一个字符变量的地址。
        • 指针(2021.05.28) - 图36
        • 指针(2021.05.28) - 图37
      • (5) 指针变量的值是可以改变的。例4.3.1 改变指针变量的值。
        • 指针(2021.05.28) - 图38
      • (6) 若字符指针变量p指向字符串,就可以用指针变量带下标的形式引用所指的字符串中的字符。如有:
        char *p=″I love China!″; 可用 printf(“%c\n”,p[5]); 输出字符‘e’。
      • (7) 字符数组中各元素的值是可以改变的(可以对它们再赋值),但字符指针变量指向的字符串中的内容是不可以被取代的(不能对它们再赋值)。如:
        #include void main() { char a[ ]=”House”; char*b=”House”; a[2]=‘r’; // 合法,‘r ’取代‘u ’ b[2]=’r’; // 非法,字符串常量不能改变 printf(“%s\n”,a); printf(“%s\n”,b); }

        5 指向函数的指针

        5.1 什么是函数指针

    • 如果在程序中定义了一个函数,在编译时,编译系统为函数代码分配一段连续的存储空间,这段存储空间的起始地址(又称入口地址)称为这个函数的指针。在C语言中,函数名本身就代表该函数的入口地址。

    • 所谓指向函数的指针,就是指向函数的入口地址。
    • 可以定义一个指向函数的指针变量,用来存放某一函数的起始地址,这就意味着此指针变量指向该函数。

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

    • 如果想调用一个函数,除了可以通过函数名调用以外,还可以通过指向函数的指针变量来调用该函数。

      • 指向函数的指针变量其定义形式如下:
        • 类型标识符(*指针变量名)(函数参数列表 );
          • 其中:类型标识符是指函数返回值的类型。 如: int (*p) (int,int);定义了一个指向函数的指针变量p,该指针变量指向的函数应返回整型值,且有两个整型形式参数。 此后只要将一个函数名赋给函数指针变量,然后就能通过函数指针间接调用该函数。
    • 例 5.2.1 用函数求a和b中的大者
    • 例5.2.2 输入两个整数,然后让用户选择1或2,选1时调用max函数,输出二者中的大数,选2时调用min函数,输出二者中的小数。
      • 指针(2021.05.28) - 图39
    • 下面对指向函数的指针作几点说明:

      • 在给函数指针变量赋值时,只需给出函数名,不必给出参数。如上例中的“p=max;”与“p=min;”。
      • 可以通过指向函数的指针变量来调用函数,其调用形式为(函数指针变量名)(实参表)如上例中的(p)(a,b) (还可以:(函数指针变量名)(实参表) 如p(a,b))。但在调用前必须给函数指针变量赋值。
      • 对函数指针变量运算是没有意义的。若p为函数指针变量,则 p=p+n、p++、p—等都是没有意义的。

        5.3 用指向函数的指针变量作为函数参数

    • 指向函数的指针变量的一个重要用途是把指针作为参数传递到其它函数。

    • 指向函数的指针也可以作为函数的参数,以实现函数地址的传递,这样就能够在被调用的函数中使用实参函数。
      • 指针(2021.05.28) - 图40
    • 例5.3.1 有两个整数a和b,由用户输入1,2或3。如输入1,程序就给出a和b中大者;输入2,就给出a和b中小者;输入3,则求a与b之和。
      • 指针(2021.05.28) - 图41
    • !!!隐式调用 (资料待补充)例子:
      int callback(int(pf)(int,int)) { return (pf)(5,8); } int max(int a,int b) { return a>b?a:b; } max(5,8); // 显式调用 callback(max); // 隐式调用 (回调)

      6 返回指针值的函数

  • 在C语言中,一个函数不仅可以返回整型、字符型、实型等值,也可以返回指针类型的值,即C语言中还允许定义返回指针值的函数,其形式如下:

    • 指针(2021.05.28) - 图42
  • 例如int a( int x,int y); { int p; … return(p); }

    • 定义的函数 a( ) 将返回一个指向整型数据的指针值。
    • 实际上,在C语言中,可以定义返回任何类型指针值的函数。
    • 例6.1 .1有若干个学生的成绩(每个学生有4门课程),要求在用户输入学生序号以后,能输出该学生的全部成绩。用指针函数来实现。
      • 指针(2021.05.28) - 图43
    • 说明:
      • Search是指针型函数,它的形参pointer是指向包含4个元素的一位数组的指针变量。
      • pointer+1指向下一行。 *(pointer+1)指向第0列元素。
    • 注意:返回变量地址时,变量的生存周期要长!!!

      7 指针数组和多重指针

      7.1 指针数组的感念

    • 元素值均为指针类型数据的数组称为指针数组。也就是说,指针数组中的每一个元素都相当于一个指针变量。
      例:int p[8]; //定义了具有8个元素的指针数组,每一个元素是一个整型指针变量 int (p)[8]; //定义了一个指针变量,指针变量指向具有8个整型元素的数组

    • 一维指针数组的定义形式为:
      • 指针(2021.05.28) - 图44
      • 指针(2021.05.28) - 图45
    • 例7.1.1 将若干字符串按字母顺序(由小到大)输出

      • 指针(2021.05.28) - 图46

        7.2 指向指针数据的指针

    • 定义一个指向指针数据的指针变量:

      • 指针(2021.05.28) - 图47
      • 说明:
        • p的前面有两个*号。
        • 运算符的结合性是从右到左,因此**p相当于(p),显然p是指针变量的定义形式。如果没有最前面的,那就是定义了一个指向字符数据的指针变量。现在它前面又有一个号,表示指针变量 p 是指向一个字符指针变量的。*p 就是p所指向的另一个指针变量。
        • 例7.2.1使用指向指针数据的指针变量。
          • 指针(2021.05.28) - 图48
        • 例7.2.2 下列C程序的功能是,首先利用指针数组指向数组中的各元素,然后利用指向指针的指针输出数组中的各元素。
          • 指针(2021.05.28) - 图49
        • 例7.2.3 一个指针数组的元素指向整型数据的简单例子 。
          • 指针(2021.05.28) - 图50
  • 7.3 数组指针(待补充!!!)
    • Main 函数的函数原型
  • C语言中的主函数是可以有参数的。带参数ain()函数的一般形式如下:
    • 指针(2021.05.28) - 图51
  • 参数:
    • argc :获取命令行参数个数
  • 引申:
    • 字符串转数值:atoi sscanf
    • 数值转字符串: sprintf
  • 例 :编写一个命令程序,其命令符为file,用以输出命令行中除命令符外以空格分隔的所有字符串(一行输出一个字符串)。其C程序如下:

    • 指针(2021.05.28) - 图52

      8 动态内存分配与指向它的指针变量

      8.1 什么是内存的动态分配

    • 前面介绍过全局变量和局部变量,全局变量是分配在内存中的静态存储区的,非静态的局部变量(包括形参)是分配在内存中的动态存储区的,这个存储区是一个称为栈(stack)的区域。

    • 除此以外,C语言还允许建立内存动态分配区域,以存放一些临时用的数据,这些数据是临时存放在一个的特别的自由存储区,称为堆(heap)区。可以根据需要向系统申请所需大小的空间。由于未在声明部分定义它们为变量或数组,因此不能通过变量名或数组名去引用这些数据,只能通过指针来引用。

      8.2 怎样建立内存的动态分配

    • 对内存的动态分配是通过系统提供的库函数来实现的,主要有以下4个函数:

      • (1) malloc 函数
        • 函数原型为 void *malloc(unsigned int size);
        • 作用 在内存的动态存储区中分配一个长度为size的连续空间。
        • 说明 此函数的返回值是所分配区域的起始地址,即指针值。
          • 例如: malloc(100);
            • 功能:开辟100字节的临时存储空间,函数值为第一个字节的地址。
        • 注意:因函数是void型的,不指向任何类型的数据,只提供一个地址。若函数未能成功执行,返回空指针NULL。
      • (2) calloc 函数
        • 函数原型为 void *calloc( unsigned n,unsigned size );
        • 作用 在内存的动态存储区中分配n个长度为size的连续空间。
        • 说明 此函数可以为一维数组开辟动态存储空间,n 为数组元素个数,为个元素长度为size,这就是动态数组。
          • 例如: p=calloc(50,4);
            • 功能:开辟50×4字节的临时分配域,把起始地址赋给指针变量p。
            • 注意:函数返回一个指向分配域起始位置的指针。如果分配不成功,返回空指针NULL。
      • (3) free 函数
        • 函数原型为 void free( void *p );
        • 作用 释放由指针变量p指向的动态空间,使这部分空间能被其它变量使用。
        • 说明 p是最近一次调用callo或malloc函数时的返回值。
        • 该函数无返回值。
          • 例如: free(p);
            • 功能:释放了指针变量p指向的已分配的动态空间。
      • (4) realloc 函数
        • 函数原型为 void realloc(void p,unsigned int size);
        • 作用 将p指向的临时分配域的大小改变为size大小。
        • 说明 若想改变已经通过malloc函数或calloc函数分配的临时域的大小,可以用recalloc函数重分配。
          • 例如: realloc(p,50);
            • 功能:将p所指向的已分配的动态空间改为50字节。
            • 注意:如果重新分配不成功,返回空指针NULL。
      • 注意:以上4个函数的声明在stdlib.h头文件中,使用它们前应用#include命令包含到程序文件中。

        8.3 void指针类型

    • void 指针类型,允许定义一个基类型为void 的指针变量,它不指向任何具体的数据。请注意:不要把“指向void类型”理解为能指向“任何的类型”的数据,而应理解为“指向空类型”或“不指向确定的类型”的数据。在将它的值赋给另一指针变量时要进行强制类型转换使之适合于被赋值的变量的类型。

      • 指针(2021.05.28) - 图53

        9 内存操作

        9.1 内存操作函数

    • C语言对内存的操作是通过系统提供的库函数来实现的,主要有以下4个函数:

      • (1) memset 函数
        • 函数原型为 void memset(void s, int c, size_t n);
        • 作用 用c去填充起始地址为s的内存前n个字节连续空间。
        • 说明 此函数的返回值是s的地址。
          • 例如: char a[10]; memset(a,0,10);
            • 功能:开辟10字节的临时存储空间,将该空间内全部填充为0。
      • (2) memcpy 函数
        • 函数原型为 void memcpy(void dest, const void *src, size_t n);
        • 作用 将内存地址src 的前n个字节的数据拷贝到内存地址dest。
        • 说明 此函数适合于内存地址无重叠的拷贝。
      • (3) memmove 函数
        • 函数原型为 void memmoevoid dest, const void *src, size_t n);
        • 作用 将内存地址src 的前n个字节的数据拷贝到内存地址dest。
        • 说明 此函数可用于内存地址重叠的拷贝。
      • (4) memcmp 函数
        • 函数原型为 int memcmp(const void s1, const void s2, size_t n);
        • 作用 比较存地址s1和s2 的前n个字节的数据
        • 说明 若参数s1 和s2 所指的内存内容都完全相同则返回0 值。s1 若大于s2 则返回大于0 的值。s1 若小于s2 则返回小于0 的值。
      • (5) memchr函数
        • 函数原型为 void memchr(const void s, int c, size_t n);
        • 作用 在地址为s的前n个字节的内存空间中查找数据c
        • 说明 此函数返回c在给定的内存范围中第1次出现的地址,如果未找到返回NULL指针,
      • (6) memrchr函数
        • 函数原型为 void memrchr(const void s, int c, size_t n);
        • 说明 该函数类似于memrchr,只不过是从后向前查找
      • 注意:以上4个函数的声明在string.h头文件中,使用它们前应用命令包含到程序文件中。
        • 本章小结
    1. 指针就是地址。变量的指针就是变量的地址。指针变量就是地址变量。指针是地址本身,指针变量是用来存放地址的变量。
    1. 指向 把谁的地址存放在指针变量中,该指针变量就指向谁
    1. 深入掌握对数组操作中怎样正确使用指针。
    1. 有关指针变量的定义形式归纳比较,见表8-4。
    1. 指针运算小结
  • ① 指针变量加(减)一个整数
    • p++、p—、p+i、p-i、p+=i、p-+i 等均使指针变量p加(减)一个整数。改变了指向的地址值。
  • ② 指针变量赋值
    • 不要把一个整型数赋给指针变量
  • ③ 指针变量可以赋空值
    • p=NULL 即不指向任何变量
  • ④ 两个指针变量可以相减
    • 两个指向同一数组的指针变量可以相减,得到两个指针之间元素的个数,但相加无意义
  • ⑤ 两个指针变量比较
    • 两个指向同一数组的指针变量可以比较,指向前面元素的指针“小于”指向后面元素的指针变量
      • 补充:指针常量,常量指针
  • 指针常量: 类型* const 指针名;
    • 例子: int* const p;
  • 常量指针: const 类型 指针名; 或 类型 const 指针名;
    • 例子: const int p; / int const p;
  • 区别:
    • 指针常量:不允许修改指针指向,但指向的数据可以修改
      int const p = &a;p++; //errorp = 7879; //ok
    • 常量指针:允许修改指针指向,但指向的数据不可以修改
      int const p = &a;p++; //okp = 7879; //error
  • 记忆小技巧:
    • {类型*} {const} 指针名
      指针 常量
    • “谁在前先叫谁,谁在前谁不能修改”