2020C语言--指针、结构体、文件之指针

一、指针

想看数据结构的请翻看之前的博客,图文并茂以2020王道为基础进行的总结。

1.指针是什么?

  • 通过地址可以找到所需的变量单元,称地址指向该变量单元比如:一个房间门口挂着房间号2008,这个2008就是该房间的地址,或者说,2008”指向“该房间。因此,将地址形象化地称为“指针”。意思是通过它能找到以它为地址地内存单元。
  • 直接按照变量名称进行访问,称为“直接访问 ”方式。
  • 还有就是“间接访问”,将变量i地值放在另一个变量之中,然后通过该变量来找到变量i地地址,从而访问i变量。
  • 指向就是通过地址来体现。
  • 一个变量地地址称为该变量地“指针”;如果有一个变量专门用来存放另一个变量地地址(即指针),则称它为**“指针变量”**;指针变量地值是地址
  • 例如:可以说变量i地指针是2000,而不能说i的指针变量是2000。

指针式一个地址,而指针变量是存放地址的变量。

牢记上面这句话。

2.指针变量

2.1 使用指着变量的简单例子

  • 程序例题
    int *p1,*p2; p1=&a;p2=&b;
    在开头定义了两个指针变量p1和p2,但此时未指向任何变量,只提供两个指针变量,后面将其分别指向a和b,其中存放的就是a和b的地址。

2.2 怎样定义指针变量

类型名 * 指针变量名
如:int *point;
左端的int是基类型。指针变量的基类型用来指定此指针变量可以指向的变量的类型。

  • 注意:
    (1)指针变量前面的“”表示该变量的类型为指针型变量。指针名是p1和p2。
    (2)在定义指针变量时必须指定基类型
    (3)一个变量的指针的含义包括两个方面:一是存储单元编号表示地址,一是它指向的存储单元的数据类型(如int,char,float)
    (4)指向整型数据的指针类型为”int
    “,读作“指向int的指针”或简称“int指针”。
    (5)指针变量中只能存放地址(指针),不要将一个整数赋给一个指针变量;如:* p1 = 100;是不合法的。

2.3 怎样引用指针变量

  • 在引用指针变量时,可能有三种情况:
    (1)给指针变量赋值。如:p=&a;指针变量p的值是变量a的地址,p指向a。
    (2)引用指针变量指向的变量。如果执行“p = &a”,即指针变量p指向了整型变量a,则
    printf(“%d”,*p);其作用是将以整型形式输出指针变量p所指的变量的值,即变量a的值。

    • 如有以下语句:p = 1;表示将1赋值给p所指向的变量。

    (3)引用指针变量的值,如:printf(“%o”,p);作用是以8进制的形式输出指针变量p的值,如果p指向a,就是输出了a的地址。

  • 熟练掌握两个有关运算符:
    (1)& 取地址运算符。&a是变量a的地址。
    (2)* 指针运算符(或称为“间接访问”运算符),*p代表指针变量p指向的对象。

2.4 指针变量作为函数参数

  • 函数的参数不仅可以是一些整型和浮点型的数据。还可以是指针类型的数据。它的作用是将一个变量的地址传送到另一个函数中。
  • 重点重点重点:函数的形参在调用结束后会被销毁(或称为空间释放)

例:有下面这样一段代码:void swap(int x,int y){交换过程},在主函数中调用swap(a,b),在函数调用时,将a的值给了x,b的值给了y,在swap函数中对x和y进行了交换,调用结束释放x和y的空间,所以本质上并未改变实参a和b的值。也就是说由于”单向传递“的”值传递方式“,形参值得改变不能使实参得值随之改变,实参与形参之间的值传递是单项的。

  • 用指针变量做函数参数时,不可能通过执行调用函数来改变实参指针变量的值,但是可以改变实参指针变量所指变量的值。

3.通过指针引用数组

