一:内存
- 即:软件在运行时,用来临时存储数据的,点击保存后,才会存储到硬盘中
- 内存地址:操作系统为了更好地管理内存里的数据,会把内存以字节为单位进行划分为好多小格子,其中一个字节管理一个内存地址
- 内存地址的规则:
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; // 500不可以。不是赋值已分配空间的内存地址
2.1 指针的作用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.2 指针的作用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]; } } }
2.3 指针的作用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; }
2.4 指针的作用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};
- 遍历:利用索引进行遍历 / 利用指针进行遍历
7.1 第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; }
7.2 第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; }