C语言数组和指针总结(上)

C语言数组和指针总结(上)

1、相同点:

    a+n <=> (unsigned int)a + n*sizoeof(*a) 如果a是一个数组那么a+n是指向第n个元素地址

    p+n <=> (unsigned int)p + n*sizoeof(*p) 如果p指向的是一个数组那么p+n是指向第n个元素地址

    p[n] <=> *(p+n)  <=>* (n+p) <=> n[p]  ====   a[n] <=>*(a+n) <=>*(n+a) <=> n[a]

2、不同点:

     数组空间大小sizeof(a) = sizeof(type)*n字节

     指针空间大小sizeof(p) = 4字节

      数组名可以看做常量指针,所以数组不能执行a++或者a—运算符,但是指针是一个变量,所以可以执行p++或者p—

3、在对&运算符含义或者操作时

      &a = 数组地址(注意:数组地址和数组首元素地址他们值是一样的,但是含义不一样)

      &p = 取指针变量p的地址

      &a+n = unsigned int(&a) + n*sizeof(*&a)

                    unsigned int(&a) +n*sizeof(a)   所以是指向整个数组后面元素

       &p+n = unsigned int(&p) + n*sizeof(&p) <=> unsigned int(&p) + n*4 就是指向下个4字节指针

4、指针与指针之间四则运算(+,-,*,/)

       a)指针之间只支持减法运算

       b) 参与减法运算的指针类型必须相同

           当两个指针指向同一个数组时,p1– p2 = (unsigned int(p1) – unsigned int(p2))/sizeof(type) 含义就是指针所指元素的下标差

            当两个指针所指不同数组进行相减运算时能够编译通过,但是结果无任何意思,只能说明内存分配谁在前,谁在后

5、指针与指针之间关系运算(>,>=,<,<=,=!,==)

       a)指针关系运算的前提是同时指向同一个数组中的元素

       b)任意两个指针之间的比较运算(=!,==)无限制

       c)参与比较运算的指针类型必须相同

       其实以上三条我都不认同,经过测试不同类型指针可以进行关系比较,因为都是指针都是保存内存地址,都是四个字节,当然可以比较,但是比较没有任何意思,只能说明在内存前后顺序,无任何意思

6、数组名作为函数参数时退化成指针目的

        C语言以高效作为最初设计目标:

        a) 参数传递的时候如果拷贝整个数组执行效率将大大下降

        b) 参数位于栈上,太大的数组拷贝将导致栈溢出

7、const修饰指针

        const int* p1

        int const* p2;

        int* const p3;

        const int*const p4;

        规律:左数右指

        含义:const在*左边,指针指向内容不能变,也就是*p = 3编译错误,但是指针可以变p = &j

                  const在*右边,指针不变,也就是p= &j编译错误,但是指针指向内容可以变,也就是* p= 4

                  const int* const p4这个即在*左边也在*右边,所以都不能改变,p= &j和*p = 5;都编译错误

                 这里结论你们可以自己写些代码测试下,加深印象

8、下面举一些代码案例

      (1)数组和指针区别:

#include <stdio.h>
int main()
{
    extern int a[];
    printf("&a = %p\n", &a);  //0x804a014  这个是数组地址
    printf("a = %p\n", a);     //0x804a014 这个是数组首元素的地址
    printf("*a = %d\n", *a);   //1
    return 0;
}
ext.c
int a[] = {1,2,3,4,5}

下面我们在改下主函数:

#include <stdio.h>
int main()
{
    extern int* a;
    printf("&a = %p\n", &a);  //0x804a014  这个是指针地址
    printf("a = %p\n", a);     //1  
    printf("*a = %d\n", *a);   //段错误,因为指针保存的1,所以按照这个地址去取值,但是这个值在内存中不允许操作,所以段错误
    return 0;
}
ext.c
int a[] = {1,2,3,4,5}

      上面就是指针和数组的区别了,如果没有区别,理论应该都编译通过,结果也应该是一样的,但是编译不通过,输出结果也不一样

     (2)多维指针

