关于C语言地址(指针)的难点解析——数组的地址与函数的地址

最近在看《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,但是底层实现原理有很大不同。

出来结果如下:

关于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' ,我们来看一下结果:

关于C语言地址(指针)的难点解析——数组的地址与函数的地址

解释一下结果,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++,因为要打印类型。

结果如下:

关于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*)类型。

上一篇:Java生成文档命令


下一篇:【网易官方】极客战记(codecombat)攻略-森林-盐碱地salted-earth