最近在看《Linux C 一站式编程》,夸一下这本书。今天花了一天时间搞懂了C语言中指针(地址)和数组、函数的关系。
很多人都知道&a[0]=*(a+0),但你知道a和&a数值是相等的吗?你知道他们类型上的差异吗?
先上代码:
#include<typeinfo>
#include<iostream>
using namespace std;
int main()
{
int a[10];
cout << "&a[0] = " << &a[0] << "\ta = " << a << "\t&a = " << &a<< endl;
cout << typeid(&a[0]).name() <<"\t"<< typeid(a).name() <<"\t"<< typeid(&a).name()<<endl;
return 0;
}
这里用的是vs编辑器用C++实现,因为要用到typeinfo文件要返回变量的类型,这个只有C++才有,C没有。需要说明的是,C++在语法上兼容C,但是底层实现原理有很大不同。
出来结果如下:
注意内容上&a[0]=a=&a,但是类型各不相同。
&a[0] 为 int *型,如果&a[0]+1,实际地址为a[0]的地址加一个整型(4字节),即指向下一个整型变量。
a为数组首地址,a+1效果同&a[0]+1,指向下一个整型变量。
&a类型为10个整型数组这么一个数组的首地址,&a+1实际地址为a[0]的地址加十个整型(40字节),为下一个int[10]数组的首地址。这种情况多出现于高维数组。
请看这一例(《Linux C 一站式编程》page341):
#include <stdio.h>
int main(){
char a[4][3][2]=
{{{'a','b'},{'c','d'},{'e','f'}},\
{{'g','h'},{'i','j'},{'k','l'}},\
{{'m','n'},{'o','p'},{'q','r'}},\
{{'s','t'},{'u','v'},{'w','x'}}};
char (*pa)[2]=&a[1][0];
char (*ppa)[3][2]=&a[1];
char*b=a[1][0];
printf("pa: %c\n",(*(pa+5))[1]);//r
printf("pa: %c\n",((*pa+5))[1]);//m
printf("ppa: %c\n",(*(ppa+1))[2][1]);//r
printf("ppa: %c\n",*(*((*(ppa+1))+2)+1));//r
printf("b: %c\n",(b+5)[1]);//m
return 0;
}
先说明一下,char (*p)[2]为数组的指针,如果是char *p[2]则是指针的数组。
这个程序想取到字符数组中的 'r' ,我们来看一下结果:
解释一下结果,pa是char * [2]类型。打印第一行pa+5为往后数5个char [2]数组的地址,*(pa+5)再访问它即为该数组,地址内容仍一样,但是类型已经变为char。(*(pa+5))[1]等同于*(*(pa+5)+1)。故打印的是'r'。
而打印第二行*p类型已变为char*,(*pa+5)[1]等同于*((*p+5)+1),等同于*(*p+6),故打印的是'm'。
ppa为char *[3][2]类型。打印第三行ppa+1从a[1]的地址开始往后找一个char[3][2],然后再访问该数组,在它内部再寻找第3个和第2个子数组,故这样找到'r'。打印第四行与第三行效果等同,不用赘述。
b类型为char *,b+5往后移5个char,b=*pa,效果与第二行等同,打印的也是'm'。
讲完了数组的地址,再来讲函数的地址。请看下例(《Linux C 一站式编程》page341):
#include<stdio.h>
#include<typeinfo>
void say_hello(const char* str)
{
printf("Hello %s\n", str);
}
int main(void)
{
void (*f)(const char*)= say_hello;
printf("%d\t%s\n", f, typeid(f).name());
printf("%d\t%s\n",say_hello, typeid(say_hello).name());
f("Guys");
(*f)("Guys");
say_hello("Guys");
return 0;
}
用的是C++,因为要打印类型。
结果如下:
f与say_hello,里面的地址都是相同的,但是类型却不相同,分别是函数的地址和函数。
void (*f)(const char*)= say_hello;左边(const char*)为输入参数,输出void,f为指向该函数的地址,初始化为say_hello。该句亦可改为
void (*f)(const char*)
f = say_hello;
注意f与say_hello,不是同一种类型,但地址是相同的。say_hello将地址传给f,但f的类型仍为其申明的函数的地址void *(const char*)类型。
如果将该句改为void (*f)(const char*)= &say_hello;效果是等同的,当然这时左右两边类型是相同的,都为void *(const char*)类型。