C语言复习6:内存,指针,二维数组

发布于:2025-03-03 ⋅ 阅读:(103) ⋅ 点赞:(0)

## 内存
        - 即:软件在运行时,用来临时存储数据的,点击保存后,才会存储到硬盘中
        - 内存地址:操作系统为了更好地管理内存里的数据,会把内存以字节为单位进行划分为好多小格子,其中一个字节管理一个内存地址
        - 内存地址的规则:
            1. 32位操作系统,内存地址以32位的二进制表示,即一共有2^32次方,大概42亿多个不同的地址 --> 4GB内存
            2. 64位操作系统,内存地址以64位的二进制表示,即最大内存地址为2^64次方,地址范围:0~2^64,最大支持的内存:2^64(字节) --> 17,592,186(GB) --> 17,179(TB)
        - 变量的内存地址:
            在电脑中以十六进制形式存储第一个字节的位置
            获取方式:&变量名
               int a = 10;
               printf("%p\n",&a); //0000004FE98B9FB14



## 指针
        - 指针就是内存地址
        - 把内存地址存起来 -> 指针变量 -> 指针
(指针变量一般会被说成指针,但是我们要清楚真正的指针其实是变量里面记录的内存地址)
        - 指针变量占用的大小,跟数据类型无关,跟编译器有关(32位:4字节;64位:8字节)
        - 给指针变量赋值的时候,不能把一个数值赋值给指针
        - 指针的作用:
             1. 查询数据
             2. 存储数据
             3. 参数传递
             4. 内存管理
                ……
           * 细说指针的作用:
             1. 操作其他函数中的变量
             2. 函数返回多个值
             3. 函数结果和计算状态分开
             4. 方便地操作数组和函数
             
       

        - 指针变量定义格式:`数据类型 * 变量名 ;`
                数据类型 -> 要和指向变量的类型保持一致
                    *    -> 标记符号
                   
                   
        - 查询数据:`* 指针名`
                   *    -> 解引号运算符(注意和定义格式的*的含义区分开来)


        - 存储数据:`* 指针名 = 数据值;`
                 
                 int a = 10;
                 int * p = &a;
                 printf("%d",*p); //10
                 *p = 100;   //利用指针存储或是修改数据
                 printf("%d",*p); //100


        - 给指针变量赋值的时候,不能把一个数值赋值给指针
                  int a = 10;
                  int * p = &a;   // &a可以。是赋值已分配空间的内存地址
                  int * p = 500;  // &a不可以。不是赋值已分配空间的内存地址



## 指针的作用1----操作其他函数中的变量

 #include<stdio.h>
        /*
          定义两个变量,要求交换变量中记录的值
          注意:交换的代码写在一个新的函数swap中
        */

        void swap(int* a,int* b);

        int main() {
            int a=10, b=20;
            swap(&a,&b);
            printf("%d %d",a,b);  // 20 10
            return 0;
        }

        void swap(int* a,int* b) {
            int temp = *a;
            *a = *b;
            *b = temp;
        }


        - - >细节:
           1. 函数中变量的生命周期跟函数相关,函数结束了,变量也会消失
           2. 此时在其他函数中,就无法通过指针使用了
           3. 如果不想函数中的变量被回收,可以在变量前面加static关键字
   

       #include<stdio.h>
        int* method();
        int main(){
            int* p = method();
            printf("拖点时间\n");
            printf("拖点时间\n");
            printf("拖点时间\n");
            printf("拖点时间\n");
            printf("拖点时间\n");
            printf("%d\n",*p);  //没加static之前,a的值为-858993460,加上后:a的值为10
            return 0;
        }

        int* method() {
            static int a = 10; 
            return &a;
        }


        

## 指针的作用2----函数返回多个值

 #include<stdio.h>
        /*
           定义一个函数,求数组的最大值和最小值,并进行返回
        */
        void getMaxAndMin(int arr[],int len,int* max,int *min);

        int main() {
            int arr[] = {17,23,5,20,26};
            int len = sizeof(arr) / sizeof(arr[0]);
            int max = arr[0];
            int min = arr[0];
            getMaxAndMin(arr, len, &max, &min);
            printf("%d %d\n", max, min);
            return 0;
        }

        void getMaxAndMin(int arr[], int len, int* max, int* min) {
             // 求数组最大值
           *max = arr[0];
           for (int i = 1; i < len; i++)
           {
                if (*max < arr[i]) {
                    *max = arr[i];
                }
           }
            //求数组最小值
            *min = arr[0];
            for (int i = 1; i < len; i++)
            {
                if (*min > arr[i]) {
                    *min = arr[i];
                }
            }
        }


