zoukankan      html  css  js  c++  java
  • C语言学习笔记--指针和数组的关系

    1.数组的本质 

    1)数组是一段连续的内存空间

    2)数组的空间大小:sizeof(array_type)*array_size; 

    3)数组名可看做指向数组第一个元素的常量指针

    (4)数组声明时编译器自动分配一片连续的内存空间 ,而指针声明时只分配了用于容纳地址值的 4 字节空间

    2.指针的运算

    1)指针是一种特殊的变量,与整数的运算规则为: 

    p + n == (unsigned int)p + n * sizeof(*p);

    当指针 p 指向一个同类型的数组的元素时,p+1 指向当前元素的下一个元素,p-1 指向上一个元素。 

    2)指针之间只支持减法运算且参与减法运算的指针类型必须相同。 

       p1 - p2 = ((unsigned int)p1 – (unsigned int)p2)/sizeof(type)

        只有当两个指针指向同一个数组中的元素时,指针相减才有意义,其意义为指针所指元 素的下标差。

        当两个指针指向的元素不在同一个数组中时,结果未定义。

    指针运算的应用

    #include <stdio.h>
    //统计元素的个数
    #define DIM(a) (sizeof(a) / sizeof(*a))
    int main()
    {
        char s[]={'H','e','l','l','o'};//
        char* pBegin = s;
        char* pEnd = s + DIM(s); //关键点,数组名 + n
        char* p = NULL;
        printf("pBegin = %p
    ",pBegin); //第 1 个元素的地址
        printf("pEnd = %p
    ",pEnd); //最后 1 个元素的地址
        printf("Size: %d
    ",pEnd - pBegin); //5
        //在同一行中打印出:Hello
        for (p = pBegin;p < pEnd; p++)
        {
            printf("%c",*p);
        }
        printf("
    ");
    
        return 0;
    }

    3.数组的访问方式

    1)以下标的形式访问数组中的元素:如 a[i];

    2)以指针的形式访问数组中的元素:如*(a+i) 

    3)下标形式与指针形式的转换:a[n]==*(a+n)==*(n+a)==n[a];

    #include <stdio.h>
    int main()
    {
        int a[5] = {0};
        int* p = a;
        int i = 0;
        for(i=0; i<5; i++)
        {
            p[i] = i + 1; //利用指针给数组元素赋值
        }
        for(i=0; i<5; i++)
        {
            //以指针形式来访问数组元素
            printf("a[%d] = %d
    ",i,*(a+i));
        }
        for(i=0; i<5; i++)
        {
            //以下标形式来访问数组元素
            i[a] = i + 10; //比较另类,相当于 a[i] = i + 10
        }
        for(i=0; i<5; i++)
        {
            //通过指针来访问数组元素
            printf("p[%d] = %d
    ",i,p[i]);
        }
    
        return 0;
    }

    指针和数组不同

    //ext.c
    //本文件应为.c,而不能是头文件。因为头文件会被直接包含在相应文件中,而.c 文件是分别编译的
    int a[5] = {1, 2, 3, 4, 5}; //在该文件中,a 被看作一个数组
    #include <stdio.h>
    //编译这两个文件:gcc test.c ext.c
    int main()
    {
        //外部文件中 a 被定义成一个数组。int a[5] = {1, 2, 3, 4, 5};
        extern int a[]; //本文件,如果这样声明,a 仍被看作一个数组
        printf("&a = %p
    ", &a); //这里的&a 为整个数组的地址
        printf("a = %p
    ",a); //a 为首元素的地址,数值上等于&a
        printf("*a = %d
    ",*a); //打印出第 1 个元素,即 1
    
    /*
        extern int* a; //如果这样声明,编译器将 a 当成是一个 int 型的指针来使用。
        printf("&a = %p
    ", &a); //会从符号表中查到指针 a 的地址,指向数组
        printf("a = %p
    ",a); //从指针 a 处,取一个 int 型的数据,即第 1 个元素,为 1
        printf("*a = %d
    ",*a); //试图从地址 0x01 处取出数据,违规内存访问错误。
    */
        return 0;
    }

    4.a &a 的区别

    1a 为数组首元素的地址

    2&a 整个数组的地址,即可将数组看成是一种数据结据。如 int[5]; 

    3a &a 区别在在指针运算

    a + 1 == (unsigned int)a + sizeof(*a);//a 代表首元素地址,*a 表示第 1 个元素

     &a + 1 == (unsigned int)(&a) + sizeof(*&a) == (unsigned int)(&a) + sizeof(a); 

    指针运算经典问题

    #include <stdio.h>
    
    int main()
    {
    int a[5] = {1, 2, 3, 4, 5}; int* p1 = (int*)(&a + 1); //指向数组最后面,即第5个元素的后面 int* p2 = (int*)((int)a + 1);//指向数组的起始地址+1byte偏移处 int* p3 = (int*)(a + 1); //指向第2个元素 printf("p1[-1] = %d ",p1[-1]);//输出第5个元素 //数组a在内存中从低地址到高地址的分布如下:(小端模式,低字节放入低地址) //01 00 00 00,02 00 00 00,03 00 00 00,04 00 00 00,05 00 00 00 //p2指向数组起始地址+1字节的偏移处,即01的后面,从这个地方开始读 //出4个字节,00 00 00 02,根据小端模式规则,该值为0x02 00 00 00, printf("p2[0] = 0x%X,p2[0] = %d ",p2[0],p2[0]);//0x02000000==33554432 printf("p3[1] = %d ",p3[1]); //输出第3个元素,即3 return 0; }

    5.数组参数

    1)数组作为函数参数时,编译器将其编译为对应的指针。因此,一般情况下,当定义的函数中有数组参数时,需要定义另一个参数来标示数组的大小。

    void f(int a[])等价于 void f(int* a);

    void f(int a[5])等价于 void f(int* a); //就是一个指针,丢失了数组长度的信息

    #include <stdio.h>
    
    void func1(char a[5]) //编译时,a被编译为一个指针,丢失了数组长度的信息
    {
        printf("In func1:sizeof(a) = %d
    ",sizeof(a));//a退化为指针,4
        
        *a = 'a';
         /*对指针所指向的内存进行存取(注意数组名也可以这样对内存
            进行访问,如访问第1次元素*a,对i个元素*(a+i);这说明数组名
            在使用上很像指针,但数组名并不是指针,见下面的分析*/
        
        //再次说明a是指针,而不是数组名
        a = NULL;//编译通过,a是指针类型。(而不是数组名,数组名不能作为左值)
    }
    
    void func2(char b[]) //b被编译成一个指针,与数组小大有没有被指定无关。
    {
        printf("In func2:sizeof(b) = %d
    ",sizeof(b));//b退化为指针,仍为4
        
        *b = 'b'; 
        
        b = NULL;//编译通过,b是指针类型
    }
    
    int main()
    {
    char array[10] = {0}; func1(array); printf("array[0] = %c ",array[0]); //array[0] = a; func2(array); printf("array[0] = %c ",array[0]); //array[0] = b; return 0; }

    参考资料:
    www.dt4sw.com
    http://www.cnblogs.com/5iedu/category/804081.html

     

  • 相关阅读:
    WCF 第四章 绑定 在多个绑定上暴露一个服务契约
    WCF 第五章 行为 事务跨操作事务流
    WCF 第五章 导出并发布元数据(服务行为)
    WCF 第五章 行为 通过配置文件暴露一个服务行为
    WCF 第五章 不支持会话的绑定的默认并发和实例
    WCF 第五章 并发和实例(服务行为)
    WCF 第五章 行为 总结
    WCF 第四章 绑定 绑定元素
    WCF 第五章 行为 事务之选择一个事务协议OleTx 或者WSAT
    WCF 第四章 绑定 比较各种绑定的性能和可扩展性
  • 原文地址:https://www.cnblogs.com/CoderTian/p/5906371.html
Copyright © 2011-2022 走看看