指针数组和数组指针


滴水石穿,非一日之功。

前言

我觉得和前面一篇就是兄弟文了。这篇就写指针数组,数组指针的一些杂七杂八的东西了。虽然写起来很头疼,但是好记性不如烂笔头啊!然后再让我感叹一下,中文真是博大精深~

指针数组

什么是指针数组

指针数组 : 数组的每一个元素是指针

指针数组:array of pointers,即用于存储指针的数组,也就是数组元素都是指针

指针数组怎么定义

类型* 数组名[数组长度]

int* p[n]  

[]优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素。

元素表示:*p[i] *(p[i])是一样的,因为[]优先级高于*

注意:在实际应用中,对于指针数组,我们经常这样使用:
typedef int* pInt;
pInt a[4];

使用

快速入门

如要将二维数组赋给一指针数组:

int *p[3];
int a[3][4];
for(i=0;i<3;i++){
p[i]=a[i];
p++; //该语句表示p数组指向下一个数组元素。注:此数组每一个元素都是一个指针
}

这里int *p[3] 表示一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2]
所以要分别赋值。

实现

/*
    指针数组的使用示例
    指针数组的概念——是一个数组,数组中的每个元素是一个指针。本例假设数组是一维的。(多维指针数组在实际中很少遇到)
    本例采用简单选择排序,对指针数组中的所有元素进行排序。

    需要提醒注意的是,本例的排序只是改变了指针的指向,并没改变常量字符串在程序常量区里面内存空间的位置和长度大小等!

*/
#include <stdio.h>

void sort(char *name[], int n);
void printf(char *name[], int n);

int main(void)
{
    /*
      定义了一个指针数组name,通过字符串赋初值的方式给name中的每个元素(一个指针)赋初值
      需要注意的是:此处这种赋初值的方式,字符串常量全都是存储在程序的常量区,并且是根据
      字符串的实际长度来申请的空间(此步骤由编译系统自动完成,无需程序员再去malloc)。另
      外,数组的长度,也是有编译器自动确定的。
      如果各个字符串的值事先不确定,需要程序运行时由用户来确定,则本程序仍然需要调用malloc
      函数去实现。
    */
    char *name[]={"Follow me", "BASIC", "Great Wall", "FORTRAN", "Computer design"};
    int n = 5;

    sort(name, n);
    printf(name, n);
    return 0;
}

/***************************************************************************************
    基于指针元素类型的数组的简单选择排序方法

    排序算法不变,只不过此处元素是指针,指向字符串,所以相互的比较需要借助strcmp函数来实现,
而不是直接使用>或<号
 **************************************************************************************/
void sort(char *name[], int n)
{
    char *temp;
    int i, j, k;

    for (i = 0; i < n - 1; i++)  //确定子序列的变化的左端
    {
        k = i;    //假定子序列的第一个元素是最大的
        for (j = i + 1; j < n; j++)
        {
            if (strcmp(name[k], name[j]) > 0)
            {
                k=j;   //若后续元素更大,则刷新最大元素下标k
            }
        }
        if (k != i)    //找到最大元素下标k的值之后,将最大值和第一个元素的值交换
        {
            temp = name[i];
            name[i] = name[k];
            name[k] = temp;
        }
    }
}

void printf(char *name[], int n)
{
    int i;

    for(i = 0; i < n; i++)
    {
        printf("%s\n", name[i]);
    }
}

图解

数组指针

什么是数组指针

数组指针 : 本质是指针(四个字节的变量) 指向数组的指针

数组指针:a pointer to an array,即指向数组的指针

数组指针怎么定义

类型说明符 (* 指针变量名) [数组长度]

int (*p)[n];

()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。

使用

快速入门

元素表示:(*p)[i]

如要将二维数组赋给一指针,应这样赋值:

int a[3][4];
int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。
 p=a;        //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
/*也可以p=&a[0][0];*/
 p++;       //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]

实现

图解

案例1(数组指针和指针数组的对比)
#include <iostream>

using namespace std;

int main()
{
int c[4]={1,2,3,4};
int *a[4]; //指针数组
int (*b)[4]; //数组指针
b=&c;
//将数组c中元素赋给数组a
for(int i=0;i<4;i++)
{
a[i]=&c[i];
}
//输出看下结果
cout<<*a[1]<<endl; //输出2就对
cout<<(*b)[2]<<endl; //输出3就对
return 0;
}

注意:定义了数组指针,该指针指向这个数组的首地址,必须给指针指定一个地址,容易犯的错得就是,不给b地址,直接用(*b)[i]=c[i]给数组b中元素赋值,这时数组指针不知道指向哪里,调试时可能没错,但运行时肯定出现问题,使用指针时要注意这个问题。但为什么a就不用给他地址呢,a的元素是指针,实际上for循环内已经给数组a中元素指定地址了。但若在for循环内写*a[i]=c[i],这同样会出问题。