## 指针的作用3----函数结果和计算状态分开    

 #include<stdio.h>
        /*
           定义一个函数,将两数相除,获取它们的余数
        */
        int getRemainer(int a, int b, int* res);

        int main() {
            int a = 10;
            int b = 1;
            int res = 0;
            int flag = getRemainer(a, b, &res);
            if (!flag)
            {
                printf("获取到的余数为:%d\n",res);
            }
            return 0;
        }

        int getRemainer(int a, int b, int* res) {
            if (b == 0)
            {
               //停止
                return 1;
            }
            *res = a % b;
            return 0;
        }



## 指针的作用4----方便地操作数组和函数


## 指针的计算
        - 指针中数据类型的作用:获取字节数据的个数
        - 指针加1,即一个步长,指针移动一次的字节个数
        - 指针有意义的操作:
             * 指针跟整数进行加/减操作,每次移动一个步长
             * 指针跟指针进行减操作(间隔步长)

             
        - 指针无意义的操作:
             * 指针跟整数进行乘除操作
             * 指针跟指针进行加/乘/除

        


## 指向不明的指针
        - 野指针:指针指向的空间未分配
        - 悬空指针:指针指向的空间已分配,但是被释放了
        - 没有类型的指针:void *p;
             特点:无法获取数据,无法计算,但是可以接收任意地址,可以接受任意类型指针记录的内存地址
             缺点:void类型的指针,无法获取变量里面的数据,也不能进行加/减的计算


       
## 二级指针和多级指针

 - 二级指针:数据类型(例如:int *) * 二级指针变量;
        - 二级指针的作用1:可以操作一级指针记录的地址
        - 二级指针的作用2:可以获取一级指针记录的地址中所存的值
 
        #include<stdio.h>
        int main() {
            int a = 10;
            int b = 20;
            int* a_p = &a;
            int* b_p = &b;
            printf("a变量的内存地址为:%p\n",a_p);//000000C5FE52FB64
            printf("b变量的内存地址为:%p\n",b_p);//000000C5FE52FB84

            int** a_pp = &a_p;
            int** b_pp = &b_p;

             printf("a变量的内存地址为:%p\n", *a_pp);//000000C5FE52FB64
             printf("b变量的内存地址为:%p\n", *b_pp);//000000C5FE52FB84

             printf("a变量存的值为:%d\n",**a_pp);//10
             printf("b变量存的值为:%d\n",**b_pp);//20

            return 0;
        }



## 数组指针
        - 数组指针:指向数组的指针
        - 数组指针的作用:方便的操作数组中的各种数据
        - 指针数组的细节:数组名参与计算的时候,会退化为第一个元素的指针,除了以下两种特殊情况:
            1. 进行sizeof运算的时候,不会退化,数组名还是整体,代表数组
            2. 利用&数组名获取地址的时候,不会退化,步长 = 数据类型 * 数组长度
       

        #include<stdio.h>
        /*
            利用指针遍历数组
        */
        int main() {
            int arr[] = {11,22,33,44,55};
            int len = sizeof(arr) / sizeof(arr[0]);
            //获取数组的指针,实际上获取的就是数组在内存中的首地址
            int* p1 = arr;
            //或写成:
            int* p2 = &arr[0];
            printf("p1指针所存的内存地址为:%p\n",p1); //0000003D614FFA78
            printf("p2指针所存的内存地址为:%p\n",p2); //0000003D614FFA78

            printf("数组arr占的字节为:%zu\n",sizeof(arr)); //40
            printf("arr的值为数组的首地址:%p\n",arr); //0000003D614FFA78
            printf("arr的值为数组的首地址:%p\n",&arr);0000003D614FFA78
            printf("这里代表的是数组首地址+1等于:%p", arr + 1);//地址值,移动了4个字节
            printf("这里代表的是整个数组+1等于:%p", &arr + 1); // 地址值,移动了40个字节

            //获取数组中的元素
            printf("arr[0]的值为%d\n", *p1);       //11
            printf("arr[1]的值为%d\n", *(p1+1));   //22
            printf("arr[2]的值为%d\n", *(p1 + 2)); //33
            printf("arr[3]的值为%d\n", *(p1 + 3)); //44
            printf("arr[4]的值为%d\n", *(p1 + 4)); //55

            //利用指针遍历数组:
            for (int i = 0; i < len; i++)
            {
                printf("%d ",*(p1 + i));           //11 22 33 44 55
            //  printf("%d ",*p++ );               //11 22 33 44 55
            }

            return 0;
        }



