一、指针与数组

(一)数组名

  1. 数组名是一个地址常量,不能进行++、–操作,但是可以"数组名+i"
  2. 获取整个数组的地址:&数组名
  3. 数组名是数组的首个元素的地址

(二)数组+/-运算

1、数组地址

int arr[3]={11,22,33};

arr:数组名,数组的首元素地址,是一个地址常量

arr[0]:数组的第0个元素,对应的值是一个变量

&arr:数组的整个地址,值为首元素地址

&arr[0]:数组首个元素的地址

2、加减运算

int arr[3]={11,22,33};

arr+1:数组名arr是首个元素的地址,+1移动一个元素,4个字节

&arr[0]+1:&arr[0]是数组首个元素的地址,+1移动一个元素,4个字节

&arr+1:移动了整个数组的大小,12个字节

arr[0]+1:arr[0]里面的数值+1,值为12

*(arr+0):获取arr[0]地址中的值,值为11

*(arr+1):获取arr[1]地址中的值,值为22

  • 注:[ ] 下标运算符
  • arr[i] == *(arr + i) //等价关系

二、二维数组与指针

(一)数组地址

逻辑结构
C语言基础(六)数组 指针 二级指针 数组指针 指针数组-LMLPHP

int arr\[3]\[2] = {{11,22},{33,44},{55,66}};

arr:二维数组的数组名称,也是首元素地址(首个元素为嵌套内层的数组)

arr[0]:第0行首个元素的首地址,相当于内嵌的第0个数组的数组名

arr[0][0]:二维数组的第0个元素

&arr[0][0]:二维数组第0个元素的地址

&arr[0]:二维数组第0行地址

&arr:取到整个数组的地址,它的地址

(二)加减运算

arr+1:移动二维数组一行的大小,可以将arr理解为是数组类型元素的数组,+1就是移动第一个元素,即第一个内套数组的大小

arr[0] +1:arr[0]相当于第一个内层数组的数组名,+1移动4个字节

&arr[0][0] + 1:移动4个字节大小

&arr[0] + 1:移动8个字节大小

&arr + 1:移动整个二维数组的大小,移动24个字节的大小

eg: 单层嵌套遍历整个二维数组

#include <stdio.h>


int main(int argc, const char *argv[])
{
    int arr[3][2]={{11,22},{33,44},{55,66}};

    int len=sizeof(arr)/sizeof(arr[0])*sizeof(arr[0])/sizeof(arr[0][0]);

    int *p= (int *)arr; //将二维数组强制类型转换为一维数组

    for(int i=0;i<len;i++)
    {   
        printf("%d ",*(p+i));
    }   

    putchar(10);
                                                                                                                                                               
    return 0;
}

三、数组名传参

数组名作为函数参数进行传递时,会降维成为指针

(一)遍历二维数组

法一:下标

    for(int i=0;i<line;i++)
    {   
        for(int j=0;j<col;j++)
            printf("%d ",arr[i][j]);
        putchar(10);
    }  

法二:

    for(int i=0;i<line;i++)
    {   
        for(int j=0;j<col;j++)
            printf("%d ",(*(arr+i))[j]);                                                                                                                     
        putchar(10);
    }   

法三:

    for(int i=0;i<line;i++)
    {
        for(int j=0;j<col;j++)
            printf("%d ",*(arr[i]+j));                                                                                                                       
        putchar(10);
    }

法四:

    for(int i=0;i<line;i++)
    {
        for(int j=0;j<col;j++)
            printf("%d ",*((*(arr+i))+j));                                                                                                                   
        putchar(10);
    }

法五:

    int *p=(int *)arr;                                                                                                                                         
    for(int i=0;i<line*col;i++)
    {
        printf("%d ",*(p+i));
        putchar(10);
    }

法六:

   int *k=&arr[0][0];
    for(int i=0;i<line*col;i++)
    {   
        printf("%d ",p[i]);                                                                                                                                  
        putchar(10);
    }   
    return 0;

四、二级指针

(一)定义

1. 格式

(1)二级指针是指向一级指针的地址

(2)定义时需要使用**,但是只起到标识作用,p才是指针

(3)二级指针在内存中,如果是32位计算机占用4个字节;如果是64位计算机占用8个字节。

(4)二级指针+1,即移动一个一级指针的大小个数,即如果是32位计算机移动4个字节;如果是64位计算机移动8个字节。

(5)二级指针的数据类型是 int**

eg: 通过二级指针访问变量

#include <stdio.h>


int main(int argc, const char *argv[])
{
    int number = 10; 
                                                                                                                                                               
    int *p = &number;
    int **q = &p; 
//通过二级指针读变量的值
    printf("%d\n",**q);
//通过二级指针写变量
	**q = 200;
	printf("%d\n",**q);
    return 0;
}

输出结果:
C语言基础(六)数组 指针 二级指针 数组指针 指针数组-LMLPHP

(二)二级指针传参

eg: 分配一块内存空间,并将分配的空间的首地址传递给指针p

#include <stdio.h>
#include <stdlib.h>

void memory(int **p)
{
    *p = (int *)malloc(128); //malloc函数申请空间完毕,会返回申请的空间的首地址
}
int main(int argc, const char *argv[])
{
    int *p = NULL;

    memory(&p);
    printf("%p\n",p);
                                                                                                                                                               
    return 0;
}

