C语言--数组与指针的学习

C语言->数组与指针的学习

标签(空格分隔): C语言


都说指针是C语言的精华部分也是最难理解的部分,在学习指针的过程中确实遇到了不少的问题,以下将逐一列举。

什么是指针,什么是指针变量?

指针不只是单纯的地址,而是一个指向地址且包涵了地址以及地址类型的信息的那么一个东西,所以指针不能简单的和地址等价。指针是一个常量,它指向的对象是一个数据地址(这个数据可以是一个普通数值的地址,也可以是另一个指针,也可以是一个函数的入口地址等等),指针可以进行加减运算但进行运算(可以理解为移动)后该指针将指向另一个对象(原始对象即地址并没有发生改变)。
在这里用地下城与勇士这游戏做个比喻吧,当你打一个怪物时它的血量信息会出现在它头顶,这是因为你指向了它,你再打另一个怪物时,另一个怪物的血量信息又出现在它的头顶,这时你的角色就是一个指针。
指针变量是一个变量,所以它可以进行赋值,赋值的对象是地址,可以把指针变量理解为存储地址信息的一个容器。
还是用地下城做比喻把,你的角色是一个指针变量,里面存储了你的血量信息,把你的血量分到三个地方进行存储,第一个地方存满血,第二个地方存一半血,第三个地方存空血。当你没被打时你的角色存的是第一个地方里的满血状态,当你被打第一下是存的是第二个地方的半血状态,被打第二下村的就是你的死亡状态啦~这就是指针变量
最后总结下吧,指针和指针变量可以用他们的用途来区分,指针的作用是指向,指针变量的作用是存储他们的共同对象都是地址,所以要把指针和指针变量区分开来

使用数组名、指针作为函数参量

函数调用的是数组时应该将数组的地址传递给函数,所以在函数声明时形参的类型应该是指针类型,即:下标型void sum(int arr[ ])或指针型void sum(int* arr)
注意
1.使用数组做形参传递的是元素的首地址而不是一个数组中的值
2.不能这样声明void sum(int arr)这传递的是一个int元素的值而不是int数组,如果在其他函数中调用该函数时这样这样写的话

void sum(int arr)//arr是一个int
int arr[5];//arr是一个含有五个int的数组
sum(arr);

则编译器会有如下报错:报错
这是告诉你你传递的是数组变量而形参的类型却只被定义了普通变量
3.传递二维数组时
有三种传递方式

  • 普通的数组传递void sum(int [ ][ 3],int );
  • 传递指向数组的指针void sum(int (*arr)[3],int );
  • 传递指向指针的指针void sum(int **arr);

方式一:void sum(int [ ][ 3],int );

这是普通的参数为数组的表达方式,下面是个例子:

int main()
{
    void sum(int [][3],int );
    int n;
    int arr[n][3] = {1,2,3,4,5,6,7,8,9};
    sum(arr,n);
    .....
}
void sum(int arr[][3],int n)
{
    int i,j;
    for(i =0;i <n;i ++)
        for(j =0;j <n;j ++)
            printf("%2d",arr[i][j]);
}

在这段代码中sum的参数包含了两个参数第一个参数是int型的二维数组名,第二个参数是传递的一维数组的个数(所谓二维就是n个一维的集合)。
在这sum函数在声明时没写参数名字,而定义时却写了,不然在定义sum函数时就使用不了从主函数中传递过来的数组了(连名字你都不知道,你怎么找得到他!)。
值得注意的是数组的传递传递过来的东西实质上是该数组的首元素地址。
arr[][3]第一个空的[]是告诉编译器arr是一个指针第二个[3]则表明这是一个指向包涵四个int型数值的数组的指针。在声明时第二个[]内的内容是不能省略的,因为它要告诉编译器这个数组包涵了多少个int型数值以方便指针计算地址(前面提到过指针是包涵了地址还有地址的信息的变量)。这是一个小测试说明了传递的数组实际上是一个地址:
测试

方式二:void sum(int (*arr)[3],int );

这个形式是指针的形式,其实和第一种方式在实质上是一样的,方式一使用的是指针的下标法也就是带[]的指针表达方式(*arr)[3]等价arr[][3]在运算中arr[1][2] == *(*(arr + 1)+2)所以arr[][]实际上是一种方便阅读的指针表达方式,在编译时还是要转换成真正的指针的形式的,所以在运算速度上慢于第二种方式。以下是一个例子:

int main()
{
    void sum(int (*)[3],int );
    int n =3;
    int arr[n][3];
    int (*p)[n];
....
    p = arr;
    sum(p,n);
}
void sum(int (*p)[3],int n)
{
    int i,j;
    for(i =0;i <n;i ++){
        for(j =0;j <n;j ++)
            printf("  %d",*(*(p+i)+j));//同p[i][j]等价
        putchar('\n');
    }
}

