C 指针&&数组

来源:互联网 发布:java读取文件Textfile 编辑:程序博客网 时间:2024/05/22 01:49

一、指针与数组的定义


首先来看一下定义与声明的具体含义


定义只能出现一次确定对象的类型并分配内存,用于创建新的对象(特殊的声明)
声明可以出现多次描述对象的类型,用于说明其它对象的类型


        我们常说数组名是数组首元素的地址,即是一个指针。有时候我们也会把数组名当做指针来访问数组;还有给函数传一个数组时传的是数组首元素的地址,函数的形参可以定义为一个指针变量。这是不是说数组名就与指针等价呢? 不是的!


来看下面的例子


1、定义数组声明为指针


    来看一下下面的代码


新建两个源文件test_1.c  test_2.c,将它们放到一个项目test中:

test_1.c:char arr[]="abcdef";


test_2.c:#include <stdio.h> extern char *arr; int main(){        printf("%s\n",arr);        return 0;}

  

      上面的代码会输出abcdef吗?答案是不会。如果运行这个程序时,编译器会报错。

       

         这是为什么呢? 因为在test_2.c中编译器会把arr当做指针,但是在arr中存放的是"abcdef",任何存入指针变量的内容,编译器都会把它当做一个地址这时编译器会把arr数组的前四个字节的内容当做一个地址,即abcd的ASCII值,即61626364,假设计算机的存储模式是小端存储,此时arr的值为64636261,这是一个非法的地址,故编译器会报错。


wKiom1ag9h6zMoQdAAELfEuuotg124.png


        那么,这个时候如果还想输出"abcdef",改怎么做呢?可以这样做:


     

  printf("%s\n",(char*)&arr);


        将arr的地址取出来强制类型转换为char*,这是我们就拿到了数组arr的首元素的地址,就可以输出数组内容。


2、定义指针声明为数组


    来看下面的代码:


test_1.c:char *p="abcdef";



test_2.c:#include <stdio.h>extern char*p[];int main(){    printf("%s\n",p);        return 0;}



        上面的代码输出的结果是什么呢?输出的并不是"abcdef",这是如果以%p输出p的值,p的值为00422fa4(不同的编译器输出的值不同),这是为什么呢?我们来分析一下。


    在定义一个指针变量,并给它赋一个常量字符串时,在内存里会发生什么呢?如下图:

wKioL1ag9oKzMWpNAAEUcqpsLBc098.png

        这时如果把指针变量声明为一个数组的话,编译器会把p的值当做一个数组,输出的值就是p的值。


        这时,如果还想输出"abcdef",改怎么做呢?可以这样做:

       

 

printf("%s\n",*(int*)p);



        将p的值强制类型转换为int*,然后解引用访问到的就是的p值,也就是"abcdef"的首地址。


        从上面的例子可以看出,指针和数组名并不完全等价。




二、指针数组与数组指针


1.指针数组


        首先,指针数组是一个数组,数组元素是指针。


        比如,int*p[2];这是定义一个数组,因为数组的元素是指针,所以称为指针数组。

        由于[]的优先级高于*,所以arr先与[2]结合,即这是大小为2的数组,前面的是数组元素的类型,可以与int a[2]类比,int为数组元素的类型。


2.数组指针


        首先,数组指针是一个指针,这个指针可以指向数组,所以称为数组指针。


        比如,int (*p1)[2];这是定义一个数组指针变量。 *先与p1结合,所以p1是一个指针,去掉变量名为类型 int[2],即p1可以指向大小为2的 int型数组。可以与int *ptr类比,ptr是一个指针,去掉*ptr,为int,即ptr可以指向int类型数据。


wKioL1ag89KjbznaAAI_mTPKmFo770.png


        我们来看一下下面的例子

#include <stdio.h>int main() {    int a[5][5];    int (*p)[4];    p = (int(*)[4])a;        printf( "%d,%p\n", &p[4][2] - &a[4][2],&p[4][2] - &a[4][2]);        return 0; }


        上面的代码输出的结果是什么呢?答案是-4 和FFFFFFFC;

        这是为什么呢?首先,我们要找到p[4][2]和a[4][2]的位置

wKiom1ahdZaCGyi0AAEkocErTms465.png


        p[4][2]与a[4][2]之间有4个元素,故&a[4][2]-&p[4][2]=4,输出的是4;

        &p[4][2]-&a[4][2]=-4,   - 4在内存中是以补码的形式存储的即FFFFFFFC,以%p输出就是FFFFFFFC



三、数组的传参


        我们知道给函数传一维数组时,传的是数组首元素的地址,函数的形参可以是指针变量,也可以把数组放到形参的位置。要注意的是传给函数的数组会降级为数组首元素的地址。

        那么二维数组该如何传给函数呢?


        如果将数组名传给函数,函数的形参应该定义为什么类型呢?


        在这之前我们要对二维数组有一个深入的理解。


        定义一个二维数组 int arr[2][5];这个数组在内存是如何存储的呢?

wKioL1ag_yXxUmU2AAAWKZzaCwQ084.png

        二维数组可以看作一个一维数组,只是这个一维数组的元素是一个数组。如上面的二位数组,可以看作一个一维数组 arr[2]={arr[0],arr[1]}


        那么如果把数组名传给函数,函数接首到的到底是什么呢?


        我们知道数组名代表数组首元素的地址,这个二位数组的首元素是arr[0]的地址,即&arr[0]. arr[0]是一个数组,取出来的是数组的地址,类型为数组指针。所以函数的参数必须定义为数组指针类型。


如下代码所示:

#include <stdio.h>void fun1(int arr[][5] ) //第一个[]可以不写大小,第二个必须写,因为表示类型{}void fun2( int (*p) [5] ){}int main(){    int arr[2][5]={{1,2,3,4,5},{6,7,8,9,0}};        fun1(arr);        fun2(arr);            return 0;}



        虽然上面的代码什么都没有做,但是它在编译,链接执行的时候都没有错。也就是说二维数组传参时,传给函数的是一个数组指针,同时函数的形参类型也必须是一个数组指针类型。







本文出自 “牛丽” 博客,请务必保留此出处http://15129279495.blog.51cto.com/10845420/1737674

0 0
原创粉丝点击