C语言学习-第八章 善于利用指针⑤

224 阅读9分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第28天,点击查看活动详情

指针数组和多重指针

什么是指针数组

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

int *p[4];

  • [] 比 * 优先级高,因此p先与[4]结合,形成p[4]形式,表示p数组有4个元素
  • 然后再与前面的 * 结合,* 表示此数组是指针类型的,每个数组元素(相当于一个指针变量)都可指向一个整型变量。

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

指向指针数据的指针变量

指向指针数据的指针变量,简称为指向指针的指针

定义一个指向指针数据的指针变量:char **p;

**p相当于*(*p), *p是指针变量的定义形式。如果没有最前面的*, 那就是定义了一个指向字符数据的指针变量。

char **p, 可以把它分为两部分看,即 char* 和(*p)

  • (*p)表示p是指针变量
  • char * 表示p指向的是char *型的数据

即p是指向一个字符指针变量(这个字符指针变量指向一个字符型数据)。

指针数组作 main 函数的形参

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

int main()int main(void)
// 括号中是空的或有“void”, 表示main函数没有参数,调用main函数时不必给出实参。
// main函数可以有参数:
int main(int argc, char *argv[])
// argc和argv就是main函数的形参,它们是程序的“命令行参数”
// argc:参数个数
// argv:它是一个*char指针数组,数组中每一个元素(其值为指针)指向命令行中的一个字符串的首字符

命令行的一般形式为:命令名 参数1 参数2 ... 参数n 命令名和各参数之间用空格分隔。命令名是可执行文件名(此文件包含main函数),假设可执行文件名为 file1.exe, 今想将两个字符串“China”,“Beijing”作为传送给main函数的参数。 命令行可以写成以下形式:file1 China Beijing

  • file1:可执行文件名,文件名应包括盘符、路径,今为简化起见,用file1来代替
  • China和Beijing是调用main函数时的实参

int main(int argc, char *argv[]);

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

什么是内存的动态分配

  • 第7章介绍过全局变量和局部变量,全局变量是分配在内存中的静态存储区的,非静态的局部变量(包括形参)是分配在内存中的动态存储区的,这个存储区是一个称为(stack)的区域。
  • C语言还允许建立内存动态分配区域,以存放一些临时用的数据,这些数据不必在程序的声明部分定义,也不必等到函数结束时才释放,而是需要时随时开辟,不需要时随时释放。这些数据是临时存放在一个特别的自由存储区,称为区(heap)。可以根据需要,详细谈申请所需大小的空间。

怎样建立内存的动态分配

对内存的动态分配是通过系统提供的库函数来实现的,主要有 malloc, calloc, free, realloc这4个函数

  • 用 malloc 函数开辟动态存储区函数原型为:
void * malloc(unsigned int size)

作用是在内存的动态存储区中分配一个长度为size的连续空间。

  • 形参size的类型定义为无符号整型(不允许为负数)
  • 此函数的值(即“返回值”)是所分配区域的第一个字节的地址,或者说,此函数是一个指针型函数,返回的指针指向该分配域的第一个字节
malloc(100); // 开辟100字节的临时分配域,函数值为其第1个字节的地址

指针的基类型为void,即不指向任何类型的数据,只提供一个纯地址。如果此函数未能成功地执行(例如内存空间不足),则返回空指针(NULL)

  • 用 calloc 函数开辟动态存储区函数原型
void *calloc(unsigned n, unsigned size);

作用是内存的动态存储区中分配n个长度为size的连续空间,这个空间一般比较大,足以保存一个数组

用calloc函数可以为一维数组开辟动态存储空间,n为数组元素个数,每个元素长度为size,这就是动态数组。函数返回指向所分配域的第一个字节的指针;如果分配不成功,返回NULL

p = calloc(50, 4); // 开辟50 X 4个字节的临时分配域,把首地址赋给指针变量p
  • 用realloc函数重新分配动态存储区函数原型
void *realloc(void *p, unsigned int size);