3.1 数组元素的指针

  • 所谓数组元素的指针就是数组元素的地址。
  • 可以使一个指针变量指向一个数组元素。例如:int a[10]={1,2,3,4,5,6,7,8,9,0};int * p; p= & a[0];
  • 引用数组元素可以使用下表法(如a[3]),也可以使用指针法。这里使用指针法能使目标程序质量高(占内存少,运行速度快)。
  • 在c中数组名代表数组首元素的地址p = &a[0]与p = a等价
    注:数组名不代表整个数组,只代表数组首元素的地址。上述”p=a;“的作用使”把a数组的首元素的地址赋值给指针变量p“,而不是”把数组a的各个元素赋给p“

3.2 在引用数组时指针的运算

  • **在一定条件下可以对指针进行加和减的运算。**一定条件:在指针指向数组元素的时候。

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

    • 加一个整数(+ 或 +=),P+1
    • 减一个整数(- 或-=),P-1
    • 自加运算,p++,++p
    • 自减运算,p–,--p
    • 两个指针相减,如p1-p2(只有p1和p2都指向同一数组中的元素时才有意义)。

    分别说明如下:
    (1)如果指针变量p已指向数组中的一个元素,则p+1指向同一数组的下一个元素,**p-1指向同一个数组中的上一个元素。**在执行p+1时并不是简单的将p的值+1,而是加上一个数组元素所占用的字节数。
    (2)如果p的初值为&a[0],则p+i和a+i就是数组元素a[i]的地址,或者说,他们指向a数组序号为i的元素。
    (3)*(p+i)或 (a+i)时p+i或a+i所指向的数组元素,即a[i],按照数组首元素的地址加上相对位移量得到要找到的元素的地址,然后找到该单元中的内容。若a首地址为1000,则a[3]的地址计算方法为:1000+34 = 1012。[ ]实际上是变址运算符。
    (4)若p2,p1指向同一个数组,如执行p2-p1,结果是两个指针变量中地址之址的差除以数组元素的长度。表示两个指针之间相差几个元素,可以得知两个指针变量的相对距离。
    注:两个地址不可以相加,没有意义。

3.3 通过指针引用数组元素

  • 根据以上叙述,引用一个数组,可以用以下两种方法:
    (1)下标法:如a[i]形式。
    (2)指针法:如*(a+i)或*(p+i),其中a是数组名,p是指向数组元素的指针变量,其初值p = a。
  • 在运算过程中可以使用p变化来进行操作,但是不可以使用数组名a变化,因为a代表数组首元素的地址,它是一个指针常量,其值在运行期间是固定不变的。a++无法实现,但是可以使用(a+10)来当循环的判断条件(因为并未改变a的值)。
  • 指向数组的指针变量也可以带下标,如p[i],在编译时,对下标的处理方法是转换为地址的,对p[i]处理成*(p+i),必须弄清楚p当前所指的是什么?如p指向a[3],则p[2]表示a[3+2]。
  • *p++:由于 和++同优先级,结合方向为自右向左,因此它等价于(p++),先引用值然后进行++操作。
  • p指向a[0],(p++)和(++p)分别表示,a[0]和a[1]的值。
  • ++(*p),若p指向a,a[0] = 3,在经过运算之后,a[0] = 4。

3.4 用数组名做函数参数

  • 当用数组名作参数时,如果形参数组中各元素的值发生了变化,实参数组中的元素的值随之变化。
    意思就是说,C在处理数组时是把它当作指针来看的,即形参虽然是数组名,但是arr表示的是一个指针,将实参传进来时,就把arr指向了实参数组的首元素的地址,所以操作形参arr就是在操作实参数组。
  • 实参数组名代表一个固定的值,是一个常量,但形参数组名并不是一个固定的地址,而是按照指针变量处理。
  • 如果使用指针变量作实参,必须先使指针变量有确定值,指向一个已定义的对象。

