第八章:指针

本文详细介绍了C语言中指针的概念、地址访问方式、指针变量的定义、赋值、运算、数组与指针的交互、字符串与指针、函数与指针的使用,以及动态内存分配函数malloc(),calloc(),free()和指针数组、二级指针在命令行参数中的应用。
摘要由CSDN通过智能技术生成

第八章:指针

指针重要性:

  • 数据库–>动态分配内存
  • 数据结构–>链表、队列、树、图等
  • 操作系统–>改善子程序的效率
  • 指针为函数提供修改变量值的手段

8.1-地址和指针

变量的地址
  • 计算机中,数据存储在内存中

  • 内存:是内部存储器,由存储单元组成的。内存可划分为若干存储单元,每个单元存放8位二进制数,即一个字节,其中存放的数据称为内存单元的内容。

  • 内存单元采用线性地址编码,每个单元具有唯一一个地址编码。地址编码是无符号整数型,通常用十六进制数表示。地址一旦编号后固定不变,但存储内容是动态的,经常变化。
    在这里插入图片描述

  • C编译系统对程序中定义的变量,会根据变量的数据类型为其分配一定字节数并且连续的存储空间。分配的存储单元大小以及存储的数据格式由该变量的数据类型决定。

  • C和C++决定:存储某变量内存空间的首地址称为该变量的地址

short a=3;
char b='A';
float c=2.5;

系统为变量a分配了1000和1001这两个内存单元,1000是变量a的地址。

变量b分配1002这个内存单元,1002是变量b的地址。

变量c分配了1003、1004、1005和1006这4个内存单元,1003是变量c的地址。

如果要输出变量a的值,则先要找到a在内存中的地址

在这里插入图片描述

访问方式
直接访问方式
  • 直接根据变量名存取变量的值
chort a=3;
char b='A';
float c=2.5;
间接访问方式
  • 定义指针变量,将变量的地址存放在此变量中,当要对变量进行存取时先读取该变量的值,得到要存取变量的地址,再对该变量进行访问
    在这里插入图片描述
  • 易混淆概念

变量i:变量的值和变量的地址(指针)

指针:地址

指针变量:存放地址的变量
在这里插入图片描述

8.2-指针变量

指针变量的概念
  • 专门用来存放内存单元地址的特殊变量:指针变量
  • 指针变量中存放的是另一个有值变量的地址
  • 变量的指针就是变量的地址。一个指针变量一旦存放了某个变量的地址,该指针变量就指向了这个变量。
    在这里插入图片描述
int *pointer_1,*pointer_2;   //定义指向整型数据的指针变量pointer_1, pointer_2

pointer_1=&a;	  //把变量a的地址赋给指针变量pointer_1
pointer_2=&b;	 //把变量b的地址赋给指针变量pointer_2 

printf("a=%d,b=%d\n",a,b);	//输出变量a和b的值
printf("*pointer_1=%d,*pointer_2=%d\n",*pointer_1,*pointer_2);//用指针变量输出变量a和b的值
指针变量的定义
  • 一般形式

    数据类型 *指针变量名[=初始地址值];
    
    数据类型是指向的变量的数据类型
    *表示其后面的变量是指针变量
    
    eg:
    int *p1,*p2;
    char *p3;
    
  • 注意:指针变量只能存放与它数据类型相同的变量的地址

int *pointer_1;
指针变量名是pointer_1,而不是*pointer_1
指针变量的赋值
  • 用变量的地址给指针变量赋值(求地址运算符&)
int a,b,*p;
p=&a;
  • 用相同类型的指针变量赋值
int a,*p1,*p2;
p1=&a;
p2=p1
  • 赋空值NULL(0)
float *p;
p=NULL
p=0;
  • 说明

NULL是一个空指针,空值指针NULL是一个不指向任何存储单元的指针,表示该指针变量的值没有意义。作用是为了避免对没有被初始化的指针变量的非法引用。

指针变量的初始化
  • 赋空值NULL
  • 用已定义的变量的地址
1.int *p1=NULL;
2.float a,*pf=&a;
指针运算与指针变量的应用
  • 基本运算
  • 算数运算
  • 关系运算
  • 赋值运算
指针基本运算规则
  • &、*优先级别相同(2级),但为右结合性(单目运算符)
  • &、*使用说明:
int a=5,*p=&a;

则&*p含义:&*p-->&a,即p
则*&a含义:*&a-->*p,即a
则(*p)++含义:(*p)++ -->a++
*p++相当于*(p++),先得p所指向得变量的值,然后p+1,p不再指向a
指针的算术运算:指针增量的概念
  • 指针增量:指针变量是做++、–、+、-等运算,指针增量的运算不是单纯的算术运算,而是地址按数据个数增加所指的目标变量字节数
float  a[10]={0,1,2,3,4,5,6,7,8,9};
float *p=a;