如果已经通过 malloc 函数或 calloc 函数获得了动态空间,想改变其大小,可以用 realloc 函数重新分配

用realloc函数将p所指向的动态空间的大小改变为size。p的值不变。如果重新分配不成功,返回NULL

realloc(p, 50); // 将p所指向的已分配的动态空间改为50字节
  • 用 free 函数释放动态存储区函数原型
void free(void * p);

作用是释放指针变量p所指向的动态空间,使这部分空间能重新被其他变量使用。p是最近一次调用 calloc 或 malloc 函数时得到的函数返回值

free(p); // 释放指针变量p所指向的已分配的动态空间

free 函数无返回值

以上4个函数的声明在 stdlib.h 头文件中

void指针类型

基类型为 void 的指针类型:定义一个基类型为 void 的指针变量(即void * 型变量),它不指向任何类型的数据

有关指针的小结

  • 准确理解指针的含义:指针就是地址

    • &n是变量a的地址,也可称为变量a的指针
    • 指针变量是存放地址的变量,也可以说,指针变量是存放指针的变量
    • 指针变量的值是一个地址,也可以说,指针变量的值是一个指针
    • 指针变量也可称为地址变量,它的值是地址
    • & 是取地址运算符,&a是a的地址,也可以说,&是取指针运算符。 &a是变量a的指针(即指向变量 a 的指针)
    • 数组名是一个地址,是数组首元素的地址,也可以说, 数组名是一个指针,是数组首元素的指针
    • 函数名是一个指针(指向函数代码区的首字节),也可以说函数名是一个地址(函数代码区首字节的地址)
    • 函数的实参如果是数组名,传递给形参的是一个地址,也可以说,传递给形参的是一个指针
  • 在C语言中,所有的数据都是有类型的,而在C语言中,所有数据都要存储在内存的存储单元中 对地址而言,也是同样的,它也有类型

    • 首先,指针型存储单元是专门用来存放地址的,指针型数据的存储形式就是地址的存储形式
    • 其次,它不是一个简单的纯地址,还有一个指向的问题,也就是说它指向的那种类型的数据。如果没有这个信息,是无法通过地址存取存储单元中的数据的。所以,一个地址型的数据实际上包含3个信息
      • 表示内存编号的纯地址
      • 它本身的类型,即指针类型
      • 以它为标识的存储单元中存放的是什么类型的数据,即基类型
  • 区别指针和指针变量。指针就是地址,而指针变量是用来存放地址的变量

  • 什么叫“指向”? 地址就意味着指向,因为通过地址能找到具有该地址的对象。对于指针变量来说,把谁的地址存放在指针变量中,就说此指针变量指向谁。但应主要:并不是任何类型数据的地址都可以存放在同一个指针变量中的,只有与指针变量的基类型相同的数据的地址才能存放在相应的指针变量中。

    void * 指针是一种特殊的指针,不指向任何类型的数据。如果需要用此地址指向某类型的数据,应先对地址进行类型转换。可以在程序中进行显式的类型转换,也可以由编译系统自动进行隐式转换。

  • 要深入掌握在对数组的操作中正确地使用指针,搞清楚指针的指向。一维数组名代表数组首元素的地址。

  • 有关指针变量的归纳比较

image.png

  • 指针运算
    • 指针变量加(减)一个整数
    • 指针变量赋值
    • 两个指针变量可以相减
    • 两个指针变量比较
  • 指针变量可以有空值,即该指针变量不指向任何变量,可以这样表示:
p = NULL;
// NULL是一个符号常量,代表整数0

使用指针的优点

  • 提高程序效率
  • 在调用函数时当指针指向的变量的值改变时,这些值能够为主调函数使用,即可以从函数调用得到多个可改变的值
  • 可以实现动态存储分配

  • 指针是什么
  • 指针变量
  • 通过指针引用数组
  • 通过指针引用字符串
  • 指向函数的指针
  • 返回指针值的函数
  • 指针数组和多重指针
  • 动态内存分配与指向它的指针变量
  • 有关指针的小结