指针与数组的爱恨情仇

本文为原创,转载请注明出处和作者。

在C语言中,指针的使用非常灵活,而且指针与数组有着千丝万缕的联系,下面的程序解释了指针与数组的部分关系。

备注:本程序除了指针与数组的关系,还有如何使用关键字extern引用外部变量的例子。(例子程序在another.h和another.c文件中。)

对程序的解释都在注释中。

文件:testPoint.c

/* 温习C语言相关知识一:指针和数组的爱恨情仇
 * 时间: 2016/5/24
 * 作者: 程凯
 * 发布: http://www.wydfx.xyz
 * 注意: 数组名是常指针,不可更改,例如程序中,a = a + 1;是非法的
 *        实参: Argument
 *        形参: Parameter
 *        一维数组名会转换成指向数组第一个元素的指针
 *        二维数组名被转换成指向数组的指针
 */
#include <stdio.h>
#include <stddef.h>
#include "another.h"

// 声明全局变量
// 注意:本工程中用到了extern引用外部变量(详见another.h文件),
// 使用extern引用外部变量时,所被引用的变量必须是全局变量才行
int a[8] = {1,2,3,4,5,6,7,8};
int twoDimension[2][4]={{1,2,3,4},{5,6,7,8}};// 二维数组名是指向一维数组的指针
int (*tDpointer)[4] = twoDimension;          // 注意,二维数组指针的定义 twoDimension的返回值是指向一维数组的指针
int (*tDpointer2)[2][4] = &twoDimension;     // 这是丧心病狂的使用方法 &twoDimension的返回值是指向二维数组的指针
int (**tDpointer3)[4] = &tDpointer;          // 这是丧心病狂的使用方法 tDpointer本身是指向一维数组的指针,
                                                // 那么 &tDpointer为指向一维数组指针的指针,相当于指向二维数组的指针
// 我的总结:一个指针(数组名也可以理解为指针)到底是指向几维数组,这里的“几”等于*与[]个数减一,
// 比如(**tDpointer3)[4]中,*和[]的个数为3,那个tDpointer3就是指向二维数组的指针,
// 再则,twoDimension[2][4]={{1,2,3,4},{5,6,7,8}},*和[]的个数为2,那么twoDimension相当于指向一维数组的指针。
                                                // tDpointer2和tDpointer3是等价的
int *column_p = twoDimension[1];             // column_p指向二维数组twoDimension的第二行
int *b = a;
int *ip = a;
int *ig = a +3;