#include <stdio.h>
int main()
{
   int value = 5;
   int* p1 = &value;
   int** p2 = &p1; //试着把这个改成int** p2 = &value然后输出**p2会有什么后果?思考下
   int*** p3 = &p2;
   int**** p4 = &p3;
   
   printf("&value = %p\n&p1 = %p\n&p2 = %p\n&p3 = %p\n&p4 = %p\n\n",&value,&p1,&p2,&p3,&p4);
   
   printf("&value = %p\np1 = %p\n",&value,p1);
   printf("&p1 = %p\np2 = %p\n",&p1,p2);
   printf("&p2 = %p\np3 = %p\n",&p2,p3); 
   printf("&p3 = %p\np4 = %p\n\n",&p3,p4); 
   
   printf("*p4 = %p\np3 = %p\n&p2 = %p\n",*p4,p3,&p2);
   printf("**p4 = %p\np2 = %p\n&p1 = %p\n",**p4,p2,&p1);
   printf("***p4 = %p\np1 = %p\n&value = %p\n",***p4,p1,&value);
   printf("****p4 = %d\n\n",****p4);
   
   printf("*p3 = %p\np2 = %p\n&p1 = %p\n",*p3,p2,&p1);
   printf("**p3 = %p\np1 = %p\n&value = %p\n",**p3,p1,&value);
   printf("***p3 = %d\n\n",***p3);
   
   printf("*p2 = %p\np1 = %p\n&value = %p\n",*p2,p1,&value);
   printf("**p2 = %d\n\n",**p2);
   
   printf("*p1 = %d\n",*p1);
   
   return 0;
}

    这里不讲了,自己思考下,如果实在不懂,就说明你还没有完全理解指针含义

    (3)指针递增和递减

      思考下下面的一些输出,哪些跟哪些相同,哪些跟哪些不同,有什么区别,如果改成数组是否有区别,如果你能全部回答正确,那说明你对指针和数组已经很了解了

       *p++ 和 *(p++)

        (*p)++

        *++p 和 *(++p) 

        ++*p

        *p+1

        *(p+1)

        p+1和p++区别

        p++和a++区别

        p+1和&p+1有啥区别   如果是数组a+1和&a+1有区别么?

#include <stdio.h>