此时p指向数组a的首地址,p++或p+1是p原有的值加上4个字节,若p原有地址值为2000,则p++或p+1的地址值是2004

即向后移动指针变量,使其指向后一个同类型变量
  • C语言地址运算规则规定:

    • 指针加减一个整数n,计算结果仍然是一个地址量,它是以运算符的地址量为基点,前方或后方第n个数据的地址
    Short int a[20],*p;
    p=&a[0];  /*指针p指向数组a的第一个元素*/
    p+=2;      /*移动指针p,使它指向数组a的第3个元素*/
    
    • 指针的算术运算只允许加减运算,不允许乘除及移位运算,不允许两个指针之间进行加运算,也不允许指针加减实型数据

    • 两个指针允许进行相减运算,结果是一个整数,表示这两个指针所指地址相差的数据个数

    short int *p1,*p2,a[10]= {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    p1=&a[8];
    p2=&a[2];
    p1-p2=6;     
    

在这里插入图片描述

指针的关系运算
  • 两指针之间的关系运算表示他们所指向的地址(位置)关系。指针的关系运算符包括:==,!=,<,<=,>,>=

  • 指向同一数据类型的指针,才有可能进行关系运算

指针变量作为函数的参数
  • 基本类型量作为函数参数时,被调函数内的多个结果值不能为主调函数所用

  • 指针变量作函数的参数,传递是变量的地址。通过间接访问方式修改函数外变量的值,可实现被调函数与主调函数间的多个数据共享。

8.3-通过指针引用数组

一维数组与指针
  • 数组的指针:数组的__首地址__

    • 一维数组的指针:一维数组的首地址,一维数组中第一个元素的地址
    • C语言规定:数组名代表数组的首地址
    int a[10]中,a与&a[0]等价
    
  • 数组元素的指针:数组元素的地址,如&a[i]

  • 指向一维数组元素的指针变量:存放一维数组首地址或数组元素地址的变量

int a[10],*p1,*p2;  //定义p1和p2为指向整型变量的指针变量
p1=&a[0];  //将数组的首地址赋给指针变量p1
p2=&a[2];  //将a[2]的地址赋给指针变量p2
  • 数组在内存中是一片连续的存储空间,通过指向数组的指针变量进行相加减一个整数的算数运算来移动,就可以访问数组中的其他数组元素
通过指针引用数组元素
  • 三种引用数组元素的方式

    • 指针法:

      *(p+i)和p[i]
      

在这里插入图片描述

设有定义float a[10], *p=a;

*p=1;表示对当前所指的数组元素赋值为1

p+1、a+1;表示同一数组中的下一个元素的地址;如float数组元素,p+1指p的值加4个字节,p+所代表的实际地址:p+1×d(d=4)

表示元素的地址可用a+i,p+i

a代表数组首地址,a+i也为地址,实际上为a+i×d

表示元素的内容用*(p+i),*(a+i),a[i];在编译时,对数组元素a[i]就是处理成*(a+i),即按数组首地址加上相对位移量得到要找的元素的地址(基地址+位移),然后找出该单元的内容
  • 下标法:

    a[i]
    
  • 地址法:

    *(a+i)
    

在这里插入图片描述

数组作为函数参数
  • 用数组元素作实参与用变量一样,值不会改变
void swap(int x, int y)
{
    int t;
    t=x; x=y; y=t;
} 

调用时,swap(a[1],a[2]);属于单向值传递方式
  • 数组名或数组指针作函数参数
f(int array[ ], int n)
{……    }

void main( )
{  
    int arr[10];
    f(arr,10);
         …… 
}

在这里插入图片描述

__注意:__当数组名作为函数的参数时,传递的是数组首地址,若形参中数组个元素的值发生变化,实参数组元素的值随之变化

8.4-字符串与指针

字符指针
  • C程序中,访问字符串有两种方法:字符数组和字符指针
  • 方法1:用字符数组存放一个字符串,然后输出该字符串
  • 方法2:用字符串指针指向一个字符串
#include <stdio.h>
void  main( )
{
    char string [ ]="Hello!";
    char *s = "hello";
    printf("(1):%s\n",string);
    printf("(2):%s\n",s);
}
字符指针作函数参数
  • 用字符数组名或字符串指针作参数。在被调函数中改变字符串的内容,在主调函数中可得到改变的字符串。
    在这里插入图片描述

8.5-函数与指针

  • 函数名表示该函数目标代码的存储首地址,即函数的入口地址
利用函数求两数之和
int add(int a,int b)
{    return a+b; }
void  main( )
{	 
    int x=add(2,4); 
	printf("x=%d\n",x);  
} 

函数指针
  • 定义一个指针变量,用来存储函数的起始地址,则此指针变量指向该函数,称为“指针函数”

  • 定义:

    int (*p)(int,int)
    
  • 用函数指针调用函数

    int add(int a,int b)
    {
        return a+b;
    }
    void main()
    {
        int x;
        int (*p)(int,int)  //函数指针定义
        p=add;  //函数指针赋值
        x=p(2,4)  //用指针变量调用函数
        printf("x=%d\n",x);
    }
    
  • 函数调用可通过三种形式调用

    • 函数名(实参表) add(2,4)
      
    • (*函数指针名)(实参表) (*p)
      
    • 函数指针名(实参表)  p(2,4)
      
  • (*p)() 表示定义一个函数指针变量,专门用来存放函数的入口地址,未赋值时,不固定指向哪一个函数

  • 在给函数指针变量赋值时,只需给出函数名,不需要参数 p=add;

  • **用函数指针变量调用函数时,用 ( *p )代替函数名,在( *p )后括号内写上实参 x=( *p )(2,4);

  • 对函数指针变量,p+n、p++、P-- 无意义

  • 函数指针和数据指针不同

    • 函数指针指向数据区,“*数据指针名"是访问该指针所指的数据
    • 函数指针指向程序代码区,“*函数指针名”使得程序控制转移到函数指针所指的函数目标代码模块首地址,执行该函数的函数体目标代码
用函数指针作函数参数
  • 把函数的入口地址作为参数传递到其他函数。指向函数的指针作为函数参数,以实现函数地址的传递,这样就能够在被调用的函数中使用实参函数
原理:
void fun(int (*x1)(int),int (*x2)(int,int))
{   int a, b, i, j;
    a=(*x1)(i);     /*调用f1函数*/  
    b=(*x2)(i,j);   /*调用f2函数*/ 
}    
调用时,fun(f1,f2)
将f1、f2的入口地址传给x1、x2 
返回指针值的函数
  • 把返回值为地址值的函数成为指针函数

  • 指针函数定义:

    int *p(int x,int y);
    
指针函数的应用(内存动态分配函数)
  • 动态内存分配函数:返回值为指针
  • C语言内存镜像

在这里插入图片描述

malloc()
  • 内存的动态存储区中分配一个长度为size的连续空间,形参size类型定为无符号整型(不允许出现符号)
  • 如果分配不成功,返回NULL
void *malloc(unsigned size);

eg:malloc(100)   //开辟100字节的临时分配域,函数值为其第一个字节的地址
calloc()
  • 内存的动态存储区中分配n个长度为size的连续空间,这个空间一般比较大,足以保存一个数组
  • calloc函数可以为一维数组开辟动态存储空间,n为数组元素个数,每个元素长度为size,即动态数组。如果分配不成功,返回NULL
void *calloc(unsigned n,unsigned size);`

eg:
p=calloc(50,4)   //开辟50×4个字节的临时分配域,把首地址赋给指针变量p
free()
  • 释放动态存储区
void free(void *ptr)

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

8.6-指针数组和多级指针

指针数组
  • 定义:如果数组中的每个元素都是指针类型数据,则这种数组称为指针数组。指针数组中的每一个数组元素相当于一个指针变量

  • 定义形式:

    数据类型 *数组名[常量表达式]
    
    int *ptr[5];
    

在这里插入图片描述

  • 指针数组初始化:

    数据类型  *数组名[常量表达式]={初值表};
    
    int a[3][2], *aptr[3]={a[0],a[1],a[3]};   //aptr是一维指针数组。aptr[0]指向a[0],aptr[1]指向a[1]
    
  • 指针数组的应用

    通过建立指针数组可以来引用二维数组元素 
    int a[3][2], *aptr[3],i,j;
    for(i=0; i<3; i++)
        aptr[i]=a[i];
    
指向指针的指针(二级指针)
  • 定义:只想指针数据的指针变量

  • 定义形式

    数据类型 **指针变量名
    
  • 说明

    • 如果指针变量的值是某一变量的地址,则称为为一级指针。如果指针变量的值是某一指针变量的地址,则称为二级指针。
    • 用指针访问另一个变量即为间接访问,一级指针中访问变量称为“单级间址”,二级指针中访问变量称为“二级间址”
      在这里插入图片描述
  • 示例

    int  i, *ptr, **pptr; 
    ptr=&i;
    pptr=&ptr;
    **pptr=2;
    
命令行参数(指针数组作main()函数的形参)
  • 作为main函数的形参**。**在程序执行时,通过命令行将参数传递给程序,以控制程序的执行,这就是命令行参数

  • void main()函数的形参格式为:
    void main(int argc,char *argv[]);
    
  • 两个特殊的内部形参argc、argv是用来接收命令行实参的,是只有main函数才有的参数。形参名习惯用argc、argv。

  • 19
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

蛰伏GR

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

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

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

打赏作者

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

抵扣说明:

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

余额充值