案例2
#include <stdio.h>
int main()  
{  
    int a[2][2]={1,2,3,4};//这是一个2*2的二维数组  
    int (*p)[2];//数组指针  
    p=a;//令p指向数组a
    return 0;  
}
分析数组,指针变量在内存中的存放

a中各个元素在内存中的样子

现在我们思考a,a[0],a[1],p,a+1,a[0]+1,p+1到底是什么?

分析:

a是一个数组名,类型是指向一维数组的指针,不是变量,a的值是指针常量,即不能有a++或者a=p这些操作。a指向这块连续空间的首地址,值是&a[0][0]。

a[0]是一维数组名,类型是指向整型的指针,值是&a[0][0],这个值是一个常量。

a[1]是一维数组名,类型是指向整型的指针,值是&a[1][0],这个值是一个常量。

p是一个数组指针变量,指向一维数组的指针变量,值是&a[0][0]。可以执行p++;p=a等操作。

a+1表示指向下一行元素,也可以理解为指向下一个一维数组。

*(a+1)是取出第一行的首地址。

a[0]+1是指向第0行第1个元素,也可以理解为指向一维数组a[0]的第一个元素。

p+1同a+1

*(p+1)*(a+1)

虽然a跟a[0]值是一样,但类型不一样,表示的意义不一样。通过分析就不难理解为什么*(*(a+i)+j)和a[i][j]等效了。

其他

1.数组名和数组指针变量的长度

案例1

#include<stdio.h>  
int  main()  
{  
    int a[2][2]={1,2,3,4};//这是一个2*2的二维数组  
    int (*p)[2];//数组指针  
    p=a;//令p指向数组a  
    printf("%d\n%d\n",sizeof a,sizeof p); 
    return 0;
}

输出:

16
4

p是一个指针变量,这个变量占用四个字节。而a是数组名,所以sizeof a返回数组a中的全部元素占用的字节数。

案例2

include<stdio.h>  

int main()  
{  
    int a[2][2]={1,2,3,4};//这是一个2*2的二维数组  
    int (*p)[2];//数组指针  
    p=a;//令p指向数组a  
    printf("%d\n%d\n",sizeof(a+1),sizeof(p+1));  
    printf("%d\n%d\n",sizeof(a+0),sizeof(p+0)); 
    return  0;
}

输出:

4
4
4
4

a在做+运算时是转化成了指针变量,但a[i]是一个一维数组的数组名,sizeof(a[0])的值是8。

案例3

#include<stdio.h>  

void f(int a[][2])  
{  
    printf("%d\n",sizeof a);  
}  
int  main()  
{  
    int a[2][2]={1,2,3,4};//这是一个2*2的二维数组  
    printf("%d\n",sizeof a);  
    f(a);  
    return 0;
}

输出:

16
4

因为传参的时候数组名a转化成指针变量,注意到函数f中f(int a[][2])这里并不需要指定二维数组的长度,此处可以改为int (*a)[2]。所以传过来的就是一个数组指针变量。

2.转化

p+1 <-> a+1 <-> &a[1] <-> &p[1]

*(p+1) <-> *(a+1) <-> a[1] <-> p[1]

p+i <-> a+i <-> &a[i] <-> &p[i]

*(p+i) <-> *(a+i) <-> a[i] <-> p[i]

3.数组指针的一维和二维数组

一维数组

如果a是一维数组,则有如下:
 a表示数组的首地址,即数组中第0个元素的地址
 *a= *(a+0) <-> a[0] ,表示数组的第0个元素的值-
 *(a+i) <-> a[i] 是a一维数组的第i个元素的值

二维数组

如果a是二维数组,则有如下:
 a表示数组的首地址,即数组中第0行的首地址
 a+i是数组中第i行的首地址,即行指针
 *a= *(a+0) <-> a[0]是数组第0行的数组名,第0行第0列的起始地址;也可以看成是a一维数组的第0个元素(行)的地址
 *(a+i) <-> a[i]是数组第i行的数组名,第i行第0列的起始地址;也可以看成是a一维数组的第i个元素(行)的地址
 *(a+i)+j <-> a[i]+j <-> &a[i][j]是第i行第j列的起始地址,即列指针
 *(*(a+i)+j) <-> *(a[i]+j) <-> a[i][j]是第i行第j列的元素值

4.*(p++)*(++p)的区别

注:*p++ <-> *(p++)
*(p++)是先执行 *p,后指针p累加,指向下一个地址

*(++p)是先将p加一后再指向结果所在的地址