3.5 通过指针引用多维数组

  • int a[3][4] = {{1,2,3,4},{9,11,13,15},{17,19,21,22}};,此时a代表的是首行(即序号为0的行)的首地址。a+1代表序号为1的行的首地址。
  • 表示二维数组中列元素的值,采用a[0]+1来表示,等价于*(a+0)+1, * (* (a+0)+1)表示取a[0][1]的地址。
  • C语言教材P247表。
  • 二维数组名(如a)是指向行的。a+1中的1代表一行中全部元素所占的字节。一维数组名a[0]是指向列的,a[0]+1中的1代表一个a元素所占的字节数。在指向行的指针前面加一个*,就转换为指向列向量的指针。反之在指向列元素的前面加&,就成为指向行的指针
  • 计算a[i][j]在数组中的相对位置的计算公式是:i*m+j,m为二维数组的列数。
  • int (*p)[4]表示指向有4个整型元素的一维数组。

4.通过指针引用字符串

4.1 字符串的引用方式

  • char *String 表示指向字符数组的第一个字符。
  • 如果想把一个字符串从一个函数传递到另一个函数。可以使用地址传递的方法,用字符数组做参数名,也可以使用字符指针变量作参数。在被调用函数中可以改变字符转的内容,在主调函数中可以引用改变后的字符串。

4.2 字符指针做函数参数

4.3 使用字符指针变量和字符数组的比较

  • 字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串第一个字符的地址)。
  • 赋值方式。可以对字符指针变量赋值,但不能对数组名赋值。
  • 数组可以在定义时对各个元素赋初值,但不能用赋值语句对字符数组中的全部元素整体赋值。
  • 编译时为字符数组分配若干存储单元,而对字符指针变量,只分配一个存储单元。
  • 指针变量的指是可以改变的,而数组名代表一个固定的值,不能改变。
  • 使用字符数组时,只能采用在定义数组时初始化或逐个对元素赋值的方法,而不能用赋值语句对数组整体赋值。

5.指向函数的指针

  • 在程序中定义一个函数,在编译时,编译系统为函数代码分配了一段存储空间,这段存储空间的起始地址(入口地址)称为这个函数的指针。
  • int(*p)(int,int)定义p是一个指向函数的指针变量。
  • 类型名 (*指针变量名)(函数参数列表).
  • 在使用时只需用(*p)即可。
  • 指向函数的指针变量的一个重要用途是把函数的地址作为参数传递到函数,

6.返回指针值的函数

  • 只是其返回的值的类型是指针类型而已。
  • 定义返回指针值的函数的一般形式为:*类型名 函数名(参数列表)

7.指针数组和多重指针

7.1 什么是指针数组

  • 一个数组其元素均为指针类型数据,称为指针数组,指针数组中的每一个元素都存放一个地址,相当于一个字这跟变量。
  • 定义一位指针数组的一般形式:*类型名 数组名[数组长度]
  • 指着数组比较适合用来指向若干个字符串,使字符串处理更加方便灵活。

7.2 指向指针数据的指针

  • a是一个指针数组,它的每一个元素是一个指针型的变量,其值为地址。设置一个指针p 用来指向a,p就是一个指向指针型数据的指针变量。
  • 定义:char **p。

7.3 指针数组做main函数的参数

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

8.1 什么是内存的动态分配

  • 全局变量是分配在内存中的静态存储区的,非静态的局部变量(包括形参)是分配内存中的动态存储区的,这个存储区称为栈;除此之外c还有动态内存分配去,来存放一些临时数据,随时需要随时开辟。存储在一个自由区叫堆。

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

  • 使用malloc函数 void *malloc(unsigned int size);
  • 使用colloc函数 void *colloc(unsigned n,unsigned size),动态数组,n为数组元素,size为数组元素大小。
  • 使用free void free(void *p);
  • 使用realloc void realloc(void *p,unsigned int size),,将p所指的动态空间的大小改为size,p值不变。若重新分配不成功,返回NULL。
  • 以上称为无类型指针。

8.3 void指针类型

9.总结


版权声明:本文为li_jeremy原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。