int main()
{
    int p1[5] = {1,2,3,4,5};
    int* p3 = p1;
    int* p4 = p1;
    int* p5 = p1;
    int a,b;
    printf("p3 = %p\np1 = %p\n",p3,p1);                  //p3 = p1 = 0xbfe4dbcc
    
    printf("&p3 = %p\n&p4 = %p\n&p1 = %p\n",&p3,&p4,&p1);//&p3 = 0xbfe4dbfc &p4 = 0xbfe4dbf8 &p1 = 0xbfe4dbcc
    printf("&p1+1 = %p\n",&p1 + 1);
    //结论:
    //    &p3跟&p1输出是不一样的,这个就是指针和数组明显区别,&p3是取p3指针变量的地址,而&p1是数组地址(注意:p1数组名代表数组首元素地址,和&p1代表数组地址,含义不一样,虽然他们输出数值是一样的)
    a = *p3++;
    b = *(p4++);
    printf("a = %d\nb = %d\n",a,b);      //a=1  b=1
    printf("p3 = %p\np4 = %p\n\n",p3,p4);//p3 = p4 = 0xbfe4dbd0
    //结论:
    //    因为++运算符优先级高于*,所以*p3++相当于*(p3++),跟*(p4++)是相同的,先算p3++, 等于p3,然后*p3,然后指针递增1
    p3 = p1;
    a = (*p3)++;
    printf("a = %d\np3 = %p\n",a,p3);//a=1 p3 = 0xbfe4dbcc
    printf("p1[0] = %d\n\n",p1[0]);  //p1[0] = 2
    //结论:
    //    由于()优先级高于++,所以先算(*p3)等于p1[0],也就是a=p1[0]++;所以a=1,p[0]再自增加1,p[0]=2
    
    p1[0] = 1;  //因为上面把这个值改了,为了不影响下面测试结果,我们再给它改成初值
    
    p3 = p1;
    p4 = p1;
    a = *++p3;
    b = *(++p4);
    printf("a = %d\nb = %d\n",a,b);      //a = b = 2
    printf("p3 = %p\np4 = %p\n\n",p3,p4);//p3 = p4 = 0xbfe4dbd0
    //结论:
    //    由于++优先级高于*,所以*++p3相当于*(++p3) = *(++p4),所以先算++p3,这样指针指向数组p1[1]地址,然后再执行取值,a = b = p1[1] = 2
    p3 = p1;   
    a = ++*p3;
    printf("a = %d\np3 = %p\n",a,p3);  //a = 2  p3 = 0xbfe4dbcc
    printf("p1[0] = %d\n\n",p1[0]);        //p1[0] = 2
    //结论:
    //    因为*和++都是单目运算符,所以运算顺序是从右向左,++*p3相当于++(*p3),所以先算(*p3)= p1[0],然后自增,也就是++p[0];这样p[0] = 2
    
    p1[0] = 1;  //因为上面把这个值改了,为了不影响下面测试结果,我们再给它改成初值
    
    p3 = p1;   
    a = *p3+1;
    printf("a = %d\np3 = %p\n\n",a,p3);    //a = 2 p3 = 0xbfe4dbcc
    printf("p1[0] = %d\n\n",p1[0]);        //p1[0] = 1 
    //结论:
    //    因为*p3 = p1[0]所以a = p[0] + 1 = 2
    p3 = p1;   
    a = *(p3+1);
    printf("a = %d\np3 = %p\n\n",a,p3);    //a = 1 p3 = 0xbfe4dbd0
    //结论:
    //    先计算()内的,就是p3,然后a = *p3,再然后p3+1
    //p3 = p1++;   //编译出错  为什么数组p1++编译出错,而指针p3++却可以?  因为数组名代表数组首元素地址,数组名可以看作常量指针,常量指针是不能改变的,所以编译出错,而指针是一个变量,所以可以改变
    p3 = p1;
    p4 = p1;   //注意:这个是考察p3++和p3+1是否有区别,但是由于C语言是顺序执行的,所以就改变了下
    p3++;
    p5 = p4+1;
    printf("p3 = %p\np4 = %p\np5 = %p\n\n",p3,p4,p5);//p3 = 0xbfe4dbd0  p4 = 0xbfe4dbcc p5 = 0xbfe4dbd0
    //结论:
    //    通过测试我们发现p3和p5是一样的,所以就是p3++和p3+1结果是一样的,但是p3++把自己改变了,指向了下一个元素地址,但是p3+1没有把自己改变,这个就是区别
    
    p3 = p1;    //这个是测试p3 +1 和&p3 + 1是否有区别,当时数组时p1 + 1和&p1 + 1时是否有区别
    printf("p1 = %p\n&p3 = %p\n",p1,&p3);                //p1 = 0xbfe4dbcc  &p3 = 0xbfe4dbf8
    printf("p3 + 1 = %p\n&p3 + 1 = %p\n",p3 + 1,&p3 + 1);//p3+1=0xbfe4dbd0  &p3+1=0xbfe4dbfc
    printf("p1 + 1 = %p\n&p1 + 1 = %p\n",p1 + 1,&p1 + 1);//p1+1=0xbfe4dbd0  &p1+1=0xbfe4dbe0
    //结论:
    //    p3 + 1和&p3 + 1输出数值是有区别的,p3+1就是移动4个字节,下个元素地址,但是&p3+1是先取p3指针地址,再移动4个字节,这个就是区别,但是当你把p3改成char* p3时,p3+1 = 0xbfe4dbcd,而&p3+1 = 0xbfe4dbfc,没有变化,这个也是区别了,结论就是p3+1是和p3指向的数据类型有关系,而&p3+1跟p3类型没有关系,都是移动4个字节,这是因为&p3取的是指针类型(注意不是int*这个类型),而指针保存就是4个字节,这个好像有点绕口,自己好好理解下
    //     p1 + 1和&p1 + 1输出有很大区别 p1 + 1就是移动4个字节,下个元素地址,而&p1 + 1移动了20字节就是数组最后一个元素地址的下一个地址,所以p1代表的是数组首元素地址,&p1代表数组地址,是完全不一样的概念
    return 0;
}

    p3+1和&p3+1的区别:

                                      p3+1:取p3内容(实际是p1数组首元素地址),移动sizeof(type)个字节,下个元素位置

                                      &p3+1:取p3指针变量地址(不是p1地址,这个要区别),移动4个字节,跟p3类型没有关系

    a+1和&a+1的区别:

                                     a+1:取数组首元素地址,移动sizeof(*a)或者sizeof(type)个字节,下个元素位置,所以p3+1=a+1

                                   &a+1:取a数组地址(这个和数组首元素地址不一样,概念不一样,虽然值相同),移动sizeof(a)个字节,数组最后元素下个地址

     其他注释已经解释的很清楚了,所以不需要讲了


主要参考狄泰软件《C语言进阶教程》


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值