zoukankan      html  css  js  c++  java
  • 深入理解C指针 指针和多维数组的关系

    1.数组名是什么

      数组名是一个标识符,它标识出我们之前申请的一连串内存空间,而且这个空间内的元素类型是相同的——即数组名代表的是一个内存块及这个内存块中的元素类型 数组名的值是数组首元素的指针常量。
      数组名不是指针,但大多数使用到数组名的地方,编译器都会把数组名隐式转换成一个指向数组首元素的指针来处理。只有两种情况下例外:

      int a[ ] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

      1)第一种是对数组名使用sizeof运算符:

    sizeof(a)

    这将会得到整个数组所占的内存大小,a是长度为10的int(4字节)数组,运算结果是40

      2) 第二种是对数组名取地址:  &a

    运算结果是数组的地址。注意,数组的地址和数组首元素的地址是不同的概念,尽管二者的值是相同的。

    2.下标引用

        一维数组int arr[i] 可以表示为 *(arr + i)  arr的值被转换成指针常量,指向第一个元素,向右移动i * sizeof(int)个字节,然后解引用,便得到了第i个元素的内容。因为第一种写法会自动转换成第二种,这个过程需要一些开销,所以我们说第二种写法通常效率会高一些。

    3.数组的类型

    以一维数组 int a[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };为例:

      数组a的类型是 int *    a 是指向元素的指针。数组的类型取决于数组元素的类型:如果它们是int类型,那么数组名的类型就是“指向int的常量指针”;如果它们是其他类型,那么数组名的类型就是“指向其他类型的常量指针”。(出自《C和指针》第141页)

      &a 的类型是 int (*)[10]   &a是指向数组的指针。  取一个数组名的地址所产生的是一个指向数组的指针,而不是一个指向某个指针常量值的指针(出自《C和指针》第142页)。

        int a[5] = { 1,2,3,4,5 };
        printf("%p
    %p", a, &a);
    
        printf("
    %d %d %d", sizeof(a), sizeof(*a), sizeof(*&a)); //20 4 20
        printf("
    %p
    %p", a, &a + 1);
    
        int *p = a;
        int(*pInt)[5] = &a;
        printf("
    %d,%d", sizeof(*p), sizeof(*pInt)); //4 20

    二维数组

      二维数组的类型同样取决于数组元素的类型,假设有二维数组int b[2][5]

    因为C语言的多维数组实际上是一维数组,二维数组实际上只是一个一维数组,只不过里面每个元素又是一个一维数组而已。

      所以b的类型是int (*)[5],而&b的类型是int (*)[2][5]

        int a[2][5] = { 1,2,3,4,5,6,7,8,9,0 };
        for (int i = 0; i < 2; i++)
        {
            for (int j = 0; j < 5; j++)
            {
                printf("%d,%p  ", a[i][j], &a[i][j]);
            }
            putchar('
    ');
        }
        printf("
    %d   %d   %d   %d
    ", sizeof(a),sizeof(*a),sizeof(**a),sizeof(*&a));
        printf("%p
    ", a);
        printf("%p
    ", a + 1);
    
        printf("%p
    ", *a);
        printf("%p
    ", *a + 1);
        printf("%p
    ", *(a + 1));
    
        printf("%p
    ", &a);
        printf("%p
    ", &a + 1);

     

      sizeof(a)会得到整个数组所占内存大小。sizeof(*a)会得到一行数组所占内存大小。sizeof(**a)会得到一个元素所占内存大小。

      *a是首元素的地址,*a + 1是指向下一个元素的地址,*(a + 1)指向下一行的地址。

      a是行指针,指向一个共有5个元素的一维数组地址,a + 1地址增加20字节,下一行地址:%p 转换10进制加上 20字节,再转换16进制。

      &a是一个指向二维数组指针,10个元素40字节,&a + 1地址增加40字节,相当于指向了下一个数组(实际上并不存在),或者说指向了数组a最后一个元素的下一个元素,这在C++里称为尾后指针。

     

      由于 a[i] = *(a + i) 那么一个二维数组int a[i][j]。

        那么a[i]+j 表示i行j列元素的地址,所以 *(a[i] + j)表示a[i][j]的元素。

      既然 *(*(a + i) + j)与* (a[i] + j)等价,表示a[i][j]的元素。

    二级指针与指针数组的关系:

    #define _CRT_SECURE_NO_WARNINGS
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    #include <ctype.h>
    void main()
    {
        char * cmd[] = { "notepad","calc","mspaint","write" }; //指针数组 数组的每一个元素都是一个指针 
        for (char **p = cmd; p < cmd + 4; p++)//指针循环方式循环指针数组
        {
            printf("%p", p);        
            printf("   %s", *p);//*p是一个指针变量,存储一个常量字符串的首地址   
            printf("   %c
    ", **p); //**p等价于*str[0]等价于‘n’
        }
    
        char *arry[] = { "Hello","world","Pumpkin","Comparative" }; //存储char *类型地址的数组 - 指针数组
        printf("%s
    ", arry[0]);//Hello arry[0]是一个地址
        printf("%c
    ", *(arry[0]));//H
        printf("%c
    ", *(arry[2] + 4));//k
        system("pause");
    }

    指针做输入第一种模型:

    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    void printMyArray(char *myArray[5], int num)
    {
        for (int i = 0; i < num; i++)
            printf("%s
    ", myArray[i]);//*(myArray + i)
    }
    void sort(char ** myArray, int num)
    {
        char *temp = NULL;
        int i = 0;
        int j = 0;
    
        for (i = 0; i < num - 1; i++)
            for (j = i + 1; j < num; j++)
            {
                if (strcmp(myArray[i], myArray[j]) > 0)//if(myArray[i] > myArray[j]) 即是错误
                {
                    temp = myArray[i];
                    myArray[i] = myArray[j]; //交换指针值 改变指针指向
                    myArray[j] = temp;
                }
            }
    }
    int main(void)
    {                        //存储char *类型地址的数组 -指针数组
        char *myArray[5] = { "Hello","world","Pumpkin","Comparative","Baby" };
    
        int num = sizeof(myArray) / sizeof(myArray[0]);
        puts("--------排序之前--------:");
        printMyArray(myArray, num);
    
        puts("--------排序之后--------:");
        sort(myArray, num);
        printMyArray(myArray, num);
        system("pause");
    }

    指针做输入第二种模型:

    #define _CRT_SECURE_NO_WARNINGS
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
        //void printMyArray_error(char **myArray, int num)
        // 不能用这种         char ** 与char[5][30]    的间接级别不同
        //指针布长不一样 指针所指数据内存空间的数据类型不一样
    void printMyArray(char myArray[][30], int num)
    {
        for (int i = 0; i < num; i++)
            printf("%s
    ", *(myArray + i));
    }
    void sortMyArray2(char (*myArray)[30], int num)
    {
        char tempBuf[30];
        for (int i = 0; i < num - 1; i++)
            for (int j = i; j < num; j++)
                if (strcmp(myArray[i], myArray[j]) > 0)
                {
                    strcpy(tempBuf, myArray[i]);//交换内存块
                    strcpy(myArray[i], myArray[j]);
                    strcpy(myArray[j], tempBuf);
                }
    }
    int main(void)
    {
        char myArray[5][30] = { "Hello","world","Pumpkin","Comparative","Baby" };
        int num = sizeof(myArray) / sizeof(myArray[0]);
    
        puts("--------排序之前--------:");
        printMyArray(myArray, num);
    
        sortMyArray2(myArray, num);
        puts("--------排序之后--------:");
        printMyArray(myArray, num);
    
        system("pause");
    }
  • 相关阅读:
    js操作cookies
    努力挣钱只是不希望我的爱情受到别人金钱的考验罢了
    ASP 删除字符串开始或/和末尾处指定字符(串)
    lhgdialog 窗体之间的传值
    vs2010教程开始
    打开某些网页时IE弹出安全警告
    flex 版本问题
    【jQuery插件】用于瀑布式布局(砖块)的插件jquery Masonry
    自动根据动态的intput计算值
    js日期格式“补零”
  • 原文地址:https://www.cnblogs.com/Yang-Xin-Yi/p/13543841.html
Copyright © 2011-2022 走看看