## 内存
- 即:软件在运行时,用来临时存储数据的,点击保存后,才会存储到硬盘中
- 内存地址:操作系统为了更好地管理内存里的数据,会把内存以字节为单位进行划分为好多小格子,其中一个字节管理一个内存地址
- 内存地址的规则:
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;
}