欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

数组指针,指针数组和函数指针

程序员文章站 2024-03-26 09:35:47
...

数组指针:首先呢他是一个指针,他有能力指向一个数组。
指针:其数值为地址的变量。

int (*p)[3]; // p首先与*结合,所以他呢是一个指针,他可以指向一个数组,这个数组有三个元素,每个元素为整形。

指针数组:首先呢他是一个数组,他的每一个元素为一个指针。
数组:由一系列类型相同的元素构成。

int *p[3]; // []的优先级高于 *,所以p首先是一个数组,这个数组有三个元素,每个元素为一个指针。

可以看出 p与谁先结合决定了他的类型是什么。
[] 的优先级高于*;() 的优先级高于[];

详解:

(1):数组在内存中的表示
创建一个数组,就是在内存中开辟了一块连续的空间。
二维数组是特殊的一位数组。

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

int (p)[2];p首先与 结合,所以p是一个指针,他可以指向一个数组,这个数组有两个元素。

(2)理解数组名和数组指针变量
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+1a+1
(p+1)(a+1)
虽然a跟a[0]值是一样,但类型不一样,表示的意义不一样。通过分析就不难理解为什么((a+i)+j)和a[i][j]等效了。

#include <stdio.h>    

int main()
{
    int a[2][2] = { 1, 2, 3, 4 };
    int(*p)[2];
    p = a;

    printf("a = %p, p = %p, &a[0][0] = %p\n", a, p, &a[0][0]);

    system("pause");
    return 0;
}

输出结果一致,数值均为数组首元素地址。

(3)利用指针便利数组

#include<stdio.h>      
#define M 2      
#define N 3      

int main()
{
    int a[M][N] = { 1, 2, 3, 4, 5, 6 };
    int *start = &a[0][0];
    int * const end = start + M*N;
    for (; start != end; start++)
        printf("%-5d", *start);
    putchar('\n');
    system("pause");
    return 0;
}

输出结果
1 2 3 4 5 6
用指针遍历一个二维数组,同时也可以说明二维数组其实就是连续的一维数组。

(4)数组名与数组指针变量的区别
从(2)中的分析中得出数组名是指针,类型是指向元素类型的指针,但值是指针常量,声明数组时编译器会为声明所指定的元素数量保留内存空间。
数组指针是指向数组的指针,声明指针变量时编译器只为指针本身保留内存空间。

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

输出结果:
16
4
sizeof() :关键字,并非函数,其作用为返回一个对象或者类型所占的内存字节数。
有输出结果可以看出**size(数组名)代表整个数组**4个int
指针p为一个地址

#include<stdio.h>    

void main()    
{    
    int a[2][2]={1,2,3,4};    
    int (*p)[2];  
    p=a;    
    printf("%d\n%d\n",sizeof(a+1),sizeof(p+1));    
    printf("%d\n%d\n",sizeof(a+0),sizeof(p+0));  
    printf("%p\n%p\n", a + 1, p + 1);
    printf("%p\n%p\n", a + 0, p + 0); 
}  

输出结果
数组指针,指针数组和函数指针
可见,a+i 变成为一个指针常量,
a+1 与 p+1 取到的地址也是相同的。

#include<stdio.h>    

void f(int a[][2])
{
    printf("%d\n", sizeof a);
}
int main()
{
    int a[2][2] = { 1, 2, 3, 4 };   
    printf("%d\n", sizeof a);
    f(a);
    system("pause");
    return 0;
}

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

总结:数组名单独出现在sizeof内部时代表整个数组,具体看以前博客。(a+1)的类型是一个指针变量。把数组名作为参数传递的时候实际上传递的是一个指针变量。sizeof对变量和数组名操作时返回的结果会不一样。数组指针是指向数组的指针,其值可以是变量。

指针数组

(1)指针数组简单认识
指针数组:存放指针的数组

#include<stdio.h>    

int main()
{
    int i = 1, j = 2;
    //p先跟[]结合,然后再跟*结合    
    int *p[2];//指针数组,存放指针的数组    
    p[0] = &i;
    p[1] = &j;
    printf("%d", sizeof(p));
    system("pause");
    return 0;
}

输出结果
8
首先呢p为一个数组,有两个元素,每个元素为一个整形指针。2*4

(2)多维数组与多级指针

A:二维数组

#include <stdio.h>  

int main (void)  
{  
    int a[3][2] = {{0, 1}, {2, 3}, {4, 5}};   
    int *p;  
    p = a[0];  
    printf ("%d\n", p[0]); 
    system("pause"); 
    return 0;  
}  

输出结果
0

#include <stdio.h>  

int main(void)
{
    int a[3][2] = { (0, 1), (2, 3), (4, 5) };
    int *p;
    p = a[0];
    printf("%d\n", p[0]);
    system("pause");
    return 0;
}

