C语言之指针进阶
指针数组<—>数组指针、动态内存、内存四区
-
指针数组本质就是数组
- 多个指针的集合
-
数组指针本质就是指针
-
二级指针:指向指针的指针
**p **(p+1) * (*p+1) * (*(p+1))+1) * (p[i]+j)) * (*(p+i)+j))
指针数组
-
指针数组通常用来处理字符串,不需要大小
//对于指针数组通常是用来处理字符串 char* str = "ASEF"; //不需要大小 puts(str); char* pStr[3] = { "ERGFDVFD","IMiss","idfSDFef" }; //pStr[0] 等效str for (int i = 0; i < 3; i++) { puts(pStr[i]); } print(pStr,3); char strArray[10] = { "FEFJ" }; //字符串结束表\0 也要算在里面 strArray[0] = 'L'; puts(strArray);
-
本质就是数组
-
多个指针的(一级指针)集合
int* pArray[3] = { iNum,iNum + 1,iNum + 2 }; //pArray[0] 是一个指针变量 //pArray[1] 是一个指针变量 //pArray[2] 是一个指针变量
数组指针
-
作用单一
-
常用于表示二维数组
//通常情况数组指针用来表示二维数组的 pArray = array; printf("第一种指针表示二维数组:\n"); for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { printf("%d\t", pArray[i][j]); } printf("\n"); } printf("第二种指针表示二维数组:\n"); for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { printf("%d\t", *(*(pArray+i)+j)); } printf("\n"); } printf("第三种指针表示二维数组:\n"); for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { //*(pArray + i) 等效pArray[i] printf("%d\t", *(pArray[i] + j)); } printf("\n"); } printf("第四种指针表示二维数组:\n"); for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { //*(pArray + i) 等效pArray[i] //*(pArray + i) 等效 (pArray+i)[0] 这种很少见 // *(*(pArray+i)+j) 让*(pArray+i)等效X //*(X+j) X[j] printf("%d\t", (*(pArray+i))[j]); } printf("\n"); } printArray(array, 2, 2);
-
传参的时候,表示二维数组,所以可以传入数组指针
-
在这里数组指针是一个指针变量,本质传入的是参数
void printArray(int(*pArray)[2], int row, int cols) { printf("数组指针传参打印数组:\n"); for (int i = 0; i < row; i++) { for (int j = 0; j < cols; j++) { printf("%d\t", pArray[i][j]); } printf("\n"); } }
-
数组下标有多少列数组下标就是多少
-
数组指针产生的是一个指针变量,指针变量指向的是一个数组
//int(*pArray)[2] = NULL; 这句与下面两句效果一样 //括号不能少 int(*pArray)[2]; pArray = NULL;
数组指针解析
-
首先把数组指针+1这种操作解析为 数组指针+ sizeof(指针所指向的类型)*偏移量
p+n //解析为: p+ sizeof(指针所指向的类型)*n; int(*pArray)[2] //指针的类型: int(*)[2] 去掉变量名 //指针所指向的类型: 去掉变量名和* : int[2] pArray+sizeof(int[2])*1; //通常情况数组指针用来表示二维数组的
指针函数
-
所有传参方式都是采用赋值的方式
-
值传递(传普通变量)
void modify(int a) { a = 100; }
-
地址传递(传指针)
void modifyByPoint(int* a) { *a = 100; //传入是一级指针,修改的是*a这个指针 //int* a=# //*a=100; //*a 等效num }
-
不能返回局部变量的地址
int* returnValue() { int temp = 123; return &temp; }
-
修改指针指向
//通过子函数实现修改指针的指向 int g_num = 10001; void modifyPoint(int* p) { p = &g_num; } void modifyPointCrrect(int** p) { *p = &g_num; //在当前函数中需要修改实参的值(指针的指向的值) } void printData(int* p) { p = &g_num; //想要实现这样赋值操作,须要传入二级指针 }
动态内存申请
-
动态内存申请也叫堆内存申请,动态数组
-
需要手动申请,手动释放一段内存
-
从哪里申请的内存,释放的时候就要在哪里释放
-
同一段内存只能被释放一次
-
栈内存:每个函数都有自己的一段栈内存,系统自动回收
-
本质就是函数调用
-
单纯的内存申请
void *malloc(size_t size);
-
自动初始化申请
void* calloc(size_t size,int count); //malloc(sizeof(int)*3) int* pC = (int*)calloc(3, sizeof(int)); assert(pC); for (int i = 0; i < 3; i++) { printf("%d\t", pC[i]); } printf("\n"); free(pC); pC = NULL;
-
重新申请
void* realloc(void* p,size_t size); //realloc 内存不够再次申请 int* pVector = (int*)calloc(3, sizeof(int)); assert(pVector); //0,0,0 //realloc 重新申请的内存是要打印原来内存 pVector = (int *)realloc(pVector, sizeof(int) * 6); assert(pVector); pVector[5] = 100; //pVector[4] pVector[3] for (int i = 0; i < 6; i++) { printf("%d\t", pVector[i]); } printf("\n"); free(pVector); pVector = NULL;
-
内存释放
void free(void *p);
-
申请单个变量内存
//强制转换 +malloc
int* pInt =(int *)malloc(sizeof(int) * 1);
float* pFloat = (float*)malloc(sizeof(float) * 1);
double* pDouble = (double*)malloc(sizeof(double) * 1);
//float和double与int的用法一样
//申请内存可能存在失败
if (pInt == NULL)
return;
printf("%d\n", *pInt);
*pInt = 100;
//一个指针只要做了动态内存申请,就可以直接充当变量的用法
printf("%d\n", *pInt);
free(pInt);
pInt = NULL; //养成这个习惯,避免悬空指针(野指针)
char* pChar = (char*)malloc(sizeof(char));
assert(pChar); //当指针为空时候,调用abort终止程序,并且显示程序中断行数,需要断言头文件 assert.h
*pChar = 'A';
putchar(*pChar);
free(pChar);
pChar = NULL;
申请多个变量内存(等同于一个数组)
int Num = 0;
scanf_s("%d", &Num);
//int array[Num];
int* pArray = (int*)malloc(sizeof(int) * Num); //等效pArray[Num]这样的数组
-
直接充当一个数组用即可
for (int i = 0; i < Num; i++) { pArray[i] = i; //*(pArray+i)=i; printf("%d\t", pArray[i]); } free(pArray); pArray = NULL;
-
长度可以根据用户决定
-
可以改变数组指向
char* pStr = (char*)malloc(sizeof(char) * 10); assert(pStr); char str[10] = { "Youngblood" }; //strcpy去复制,或者用字符赋值 //pStr = "Youngblood"; //这样写会改变指向 printf("\n"); int i = 0; while (str[i] != '\0') { pStr[i] = str[i]; i++; } pStr[i] = '\0'; puts(pStr); pStr++; //指针指向一段内存更灵活 putchar(pStr[0]); //同一段内存,只能被释放一次 pStr--; free(pStr); pStr = NULL;
动态内存申请二维数组
-
二级指针申请内存
void initArray(int **array, int row, int cols) { for (int i = 0; i < row; i++) { for (int j = 0; j < cols; j++) { array[i][j] = cols * i + j; } } } void printArray(int** array, int row, int cols) { for (int i = 0; i < row; i++) { for (int j = 0; j < cols; j++) { printf("%d\t", array[i][j]); } printf("\n"); } } int* p1D = (int*)malloc(sizeof(int) * 4); //p1D[i]都是一个整形变量 free(p1D); p1D = NULL; int** pArray=(int**)malloc(sizeof(int*) * 4); //pArray[i] 都是一个一级指针 assert(pArray); for (int i = 0; i < 4; i++) { pArray[i] = (int*)malloc(sizeof(int) * 3); } initArray(pArray, 4, 3); printArray(pArray, 4, 3); for (int i = 0; i < 4; i++) { free(pArray[i]); pArray[i] = NULL; } free(pArray); pArray = NULL;
-
数组指针申请内存
void initArray1(int array[][3], int row, int cols)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < cols; j++)
{
array[i][j] = cols * i + j;
}
}
}
void printArray1(int array[][3], int row, int cols)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < cols; j++)
{
printf("%d\t", array[i][j]);
}
printf("\n");
}
}
int(*p)[3] = NULL;
//(指针类型)malloc(sizeof(指针所指向类型) * 4);
p = (int(*)[3])malloc(sizeof(int[3]) * 4);
initArray1(p, 4, 3);
printArray1(p, 4, 3);
free(p);
p = NULL;
自定义函数申请内存
-
传参的方式
void createArrayTwo(int** p,int size) { *p = (int*)malloc(sizeof(int) * size); } int main() { createArrayTwo(&array, 3); for (int i = 0; i < 3; i++) { array[i] = i; printf("%d\t", array[i]); } printf("\n"); free(array); array = NULL; return 0; }
-
返回值的方式
- 利用了堆内存不会被系统自动回收特性
int* createArray(int size) { int* p = (int*)malloc(sizeof(int) * size); //有效利用了堆内存不会被系统自动回收特性 return p; } int main() { int* array = createArray(3); //使用数组 也就是使用申请内存 for (int i = 0; i < 3; i++) { array[i] = i; printf("%d\t", array[i]); } printf("\n"); free(array); array = NULL; return 0; }
内存四区
-
代码区
- 存放程序的二进制代码
-
栈区
- 存放局部变量,子函数参数等,内存系统自动回收
- 每一个函数的栈区都是不一样,函数名就是函数占用的地址
-
堆区
- 手动申请手动申请的
- malloc 、realloc、 calloc 申请内存都是堆区
- 堆区需要 free释放
-
全局区(静态区)
- 常量
- 静态变量
- 全局变量
栈区----->全局区
#include <stdio.h> int Max(int a, int b) { return a > b ? a : b; } int Min(int a, int b) { return a > b ? b : a; } int main() { int a = 1; int b = 2; int i = 0; printf("%d\t%d\t%d\t", i++, i++, i++); //打印的是2,1,0;从右往左入栈 int max = Max(1, 2); int min = Min(1, 2); char* str = "ILoiy"; //str[0] = 'A'; 错误代码 int* p = NULL; //*p = 100; 错误代码 //指针充当变量 //用变量的地址 int num = 0; p = # *p = 100; //堆内存初始化 p = (int*)malloc(sizeof(int)); *p = 100; printf("%d\n", *p); free(p); p = NULL; char* pstr[3] = { "Point","Feasible","d" }; pstr[0] = "Hidden"; char* ppstr = "Il"; //本质是存的是常量的字符串的地址的,并不是直接存的字符串 ppstr = "Kedah"; //可以修改指向,但是不能修改莫一个常量区字符串的值 puts(ppstr); //长度只对数组又影响 return 0; }
本文来自博客园,作者:{oy},转载请注明原文链接:https://www.cnblogs.com/Oysen/p/17005610.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话