linux c数组和指针(详解)

2018-05-13 01:15:39

指针是c语言中最重要的同时也是最难掌握的,因为数组跟指针息息相关,所以放在一起讲解。

//声明数组
int arr[3];
int arr[3] = {1,2,3};

#非字符串
char str_arr = {'f','r','e','e','c','l','s'};

#字符串
char str = {'f','r','e','e','c','l','s','\0'};

//由编译器自动指定数组大小
int arr[] = {1,2,3}

//多维数组
int arr[2][2] = {{1,2}, {3,4}}


数组变量名本身就是数组的地址,数组跟指针的关系很密切

int arr[] = {1,2,3}
//数组的地址等于数组第一个元素的地址
//这两者都是常量,不能改变,如果想改变可以赋值给一个指针,然后改变该指针的值
arr == &arr[0]

//数组名可以表示指针名,反之亦然
int *ptr = arr
ptr[0] == arr[0]

//当作为函数形参时,int [], int *是一样的

//相同的地址
arr + 2 == &arr[2]

//相同的值
*(arr + 2) == arr[2]

//第1个元素值加2,因为*的优先级高于+
*arr + 2


指针的加减会根据指针类型的存储单元倍数移动
下面的例子中:
int型指针+1实际上指针位置移动了4(我的centos7 64位int占4个字节)
long型指针+1实际上指针位置移动了8(我的centos7 64位long占8个字节)

#include <stdio.h>

int main(){
    int arr[3] = {1,2,3};
    long arr_l[3] = {1,2,3};
    
    int *ptr = arr;
    long *ptr_l = arr_l;
    
    //0x7ffd89358850 0x7ffd89358854
    printf("%p %p\n", ptr, ptr+1);
    
    //0x7ffea17892d0 0x7ffea17892d8
    printf("%p %p\n", ptr_l, ptr_l+1);
}


指针求差,比较:
2个指针必须在同一个数组里,差值就是他们之间相差几个存储单元

#include <stdio.h>
int main(){
    int arr[6] = {1,2,3,4,5,6};
    int *ptr_1 = &arr[2];
    int *ptr_2 = &arr[4];
    
    //2    -2
    printf("%d %d\n", ptr_2 - ptr_1, ptr_1 - ptr_2);
    
    //1    0
    printf("%d %d\n", ptr_2 > ptr_1, ptr_2 < ptr_1);
    
    //相加直接报错
    //printf("%d %d\n", ptr_2 + ptr_1, ptr_1 + ptr_2);
    
}


解引用未初始化的指针

int *pt;
*pt = 5;    //严重错误

因为创建一个指针时系统只分配了储存指针本身的内存,并未分配存储数据的内存。


进阶例子

#include <stdio.h>

int main(){
    int arr[6] = {1,2,3,4,5,6};
    int *ptr = arr;
    
    int total = 0;
    
    //结合律优先级相同的从右往左
    //所以右边的ptr指针先++再求值,所以ptr已经指向数组第二个值
    //而total是先加上ptr指向的值,然后ptr++
    total += *ptr++;
    /*等同于
        total = total + *ptr
        ptr++;
    */
    printf("%d %d", total, *ptr);   //1    2
    
    
    total += *++ptr;
    /*等同于
        total = total + *(ptr+1)
        ptr++;
    */
    printf("%d %d", total, *ptr);   //4    3
}

备注:有关优先级的操作尽量使用括号,免得影响代码阅读


指针和数组

int arr[2][2] = {{1,2}, {3,4}};

1.arr是数组首元素的地址,二维数组的首元素是arr[0](是arr[0][0]的地址),所以*(arr[0])就是arr[0][0]。

2.*arr代表数组首元素(arr[0])的值,而arr[0]是一个数组,数组本身是地址,所以*arr == &arr[0][0]。

3.**arr与*&arr[0][0]等价

简而言之,arr是地址的地址,要解引用2次才能得到原始值,地址的地址就是指针的指针,数组维度越大,就越复杂,这就是为啥指针那么难理解的原因。

#include <stdio.h>
int main(){
    int arr[3][3] = {{1,2,3}, {3,4,5}};
    
    //0x7ffd6688d660 0x7ffd6688d660 0x7ffd6688d660 0x7ffd6688d660
    printf("%p %p %p %p\n", arr, arr[0], &arr[0], &arr[0][0]);
    
    //0x7ffd6688d668 0x7ffd6688d664 0x7ffd6688d668 0x7ffd6688d664
    printf("%p %p %p %p\n", arr+1, arr[0]+1, &arr[0]+1, &arr[0][0]+1);
}


arr                    一个指向第一个数组的指针(数组指针,也就是指针的指针)
arr+1                由于arr是数组指针存储单元为数组大小,所以会往后移动一个数组,即&arr[1]

*(arr+1)            第二个数组的值即&arr[1][0]

*(arr+1)+1        即&arr[1][1]

*(*(arr+1) + 1)    即arr[1][1]

#include <stdio.h>
int main(){
    int arr[3][3] = {{1,2,3}, {3,4,5}};
    
    //0x7ffe4c7472b0 0x7ffe4c7472bc 0x7ffe4c7472bc 0x7ffe4c7472c0 4
    printf("%p %p %p %p %d\n", arr, arr+1, *(arr+1), *(arr+1) + 1, *(*(arr+1) +1 ) );
    
    //0x7ffe4c7472b0 0x7ffe4c7472bc 0x7ffe4c7472bc 0x7ffe4c7472c0 4
    printf("%p %p %p %p %d\n", arr, &arr[1], &arr[1][0], &arr[1][1], arr[1][1] );
}


再来看下面2个声明的区别,它们都是指针的指针
一个是指针(可以改变自己,比如说自增)
一个是指针数组(不能改变自己)

int (* ptr)[2];

声明了1个指针ptr,它指向了一个数组,该数组包含2个int值。


int * pax[2];

声明了一个指针数组pax,它里面包含2个int型指针。

例子

#include <stdio.h>
int main(){
    int (*ptr)[2];
    int arr[2][2] = {{1,2}, {3,4}};
    
    ptr = &arr[0];
    
    //1    2
    printf("%d %d\n", ptr[0][0], ptr[0][1]);
    printf("%d\n", **(++ptr));      //3
    
    int * pax[2];
    
    pax[0] = arr[0];
    pax[1] = arr[1];
    
    //1    2
    printf("%d %d\n", pax[0][0], pax[0][1]);
    
    //报错因为pax为数组变量,不能改变自己
    printf("%d %d\n", **(++pax));
}


总结

1.本文对linux c指针做了详细的介绍,建议初学者多看几遍,因为指针时c语言里最难的部分,如果有疑问可以给我留言
2.gcc4.8,运行环境centos7 64位
3.原文地址http://www.freecls.com/a/2712/1d

©著作权归作者所有
收藏
推荐阅读
简介
天降大任于斯人也,必先苦其心志。