## 二维数组
        - 概念:把多个小数组放到一个大的数组当中
        - 定义格式1:
            数据类型 数组名[m][n] = {        // m是二维数组的长度,代表有多少个一维数组
              {3,6},                    // n是一维数组的长度,代表每个一维数组有几个元素
              {2,7,1,3},
              {9,5,4},
              …
        };


        - 定义格式2:先定义一维数组,再将一维数组加入二维数组内
            int arr1[4] = {3,6};
            int arr2[4] = {2,7,1,3};
            int* arr[2] = {arr1,arr2};

          
         - 遍历:利用索引进行遍历 / 利用指针进行遍历

         ### 第1种定义格式下的二维数组遍历

        #include<stdio.h>
        int main() {
            int arr[3][5]{
                {1,5,2,3,4},
                {9,7,2,4,2},
                {6,4,7,4,3}
            };

            //利用索引进行遍历
            //arr[0]:表示二维数组当中的第一个一维数组:{1,5,2,3,4}
            //arr[1]:表示二维数组当中的第二个一维数组:{9,7,2,4,2}
            //arr[2]:表示二维数组当中的第三个一维数组:{6,4,7,4,3}
            for (int i = 0; i < 3; i++)
            {
                for (int j = 0; j < 5; j++) {        
                    printf("%d ",arr[i][j]);
                }
                printf("\n");
            }

            /*
              遍历结果:
                    1 5 2 3 4
                    9 7 2 4 2
                    6 4 7 4 3
            */

            //利用指针进行遍历
            //二维数组指针类型
            //int[5] * p = arr;
            int(*p)[5] = arr;

            for (int i = 0; i < 3; i++)
            {
                for (int j = 0; j < 5; j++) {
                    printf("%d ", *(*p + j));
                }
                printf("\n");
                //移动二维数组的指针,继续遍历下一个一维数组
                p++;
            }
 
            /*
              遍历结果:
                    1 5 2 3 4
                    9 7 2 4 2
                    6 4 7 4 3
            */

            return 0;
        }
        
           


        ### 第2种定义格式下的二维数组遍历
       

        #include<stdio.h>
        int main() {
            int arr[3][5]{
                {1,5,2},
                {9,7,2,4,2},
                {6,4,7,7}
            };

            //常规下:利用索引进行遍历
            for (int i = 0; i < 3; i++)
            {
                for (int j = 0; j < 5; j++) {
                    printf("%d ", arr[i][j]);
                }
                printf("\n");
            }

            /*
              遍历结果:
                  1 5 2 0 0
                  9 7 2 4 2
                  6 4 7 7 0
            */

            //第一种方法,进行数组拆分,然后重组遍历
            //1.定义3个一维数组
            int arr1[] = { 1,5,2 };
            int arr2[] = { 9,7,2,4,2 };
            int arr3[] = { 6,4,7,7 };
            int* arrArr[3] = { arr1,arr2,arr3 };//把三个一维数组放到二维数组中去

            //2.预先计算每一个数组真实的长度
            int len1 = sizeof(arr1) / sizeof(arr1[0]);
            int len2 = sizeof(arr2) / sizeof(arr2[0]);
            int len3 = sizeof(arr3) / sizeof(arr3[0]);
            int lenArr[] = {len1,len2,len3};//把每个一维数组的长度放到一个新数组中

            for (int i = 0; i < 3; i++)
            {
                for (int j = 0; j < lenArr[i]; j++) {
                    printf("%d ",arr[i][j]);
                }
                printf("\n");
            }
    
            /*
              遍历结果:
                  1 5 2
                  9 7 2 4 2
                  6 4 7 7
            */

             //第二种方法,利用指针进行遍历
             //int* arrArr[3] = { arr1,arr2,arr3 };
             //这个二维数组的指针类型:int*
             int** p = arrArr;  //第二个*是指针变量标识符
             for (int i = 0; i < 3; i++)
             {
                for (int j = 0; j < 5; j++) {
                    printf("%d ",*(*p + j));
               //或:printf("%d ", (*p)[j]);
                }
                printf("\n");
                p++;
            }

           /*
              遍历结果:
                  1 5 2 -858993460 -858993460
                  9 7 2 4 2
                  6 4 7 7 -858993460
            */

            return 0;
        }




## 指针数组
        - 顾名思义,就是存放指针的数组

## 函数指针
        - 格式:返回值类型 (*指针名)(形参列表)
        - 作用:利用函数指针,可以动态的调用函数
 

        #include<stdio.h>
        /*
            函数指针
        */

        void method1();
        int method2(int a, int b);
        int main() {
            //1.定义两个指针分别指向两个函数
            void(*p1)() = method1;
            int (*p2)(int, int) = method2;
            //2.利用函数指针去调用函数
            p1();  // method1()方法被执行了
            int num = p2(10,20);
            printf("%d\n",num); //30
            return 0;
        }

        void method1() {
            printf("method1()方法被执行了\n");
        }

        int method2(int a, int b) {
            return a + b;
        }


网站公告

今日签到

点亮在社区的每一天
去签到