C语言中的指针和数组

引言

  1. 由于我目前较少接触指针数组这部分的内容,所以本文中这部分内容写的不是很多,以后有时间再仔细学习一下这部分的内容。
  2. 本文所写的是C语言中指针和数组部分的基础内容,由于学习数据结构(C语言版)要经常用到该部分的内容,所以整理了一下相关的知识点。文中都是一些较基础的知识点,如要深入学习这部分的内容,建议去看相关书籍和教学视频。

知识点思维导图

在这里插入图片描述

指针和一维数组

  1. 原理:
    1)数组名代表数组的首地址,即元素 a[0] 的地址(&a[0] )。
    2)表达式 a+i 代表数组中下标为 i 的元素 a[i] 的地址。
    3)*(a+i) 表示第 i 个元素的内容。

  2. 指针运算注意事项:
    1)*p++=a[ i++ ](地址加一,指向下一个元素);
    2)(*p)++ = (a[ i ]=a[ i ] +1)(值加一,指向的元素自增);

    3)*(++p) = a[ ++i ](地址加一)。

    4)p++ 加的是 1*sizeof(基类型) 个字节。

    5)链式结构一定不能用 p++ ,应该用 p = p->next。

指针和二维数组

  1. 原理:例如对于数组 a[3][4] , 首先可将二维数组 a 看成由 a[0]、a[1]、a[2] 三个元素组成的一维数组,a 是它的数组名。再将 a[0]、a[1]、a[2] 三个元素分别看成是由4个整型元素组成的一维数组的数组名。

  2. 对于二维数组 a[2][3] :a+1 表示由 a[0][0] 变为 a[1][0] (跨4×sizeof(int) 个字节;a[i]+1 表示由 a[i][0] 变为 a[i][1] (跨1×sizeof(int) 个字节。

  3. 二维数组 a[ i ] [ j ] 4种等价的表示形式: a[ i ] [ j ] ,*( a[ i ]+j ) ,*(*a+i )+j ), (*(a+i) ) [ j ]。

  4. 行指针:
    1)定义:

    int (*p)[4];
    

    注:[ ] 中的4表示指针所指一维数组的长度,不可省略。p 可作 为一个指向二维数组的行指针,它所指向的二维数组的每一行有4 个元素。

    2)初始化:

    p=a;
    //或者如下: 
    p=&a[0];
    

    3)行指针引用二维数组的四种方式:p[ i ] [ j ] ,*( p[ i ]+j ) ,*(*p+i )+j ), (*(p+i) ) [ j ]。

  5. 列指针:
    1)定义:

    int *p;
    

    列指针所指向的数据类型为二维数组的元素类型,故其和指向同类型的简单变量的指针的定义方法是一样的。

    2)初始化:

    p=a[0];
    //或者如下:
    p=*a;
    //或者如下:
    p=&a[0][0];
    

    3)由于 p 代表数组的第0行第0列地址,而从数组的第0行第0列寻址到数组的第 i 行第 j 列,中间要跳过 i*n+j 个元素,因此「 p+ i*n+j 」代表数组的第 i 行第 j 列地址,即 &a[ i ] [ j ] ,*( p+ i*n+j ) 或 p[ i*n+j ]都表示 a[ i ] [ j ] 。
    4)此时不能用 p[ i ][ j ] 来表示数组元素,因为此时并未将这个数组看成二维数组,而是将二维数组等同于一维数组看待。

指针数组

  1. 定义:由若干基类型相同的指针所构成的数组,称为指针数组。
  2. 主要用途:对多个字符串进行处理。
  3. 用法:
    int i,n;
    char name[N][MAX_LEN];//定义二维字符数组
    char *pStr[N];//定义字符指针数组
    for(i=0;i<n;i++)
    {
    	pStr[i]=name[i];//让 pStr[i] 指向二维字符数组 name 的第 i 行
    	gets( pStr[i] );//输入第 i 个字符串到 pStr[i] 指向的内存
    }
    //代码块
    for(i=0;i<n;i++)//输出所有(n个)字符指针所指的字符串
    {
    	puts(pStr[i]);
    }
    

动态数组

  1. 用动态内存分配函数来申请的内存都是从堆上分配来的。

  2. 函数 malloc()
    1)用于分配若干字节的内存空间,返回一个指向该内存首地址的指针。
    2)函数原型: void *malloc( unsigned int size );
    3)用法:

    //申请10个连续的 float 型存储单元,并用指针 pf 指向其首地址
    float *pf = NULL;
    pf = (float*)malloc(10*sizeof(float));
    
  3. 函数 calloc():
    1)用于给若干同一类型的数据项分配连续的存储空间,并为其赋值为0。
    2)函数原型: void *calloc( unsigned int num, unsigned int size );
    3)用法:

    pf = (float*)calloc(10, sizeof(float));
    

    4)注意事项:从安全角度讲,使用 calloc() 更好,因为它能自动将分配的内存初始化为0。

  4. 函数 realloc():
    1)用于改变原存储空间的大小,返回新分配的存储空间的首地址(与原来的首地址不一定相同)。
    2)函数原型: void *realloc( void *p, unsigned int size )
    3)用法:

    //改变指针pf的存储空间大小
    float *q = NULL;
    q = (float*)realloc(pf,100*sizeof(float));
    

    4)注意事项:使用时应保留原指针,避免分配失败产生内存泄露。

  5. 函数 free():
    1)用于释放向系统动态申请的由指针 p 指向的存储空间。
    2)函数原型: void free( void *p );
    3)用法:

    //释放指针 pf 指向的动态申请的存储空间
    free(pf);
    
  6. 长度可变的一维动态数组( p[n] )

    int *p = NULL, n;
    scanf("%d", &n);//数组长度 n 可变
    p = ( int* ) malloc ( n*sizeof ( int ));//p相当于数组名
    if ( p == NULL )//看是否分配成功
    {
    	printf("内存不足!\n");
    	exit( 1 );
    }
    //代码块
    free( p );//释放内存
    
  7. 长度可变的二维动态数组( p[m] [n] )

    int *p = NULL, m, n;
    scanf("%d%d", &m, &n);//数组的行数和列数可变
    p = ( int* ) calloc ( m*n, sizeof(int) );//p相当于数组名
    if ( p == NULL )//看是否分配成功
    {
    	printf("内存不足!\n");
    	exit( 1 );
    }
    //代码块
    free( p );//释放内存
    

    注意:用该方法建立的二维数组,在通过指针 p 来寻址数组元素时,必须将其当做一个一维数组来处理,只能使用 *(p+i*n+j) 或 p[ i*n+j ] ;来表示数组元素的值。

注意事项与易错点

  1. 数组和指针并非在所有情况下都是等同的。例如, sizeof (数组名)表示数组的大小( n×基类型),而 sizeof (指针变量名) 表示基类型大小(1×基类型)。
  2. 能用自动变量解决的问题,就不要用动态数组(能少用就少用)。
  3. 若多次使用了动态内存分配函数,释放存储空间时一定要注意释放存储空间的次序,并要在恰当的位置处释放存储空间(比如 free() 语句位置不合理,致使其未执行,从而导致内存泄露)。
  4. 谨防「野指针」。指向垃圾内存的指针指针被称为野指针。产生野指针的主要原因就是释放了内存却仍然使用它(比如用指针指向局部变量的地址)。

参考资料:
[1] 苏小红. C语言程序设计(第4版). 北京:高等教育出版社,2020.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

沉远

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

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

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

打赏作者

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

抵扣说明:

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

余额充值