int main(){
    ptrdiff_t n = ig - ip;// ptrdiff_t 是C语言的一个标准类型,用来存储指针之间的差值
                        // 注意: ANSI C标准中没有定义两个指针相加的运算,通常在编译环节报错 
    /*=================
     * 对一维数组的讨论
     *=================*/
    printf("对一维数组的讨论:\n");
    printf("ig和ip所指对象的地址之间的差值%d\n",n);
    printf("\n使用数组名的第一种方法\n");
    printf("a[0] = %d\n",a[0]);
    printf("a[1] = %d\n",a[1]);
    printf("a[2] = %d\n",a[2]);
    printf("\n*(a+1) = %d\n\n",*(a+1));
    printf("\n使用数组名的第一种方法\n");
    printf("a[0] = %d\n",a[0]);
    printf("a[1] = %d\n",a[1]);
    printf("a[2] = %d\n",a[2]);
    printf("\n*(a+1) = %d\n\n",*(a+1));
    printf("使用指针的第一种方法\n");
    printf("a[0]=%d\n",*b);
    printf("a[2]=%d\n",*(b+1));
    printf("a[3]=%d\n\n",*(b+2));
    printf("使用指针的第二种方法\n");
    printf("a[0]=%d\n",b[0]);
    printf("a[2]=%d\n",b[1]);
    printf("a[3]=%d\n\n",b[2]);
    printf("a的地址为%d\n",b);
    printf("\na的大小为%d\n",sizeof(a));//sizeof(a)的返回值为数组a中元素的个数乘以元素类型的大小,
                                        //结果表示数组a所占字节的大小,
                                        //本例输出为32=8*4,8表示有8个元素,4表示一个int变量占4个字节

    /*=================
     * 对二维数组的讨论
     *=================*/
    printf("\n\n对一维数组的讨论:\n");
    printf("使用二维数组名的第一种方法\n");
    printf("twoDimension[0][1]=%d\n",twoDimension[0][1]);
    printf("twoDimension[1][1]=%d\n",*(twoDimension[1]+1));
    printf("\n使用二维数组名的第二种方法\n");
    printf("twoDimension[1][3]=%d\n",*(*(twoDimension+1)+3));
    printf("twoDimension[1][3]=%d\n",*((twoDimension+1)+3));//本行代码可执行,但结果错误
    /* 对上面第二种方法的解释
     * twoDimension是二维数组名,是一个指向长度为8的数组的指针,同时也是一个常指针,其值不可更改
     * *(twoDimension+1)其实就是twoDimension[1]
     * 我的猜测:
     * 经过验证(本程序后面的有验证的程序段),发现(twoDimension+1)的值和*(twoDimension+1)的值是一样的
     * 此外,(tDpointer+1)的值和*(tDpointer+1)的值也是一样的,既然加不加*,值都是一样的,那干嘛还要加*呢?
     * 我是这样想的,虽然值是一样的,但用不同的视角看,意义是不一样的,程序将(twoDimension+1)和(tDpointer+1)
     * 都看成int类型的数据,把里面的数就看成一个数,而把*(twoDimension+1)和*(tDpointer+1)都看成一个指针,
     * 把里面的数看成一个地址,这就是加*和不加*的区别。
     */
    printf("twoDimension[1][3]=%d\n",*(column_p+3));
    // column_p只能访问第二行,而不能访问第一行
    printf("twoDimension[1][4]=%d\n",*(column_p+4));// 数组越界,但是编译和运行都不报错,看来指针很自由啊
                                                    //(其实用数组名也可越界,编译和运行也都不报错)
    
    printf("sizeof(twoDimension) = %d\n",sizeof(twoDimension));
                                        //sizeof(twoDimension)的返回值为数组a中元素的个数乘以元素类型的大小,
                                        //结果表示数组twoDimension所占字节的大小,本例输出为32=4*2*4,
                                        //4表示一行有4个元素,2表示一共有2行,4表示一个int变量占4个字节
    printf("sizeof(twoDimension[1]) = %d\n",sizeof(twoDimension[2]));
    printf("sizeof(tDpointer) = %d\n",sizeof(tDpointer));
    printf("sizeof(column_p) = %d\n",sizeof(column_p));

    printf("\ntwoDimension数组名的地址 = %d\n",twoDimension);
    printf("twoDimension[0] = %d\n",twoDimension[0]);
    printf("twoDimension[1] = %d\n",twoDimension[1]);
    printf("twoDimension[0][0]的地址 = %d\n",&twoDimension[0][0]);
    printf("twoDimension[0][1]的地址 = %d\n",&twoDimension[0][1]);
    // 运行以上两行代码可以发现,加不加*,值都是一样的

    printf("\ntwoDimension+1 = %d\n",twoDimension+1);
    printf("*(twoDimension+1) = %d\n",*(twoDimension+1));

    printf("\n*(*(tDpointer+1)+1) = %d\n",*(*(tDpointer+1)+1));
    printf("tDpointer[1][1] = %d\n",tDpointer[1][1]);
    printf("tDpointer = %d\n",tDpointer);
    printf("tDpointer+1 = %d\n",tDpointer+1);
    printf("*(tDpointer+1) = %d\n",*(tDpointer+1));


    // 运行以上两行代码可以发现,加不加*,值都是一样的

    /* 总结:
     * 假若要访问二维数组twoDimension中第2行第2列(注意数组下标从0开始)的元素可以有以下的几种方式(i为int型变量):
     * 通过数组名引用                      通过指针p的引用                    通过指针column_p的引用
     * i = twoDimension [1][1];             i = *(*(p+1)+1);                column_p = twoDimension[1];
     * i = *(twoDimension [1]+1);           i = *(p[1] + 1);                i = *(column_p+1);
     * i = *(*(twoDimension+1)+1);          i = (*(p + 1))[1];              i = column_p[1];
     */

    /* 下面看丧心病狂的使用方法
     * 注意:第一个*号内必须是+0,因为实际上,tDpointer2指向的是一个三维数组,即二维数组进食三维数组的一个元素
     */
    printf("*(*(*(tDpointer2+0)+1)+1) = %d\n",*(*(*(tDpointer2+0)+1)+1));
    printf("*(*(*(tDpointer3+0)+1)+1) = %d\n",*(*(*(tDpointer3+0)+1)+1));

    /* &twoDimension: 对二维数组名取地址,返回一个指向二维数组的指针;
     * twoDimension:  二维数组名会被转换为指向第一行(第一个数组)的指针,与&twoDimension[0]等价;
     * &twoDimension[0]:对第一个一维数组的数组名取地址,返回一个指向一维数组的指针;
     * twoDimension[0]:  二维数组中第一个一维数组的数组名,与&twoDimension[0][0]是等价的;
     * &twoDimension[0][0]:对第一行第一列元素取地址,返回一个指向整型元素的指针。
     */
    printf("&twoDimension = %d\n",&twoDimension);
    printf("twoDimension = %d\n",twoDimension);
    printf("&twoDimension[0] = %d\n",&twoDimension[0]);
    printf("twoDimension[0] = %d\n",twoDimension[0]);
    printf("&twoDimension[0][0] = %d\n",&twoDimension[0][0]);

    printf("\n调用another.c文件里的函数\n");
    printInformation();

    scanf("%d",&a[1]);
}

 文件:another.h

#include <stdio.h>

// extern关键字,引用外部变量
extern int a[];
extern int twoDimension[][4]; // 引用外部二维数组使,最后一个中括号中一定要写上数字,否则编译器不知道数组是如何组织的。
extern int (*tDpointer)[4];
extern int (*tDpointer2)[2][4];
extern int (**tDpointer3)[4];
extern int *column_p;             // column_p指向二维数组twoDimension的第二行
extern int *b;
extern int *ip;
extern int *ig;

void printInformation();

文件:another.c

#include <stdio.h>
#include "another.h"

void printInformation(){
    printf("twoDimension[1][1] = %d\n",twoDimension[1][1]);
    printf("tDpointer[1][1] = %d\n",tDpointer[1][1]);
    printf("tDpointer[1][1] = %d\n",tDpointer[1][1]);

    printf("\n*(*(tDpointer+1)+1) = %d\n",*(*(tDpointer+1)+1));
    printf("tDpointer[1][1] = %d\n",tDpointer[1][1]);
    printf("tDpointer = %d\n",tDpointer);
    printf("tDpointer+1 = %d\n",tDpointer+1);
    printf("*(tDpointer+1) = %d\n",*(tDpointer+1));
    printf("*(*(*(tDpointer2+0)+1)+1) = %d\n",*(*(*(tDpointer2+0)+1)+1));
    printf("*(*(*(tDpointer3+0)+1)+1) = %d\n",*(*(*(tDpointer3+0)+1)+1));
}

本工程下载地址:链接:http://pan.baidu.com/s/1dFNTgAl 密码:wjhk

发表评论

电子邮件地址不会被公开。 必填项已用*标注

5 − 5 =

41 + = 45