输出结果
1
分析上述代码初始化部分
int a[3][2] = {{0, 1}, {2, 3}, {4, 5}};
int a[3][2] = {(0, 1), (2, 3), (4, 5) };
第一个为常见的初始赋值;
第二个则是一个逗号表达式,等价于 int a [3][2]={ 1, 3, 5};不完全的初始化赋值。

考虑存储大小:

#include <stdio.h>  

int main (void)  
{  
    int a[3][2] = {0, 1, 2, 3, 4, 5};  
    int *p;  
    p = a[0];  
    printf ("%d\n", p[0]);  

    printf ("sizeof (a) = %d, sizeof (a[1]) = %d, sizeof (a[2][1]) = %d\n", sizeof (a), sizeof (a[1]), sizeof (a[2][1]));  
    return 0;  
}  

输出结果:
0
sizeof (a) = 24, sizeof (a[1]) = 8, sizeof (a[2][1]) = 4

B:二级指针

#include <stdio.h>  

int main(void)
{
    char *p = "abcdef";
    char **p1 = &p;
    system("pause");
    return 0;
}

二级指针即存放地址的指针。

(3)函数指针
显而易见,函数指针为指向函数的指针。

分析下面的函数指针:

char * (fun)(char p1,char * p2);
fun首先与 ‘*’结合所以呢他是一个指针,去掉(*fun)
char * (char * p1,char * p2);
可见fun这个指针变量指向了一个函数,这个函数的参数 (char * p1,char * p2);返回类型为 char*。

相当于char * ()(char p1,char * p2) fun1;

char *my_strcpy(char *dest, const char *src)
{
    char *tmp = dest;

    while ((*dest++ = *src++) != '\0')
        /* nothing */;
    return tmp;
}

int main(void)
{
    char *orig = "best";
    char copy[40] = "Be the best that you can be.";
    char *ps;

    ps = my_strcpy(copy + 7, orig);
    puts(copy);
    puts(ps);
    system("pause");
    return 0;
}

输出结果:
Be the best
best

了解:(int)&p 这是什么?

#include <stdio.h>    
void Function()
{
    printf("Call Function!\n");
}
int main(void)
{
    void(*p)();
    *(int*)&p = (int)Function;
    (*p) ();
    system("pause");
    return 0;
}

输出结果
Call Function!

void (*p)();
这行代码定义了一个指针变量 p, p 指向一个函数,这个函数的参数和返回值都是 void。

&p 是求指针变量 p 本身的地址。
(int*)&p 表示将地址强制转换成指向 int 类型数据的指针。

(int)Function 表示将函数的入口地址强制转换成 int 类型的数据。

(int)&p=(int)Function;表示将函数的入口地址赋值给指针变量 p。
那么(*p) ();就是表示对函数的调用。

使用函数指针的好处在于,可以将实现同一功能的多个模块统一起来标识,这样一来更容易后期的维护,系统结构更加清晰。或者归纳为:便于分层设计、利于系统抽象、降低耦合度以及使接口与实现分开。

函数指针数组

#include <stdio.h>  
#include <string.h>  

char * fun1(char * p)
{
    printf("%s\n", p);
    return p;
}
char * fun2(char * p)
{
    printf("%s\n", p);
    return p;
}
char * fun3(char * p)
{
    printf("%s\n", p);
    return p;
}
int main(void)
{
    char * (*pf[3])(char * p); //pf首先与[]结合表明pf为一个数组,这个数组有三个元素,去掉pf[]    char * (*)(char * p);可以看出每个元素是一个函数指针,这个函数参数为 char* 返回类型为 char*

    pf[0] = fun1; 
    pf[1] = &fun2; // 可以用函数名加上取地址符  
    pf[2] = fun3;

    pf[0]("fun1");
    pf[0]("fun2");
    pf[0]("fun3");
    system("pause");
    return 0;
}

输出结果
fun1
fun2
函数指针数组的指针

#include <stdio.h>  
#include <string.h>  
char * fun1(char * p)
{
    printf("%s\n", p);
    return p;
}
char * fun2(char * p)
{
    printf("%s\n", p);
    return p;
}
char * fun3(char * p)
{
    printf("%s\n", p);
    return p;
}
int main(void)
{
    char * (*a[3])(char * p);
    char * (*(*pf)[3])(char * p);//pf首先与'*'结合,所以他是一个指针,去掉*pf,   char * (*[3])(char * p);可以看出这个指针指向一个数组,这个数组有3个元素,去掉[3],char * (*)(char * p);
可以看出每个元素为一个函数指针,参数与返回类型都为char*

    pf = &a;
    a[0] = fun1;
    a[1] = &fun2;
    a[2] = &fun3;
    pf[0][0]("fun1");
    pf[0][1]("fun2");
    pf[0][2]("fun3");
    system("pause");
    return 0;
}

输出结果
fun1
fun2
fun3

以上就是我对数组指针,指针数组,函数指针的一些简单认识,有不足之处希望大家指出。