输出结果:
C语言基础(六)数组 指针 二级指针 数组指针 指针数组-LMLPHP

  • 注:为什么memory函数传递参数是int**,而非int*类型?
  • 析:假设memory函数中传递的参数是int* 类型的参数,p也是int*类型,当调用该函数时,“memory§;”,此时在memory中的操作均是对形参的操作,对实参p没有影响,则达不到向p中写入地址的目的。
  • 而如果传递int**类型的参数,则需要传入p指针的地址,即"memory(&p);",此时再对*p进行操作,则可以改变p的值。

(三)多级指针

int a; //变量
int *p = &a; //一级指针存放变量的地址
int **pp = &p; //二级指针存放一级指针的地址
int ***ppp = &pp; //三级指针存放二级指针的地址

五、数组指针

(一)概念

数组指针,本质是一个指针,指针指向的是一个数组。

(二)练习

eg: 数组指针

#include <stdio.h>


int main(int argc, const char *argv[])
{
    int arr[5]={1,2,3,4,5};

    int (*p)[5]=&arr; //&arr是整个数组的地址

    for(int i=0;i<5;i++)
    {   
        printf("%d ",(*p)[i]);
    }                                                                                                                                                          
    putchar(10);
    return 0;
}

eg: 使用数组指针遍历二维数组

#include <stdio.h>


int main(int argc, const char *argv[])
{
    int arr[2][2]={{1,2},{3,4}};

    int (*p)[2]=arr; //arr就是第一个元素的首地址,arr是int [2]类型

    for(int i=0;i<2;i++)
    {   
        for (int j=0;j<2;j++)
            printf("%d ",(*(p+i))[j]);
    }   
    putchar(10);
    return 0;
}    

eg: 定义一个函数,找到数组中的最大值和最小值

#include <stdio.h>


void function(int (*a)[2],int line,int col,int *max,int *min)
{//此处传参必须写"int (*a)[2]",不能写"int (*a)[]",会报错
    *max = (*a)[0];
    *min = a[0][0];//两种方式

    for(int i=0;i<line;i++)                                                                                                                                                           
    {   for(int j=0;j<col;j++)
        {
            if(((*(a+i))[j]) > (*max))
                *max = (*(a+i))[j];
            if(a[i][j] < (*min))
                *min = (*(a+i))[j];
        }
    }   
}
int main(int argc, const char *argv[])
{
    int arr[3][2];

    for(int i=0;i<3;i++)
    {   
        for (int j=0;j<2;j++)
            scanf("%d ",&arr[i][j]);
    }   

    int max=arr[0][0];
    int min=arr[0][0];
    function(arr,3,2,&max,&min);

    printf("max:%d;min:%d\n",max,min);
    return 0;
}

六、指针数组

(一)概念

指针数组本质上是一个数组,这个数组中存放的元素的数据类型是指针。

(二)练习

eg: 通过指针数组遍历多个变量

#include <stdio.h>


int main(int argc, const char *argv[])
{
    int a=100;
    int b=200;
    int c=300;

    int *pa = &a; 
    int *pb = &b; 
    int *pc = &c; 

    int *arr[3] ={pa,pb,pc};

    for(int i=0;i<3;i++)
    {   
        printf("%d ",*arr[i]);
    }   
    putchar(10);                                                                                                                                                                      
    return 0;
} 

(三)特殊的字符串定义方法

char *str="Hello world";
指针指向常量区的一个值

  • 注意:此时指针指向的是常量区的值,不允许更改

char *arr[3]={"Hello world","hello python","hello baidu"};
eg: 使用上述方法定义一个字符数组,并打印出来

#include <stdio.h>


int main(int argc, const char *argv[])
{
    char *s = "this is a string";
    char *arr[3]={"Hello world","I love C","Work hard"};

    printf("%s\n",s);                                                                                                                                                                 
    for(int i=0;i<3;i++)
    {   
        printf("%p:%s\n",arr[i],arr[i]);
        printf("%d:%c\n",i,*arr[i]);
    }   
    return 0;
}

输出结果:
C语言基础(六)数组 指针 二级指针 数组指针 指针数组-LMLPHP

  • 注:
    1. arr中存放的是指针,指针存放的是各个字符串的首个字符的地址
    1. 当输出格式是%p时,打印出指针存放的地址
    1. 当输出格式是%s时,与之对应的参数是一个指向字符串的指针,打印时找到指针指向的空间的值,一直打印直到遇到’\0’
    1. *arr[i] 取出的是指针指向的空间的值;如果此时使用%s,会将取到的值当作一个指针去寻址,然后到该地址去取得值。编译时会报警告,运行时程序会崩溃。现象如下:
  • C语言基础(六)数组 指针 二级指针 数组指针 指针数组-LMLPHP

七、练习

有一个二维数组 int a[5][4],二维数组的首地址是100,int (*p)[4] = a,问 *(p+2)+3是多少?
C语言基础(六)数组 指针 二级指针 数组指针 指针数组-LMLPHP

答案:144

#include <stdio.h>


int main(int argc, const char *argv[])
{
    int arr[5][4];

    printf("%p\n",arr[0]);

    int (*p)[4]=arr;

    printf("%p\n",*(p+2)+3);
    
    printf("%#u\n",(*(p+2)+3)-(arr[0]));
    //相差多少个元素大小
    printf("%#u\n",(char)(*(p+2)+3)-(char)(arr[0]));
    //强制类型转换为char,相差多少个字节
    return 0;
}  
05-07 21:52