在声明函数时int (*)[]的()不能省略带上()它是这样的:
int (*arr)[3]==int arr[][3]
去掉了()它是这样的:
int* [3]==int arr[3]
前者是指向包涵了n个int数组的指针,而后者是指向包涵了n个int数值的指针(二维数组可以理解为存放数组的一维数组,而普通一维数组存放的是普通类型值)。
代码的p是一个指针变量,存放的是地址(地址是一个常量)还是用下标法吧- -这个忒麻烦看着老别扭.

方式三:void sum(int **arr);

这传递的是指向指针的地址的指针,在我看来这是最费神的一个方式了,一不小心就满屏皆warning(血的教训啊!!)不过既然C拥有这一个特性自然有它的道理,原谅初学的我没体会到它的强大,但还是要了解的吧~下面是代码:

int main()
{
    void sum(int **,int );
    int n =3;
    int arr[n][3];
    int *p;

    p = arr[0];
    sum(&p,n);
    return 0;
}
void sum(int **po,int n)
{
    int i;
    for(i =0;i <n*n;i ++){
            printf("%2d",*(*po+i));
        if(0==(i+1)%3)
            putchar('\n');
    }
}

因为sum需要的参数是一个指向指针的指针(**po),而指针变量p存储的是arr[0](arr[0][0]的地址)所以函数sum应该接受的是指向p地址的指针也就是&p(sum(&p,n);指针既然是一个数据自然有它的地址)。
在sum函数中我取了个巧,只用一个循环将arr数组的内容输出出来,按照当时的想法是用两个循环输出的,如下:

    for(i =0;i <n;i ++){
        for(j =0;j <n;j ++)
            printf("  %d",*(*(p+i)+j));

然而,这样输出的结果并不是我想要的,当i进行了第二次循环时,指针已经打野去了,指向了一群不是我想要的数据,*(*(p+i)+j)中p =arr[0],*(p+1)==*(arr[0]+1)这是什么鬼东西的地址?!鬼知道啊!!!!然后我就只能在获得了arr这个数组首元素地址之后单用一个for循环输出结果了。
有谁能解释下这段嘛?这里是我比较迷惑的地方,如果有哪位看官能解释下,小弟我感激不尽。

变长数组(VLA)

在被定义时数组的大小是固定的,变长数组并不是能够改变数组的大小,而是在没定义数组时可以通过变量来声明数组的大小,如:int arr[n][M];
注意!是没定义数组时才可以这样声明,所以在声明变长数组时不能进行初始化,不然就会警告!
下面是一个变长数组的应用:

#include<stdio.h>
int main()
{
    void sum(int [][3],int )
    int i,j;
    int n =3;
    int arr[n][n];//只是声明没有定义

    for(i =0;i <n;i ++)            //这三行就给arr数组
        for(j =0;j <n;j ++)        //定义
            scanf("%d",&arr[i][j]);//了
    for(i =0;i <n;i ++)
        for(j =0;j <n;j ++)
            printf("%d ",arr[i][j]);
}
void sum(int arr[][3],int n)//这里的arr作为形参也只是声明
{
    .....
}

我还没接触大工程已经能感受到变长数组的灵活性了,何况大工程中呢~

复合文字

在cpp的表述中,复合文字是为了能给函数传递数组常量而设计的,它的长相是这样的:(int[2]){1,2}; (int [2][2]){1,2,3,4};它是一个没有名字的数组,下面是例子:

int main()
{
    void sum(int [],int)
    int i;
    int *p;

    p =(int[2]){1,2};
    for(i =0;i <2;i ++,p++)
        scanf("%d",p);
    p =p-2;//这是让p回到符合文字的首地址,使用指针变量一定要记住这步!不然会死的很惨,这是血的教训。
    for(i =0;i <2;i ++,p++)
        printf("%d ",*p);
    sum((int [2]){1,2},2);//此int [2]非上面的int [2],他们的地址是不同的
    return 0;
}
void sum(a[],int n )
{
...
}

复合文字在被定义了之后系统会纪录它的首元素地址,因为复合文字的数组是没有名字的,所以在定义了它之后还想再找到它只能声明一个指针变量用来存储它的地址了如代码中的p =(int[2]){1,2};p被赋予的是该复合文字的首地址。
然后就是,我还以为复合文字就是常量了不能被改变呢,结果一试居然能变~

END….


这是第一篇技术博客,可能有很多不足的或者错误的地方,希望各位博友轻喷,有什么错误的地方也请指出来。以后每学习一个阶段都会复习、总结一次,作为一名普通本科又初次接触变成的小学徒,不指望成为什么样的大神,但愿能不断进步,突破自己,毕竟,编程已经是一件神奇的事了~

(未经允许,禁止转载)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值