数组指针,指针数组和函数指针
数组指针:首先呢他是一个指针,他有能力指向一个数组。
指针:其数值为地址的变量。
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+1同a+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
以上就是我对数组指针,指针数组,函数指针的一些简单认识,有不足之处希望大家指出。