东山县建设官方网站,产品 网站建设,阿里云网站用什么做的,定制公交appC语言#xff1a;指针详解 1#xff1a;指针的基本概念1#xff1a;什么是指针2#xff1a;为什么要引入指针3#xff1a;指针的作用4#xff1a;指针的类型 2#xff1a;指针的声明与初始化1#xff1a; 指针的声明2#xff1a; 指针的初始化 3#xff1a;指针的操作… C语言指针详解 1指针的基本概念1什么是指针2为什么要引入指针3指针的作用4指针的类型 2指针的声明与初始化1 指针的声明2 指针的初始化 3指针的操作1 取地址操作符 2 解引用操作符 * 3 指针的算术运算4 指针的比较规则与应用4.1 指针的相等性比较4.1.2指针的相等性比较 4.1.3规则 4.2 指针的大小比较4.2.1大小比较( )4.2.2规则 4.3指针比较的合法性4.3.1合法的指针比较4.3.2非法的指针比较 4.4指针比较的应用场景4.4.1数组遍历4.4.2字符串处理4.4.3动态内存管理 4指针数组与数组指针1指针数组Array of Pointers1.1定义1.2声明1.3使用1.4应用场景 2数组指针Pointer to an Array2.1定义2.2声明2.3使用2.4应用场景 3指针数组与数组指针的区别3.1声明格式 5函数指针与指针函数1函数指针Function Pointer1.1声明1.2声明一个函数指针并初始化1.3使用 2指针函数Pointer Function2.1声明2.2示例2.3使用 3区别总结3.1使用场景3.1.1函数指针3.1.2指针函数 4总结 6字符数组和字符指针1字符串的实现1.1案例两种实现方式 2字符数组与字符指针的联系2.1字符数组2.2字符指针 3字符串作为形式参数3.1实参与形参都可以是字符数组3.2实参用字符数组形参用字符指针再函数内部不能对字符串中的字符做修改3.3实参是指针类型形参是字符数组3.4案例3.5main函数原型 7野指针、空指针、空悬指针1野指针1.1定义1.2危害2野指针产生的场景2.1遍历未初始化通过指针访问该变量2.2指针未初始化2.3指针指向的内存空间被free回收了2.4指针函数中 直接返回了局部变量的地址 3如何避免野指针 2空指针1定义 3空悬指针4void 与void* 的区别4.1定义4.2void:4.2.1说明 4.3void*:4.3.1说明4.3.2举例: 8动态内存分配1malloc1.1说明1.2案例 2calloc2.1说明2.2案例 3realloc3.1说明3.2案例 4free4.1:说明 5:注意1: 检查内存分配是否成功2.:释放内存3.:内存初始化4: realloc 的使用 9:内存操作1:常用的内存操作函数1:内容填充memset2:内容拷贝(memcpy || memmove)3:内存比较(memcmp)4:内存查找(memchr || memrchr) 1指针的基本概念
1什么是指针
指针是一个变量其值为另一个变量的内存地址。换句话说指针“指向”另一个变量的存储位置。指针的类型决定了它所指向的变量类型。通过指针我们可以直接访问和操作内存中的数据这使得指针在C语言中具有非常强大的功能。
2为什么要引入指针
为函数修改实参提供支持为动态内存管理提供支持为动态数据结构链表队列等提供支持。为内存访问提供另一种途径。使程序更高效简洁
3指针的作用
直接操作内存指针允许程序员直接访问和操作内存地址从而实现高效的内存管理。动态内存分配通过指针可以动态分配和释放内存例如使用malloc、calloc和free等函数。实现复杂数据结构指针是实现链表、树、图等复杂数据结构的基础。函数参数传递通过指针可以将变量的地址传递给函数从而实现对变量的直接修改。数组操作数组名本质上是一个指向数组首元素的指针通过指针可以方便地操作数组。
4指针的类型
C 语言中的指针类型非常丰富主要包括
基本数据类型的指针如 int *、char * 等。数组指针如 int (*p)[5]。指针数组如 int *arr[5]。函数指针如 int (*p)(int, int)。指针函数如 int *p()。多级指针如 int **p。void 指针如 void *p。结构体指针如 struct Point *p。常量指针与指针常量如 const int *p 和 int *const p。空指针如 NULL。野指针。
2指针的声明与初始化
1 指针的声明
指针的声明格式为
数据类型 *指针变量名;例如
int* p;//声明一个指向 int 类型的指针变量p这里 int表示指针所指向的变量类型* 表示这是一个指针变量p是变量名。
2 指针的初始化
int a 10;
int* p a;//将变量 a 的地址赋给指针p 3指针的操作
1 取地址操作符
取值操作符 用于获取变量的内存地址 。例如
int a 10;
int *p a; // 获取变量a的地址并赋值给指针p2 解引用操作符 *
解引用操作符*用于访问指针所指向的内存中的数据。例如
int a 10;
int *p a;
printf(%d, *p); // 输出10即指针p所指向的内存中的数据3 指针的算术运算
运算类型说明示例指针整数指针指向值按照所指向的类型大小向后移整数个单位int* p arr; p p2;指针-整数指针所指向的值按照所指向的类型大小向前移整数个单位int* p arr; p p -2;指针-指针两个指针相减的结果是他们之间的元素个数int* p arr; int* p2 arr 1; int dis p2 -p1;
4 指针的比较规则与应用
在C语言中指针之间的比较是一种常见的操作通常用于判断指针是否指向同一个地址或者用于比较指针在内存中的相对位置。指针之间的比较操作包括相等性比较 和 !和大小比较、、、。
4.1 指针的相等性比较
4.1.2指针的相等性比较
指针的相等性比较用于判断两个指针是否指向同一个地址。这种比较操作在以下几种情况下非常有用
1、判断指针是否为空
int *p NULL;
if (p NULL) {printf(Pointer is NULL\n);
}2、判断两个指针是否指向同一个变量
int a 10, b 20;
int *p1 a, *p2 b;
if (p1 p2) {printf(p1 and p2 point to the same address\n);
} else {printf(p1 and p2 point to different addresses\n);
}3、判断指针是否指向数组的某个特定位置
int arr[5] {1, 2, 3, 4, 5};
int *p arr 2;
if (p arr 2) {printf(p points to the third element of the array\n);
}4.1.3规则
类型兼容性进行相等性比较的两个指针必须是兼容的类型或者其中一个可以隐式转换为另一个的类型。例如int * 和 char * 是不兼容的类型但 void * 可以与任何类型的指针进行比较。空指针比较空指针NULL可以与任何类型的指针进行比较结果为布尔值。
4.2 指针的大小比较
4.2.1大小比较( )
指针的大小比较 用于判断两个指针再内存中的相对位置。
1、判断指针是否在某个范围内
int arr[5];
int* p arr 2;
if(p arr p arr 5)
{printf(p is within the array bounds\n);
}2、遍历数组
int arr[5] {1,2,3,4,5};
int* p arr;
while(p arr5)
{printf(%d\n,*p);p;
}3、比较两个指针的相对位置
int arr[5] {1, 2, 3, 4, 5};
int *p1 arr 1;
int *p2 arr 3;
if (p1 p2)
{printf(p1 is before p2 in memory\n);
}4.2.2规则
类型兼容性进行大小比较的两个指针必须是兼容的类型或者其中一个可以隐式转换为另一个的类型。同一数组或对象两个指针必须指向同一数组或同一对象的元素或者指向数组或对象的末尾位置即数组或对象的最后一个元素的下一个位置。如果两个指针指向不同的数组或对象比较结果是未定义的。空指针空指针NULL可以与任何指针进行大小比较但结果通常没有实际意义。
4.3指针比较的合法性
4.3.1合法的指针比较
1、同一数组或对象的指针比较
int arr[5] {1, 2, 3, 4, 5};
int *p1 arr;
int *p2 arr 3;
if (p1 p2)
{printf(p1 is before p2 in memory\n);
}2、指针与空指针的比较
int *p NULL;
if (p NULL)
{printf(Pointer is NULL\n);
}3、指针与数组末尾位置的比较
int arr[5] {1, 2, 3, 4, 5};
int *p arr 5; // 指向数组末尾位置
if (p arr)
{printf(p is beyond the array bounds\n);
}4.3.2非法的指针比较
1、指向不同数组或对象的指针比较
#include stdio.hint main() {int arr[5] {1, 2, 3, 4, 5}; // 定义一个整型数组int *p1 arr; // p1指向数组的首元素int *p2 arr 3; // p2指向数组的第四个元素// 比较两个指针的大小if (p1 p2){printf(p1 is before p2 in memory\n);// 如果p1小于p2输出这条信息} else {printf(p1 is not before p2 in memory\n);// 否则输出这条信息}return 0;
}2、未初始化的指针比较
int *p1, *p2;
if (p1 p2) { // 未定义行为printf(p1 and p2 point to the same address\n);
}4.4指针比较的应用场景
4.4.1数组遍历
指针比较常用于数组遍历判断指针是否超出数组范围。例如
int arr[5] {1, 2, 3, 4, 5};
int *p arr;
while (p arr 5)
{printf(%d\n, *p);p;
}4.4.2字符串处理
指针比较也常用于字符串处理判断指针是否到达字符串末尾。例如
char str[] Hello, World!;
char *p str;
while (*p ! \0)
{printf(%c, *p);p;
}
printf(\n);4.4.3动态内存管理
指针比较用于动态内存管理判断指针是否指向已分配的内存块。例如
int *arr (int *)malloc(5 * sizeof(int));
if (arr NULL) {printf(Memory allocation failed\n);
}
else
{int *p arr;while (p arr 5){*p 0;p;}free(arr);
}4指针数组与数组指针 在C语言中“指针数组”和“数组指针”是两个容易混淆的概念但它们的含义和用途完全不同。它们的区别主要在于类型定义和使用方式。 1指针数组Array of Pointers
1.1定义
指针数组 首先 他是数组 是存放指针的数组
1.2声明
指针数组的声明格式如下
类型名 *数组名[数组长度];例如
int *arr[10]; // 声明一个包含10个指针的数组每个指针指向int类型的变量1.3使用
指针数组的每个元素都是一个指针可以分别初始化和使用。例如
int a 10, b 20, c 30;
int *arr[3];//声明一个指针数组 其包含三个指针 int 类型的指针
arr[0] a;// 取 a 的地址
arr[1] b;
arr[2] c;printf(%d %d %d\n, *arr[0], *arr[1], *arr[2]); // 输出10 20 30 *arr[0] 解引用arr[0] 取到arr[0] 所指的值1.4应用场景
• 字符串数组可以使用指针数组来存储字符串数组。
#include stdio.hint main() {char *str[3] {Hello, World, CSDN};// 打印数组中的每个字符串for (int i 0; i 3; i) {printf(%s\n, str[i]);}// 打印 strArray[1]printf(str[1]: %s\n, str[1]);return 0;
}
2数组指针Pointer to an Array
2.1定义
数组指针 就是 指针 指向数组的指针 存放数组的地址
2.2声明
数组指针的声明格式如下
类型名 (*指针变量名)[数组长度];例如
int arr[5] {1, 2, 3, 4, 5};
int (*p)[5] arr;// 声明一个指针 p 指向一个包含 5 个 int 类型元素的数组2.3使用
通过数组指针可以访问数组的元素。例如 // 输出数组arr的第三个元素即3
int main()
{int arr[5] {1,2,3,4,5};int (*pa)[5] arr;int i 0;for(i 0;i 5;i){//输出数组 arr 的元素printf(%d,(*pa)[i]);printf(%d,*(pai);printf(%d,*(*pa i)); }
}2.4应用场景
• 多维数组可以使用数组指针来操作多维数组。
通过指针引用多维数组
表 示 形 式含义地 址a二维数组名指向一维数组a[0即0行首地址a[0,*(a0), *a0行0列元素地址a1a11行首地址a1,*(a1)1行0列元素a[1] [0]的地址2008[1]2,*(a1)2,a[1] [2]1行2列元素a[1] [2] 的地址2012(a[1]2), * ((a1)2), a[1] [2]1行2列元素a12的值元素值为13
#include stdio.h
void aver(int* p,int row,int col)
{int sum 0;int ave 0;int count 0;for (int i 0; i row; i)//行{for (int j 0; j col; j)//列{printf(%d\t, *((pi*col)j));sum *((pi*col)j);count;//计数} printf(\n); }// ave sum /count;printf(aver %d\n,ave sum /count);
}
int main(int argc, char const *argv[])
{int arr[3][4] {10,20,30,40,12,23,34,65,56,76,54,45};int* p arr[0];//指针指向第一行首元素位置int row sizeof(arr)/sizeof(arr[0]); //数组的行数int col sizeof(arr[0])/sizeof(arr[0][0]);//数组的列数aver(p,row,col);return 0;
}
• 函数参数可以将数组指针作为函数参数传递以操作多维数组。
#include stdio.h
void change(int (*p)[3],int len)
{for (int i 0; i len; i){for (int j i1; j len; j){int temp *(p[i]j);*(p[i]j) *(p[j]i); *(p[j]i) temp;} }
}void my_printf(int (*p)[3],int len)
{for (int i 0; i len; i){for (int j 0; j len; j){printf(%-3d \t, *(*(pi)j));}printf(\n);}
}int main(int argc, char const *argv[])
{int arr[][3] {1,2,3,4,5,6,7,8,9}; int len sizeof(arr)/sizeof(arr[0]); printf(转置前:\n);my_printf(arr,len);change(arr,len); printf(转置后\n); my_printf(arr,len);return 0;
}3指针数组与数组指针的区别
特性指针数组Array of Pointers数组指针Pointer to an Array声明type *arr[size];type (*arr)[size];类型数组每个元素是一个指针指针指向一个数组用途存储多个指针可以指向不同对象指向一个数组用于操作整个数组解引用方式*arr[i](*arr)[i]内存布局连续存储指针指针指向其他位置的数据指向一个连续的数组
3.1声明格式
• 指针数组
int *p[5] {1,2,3,4,5}; // 声明一个包含5个指针的数组• 数组指针 int (*p)[5] {1,2,3,4,5}; // 声明一个指向包含5个int的数组的指针5函数指针与指针函数 在 C 语言中指针函数和函数指针是两个容易混淆但功能不同的概念。它们在声明、使用和用途上都有显著的区别。 1函数指针Function Pointer
函数指针是一个指针它指向一个函数的入口地址。通过函数指针可以在运行时 动态地调用不同的函数。
1.1声明
函数指针的声明格式如下
返回值类型 *变量名形式参数列表示例
int (*p)[3];
int (*p)(int a ,int b)1.2声明一个函数指针并初始化
①定义的同时赋值
int (*func)(int, int) add; // 声明一个函数指针指向 add 函数②先定义后赋值
//定义一个普通的函数
int add(int a,int b)(return a b);
//定义一个函数指针
int(*p)(int a,int b);
//给函数赋值
p add;注意 函数指针指向的函数要和函数指针定义的返回值类型形参列表对应否则编译会报错 函数指针是 指针但不能进行指针运算 如p等没有实际意义 函数指针作为形参可以形成回调 函数指针作为形参函数调用时的实参只能是与之对应的函数名。不能带小括号 带了表示调用 不是赋值 函数指针的形参列表中的变量名可以省略 1.3使用
通过函数指针调用函数
int result func(5, 3); // 调用 add 函数
printf(Result: %d\n, result); // 输出 8完整代码示例
#include stdio.hint add(int a, int b) {return a b;
}int subtract(int a, int b) {return a - b;
}int main() {int (*func)(int, int) add; // 声明并初始化函数指针int result func(5, 3); // 通过函数指针调用 addprintf(Result of add: %d\n, result); // 输出 8func subtract; // 改为指向 subtract 函数result func(5, 3); // 通过函数指针调用 subtractprintf(Result of subtract: %d\n, result); // 输出 2return 0;
}输出结果
Result of add: 8
Result of subtract: 22指针函数Pointer Function
指针函数是一个返回指针的函数。换句话说它是一个函数其返回值是一个指针。
2.1声明
指针函数的声明格式如下
指针类型 函数名形参列表2.2示例
假设我们有一个函数create_array它动态分配一个整数数组并返回指向该数组的指针
int* create_array(int size)
{int* arr (int*)malloc(size * sizeof(int));if (arr NULL) {return NULL; // 内存分配失败}return arr; // 返回指向动态分配数组的指针
}2.3使用
int* get(int a)
{int *b a;return b;
}
int main()
{int* a get(5);printf(%d\n,*a);
}案例
//指针函数
//有若干个学生的成绩每个学生有四门成绩
//再用户输入学生序号后能输出改学生的全部成绩用指针函数来实现//定义一个函数传入学生的序号返回学生的所有课程成绩
// p 二维数组
// n 二维数组的索引
// return 学生成绩 行号对应的列数组
float *search(float (*p[4]),int n)
{//定义一个指针用来接收查询到的某个学生的所有课程float *pt;//pt *(pn);//*p[n],p[n]
}int main()
{float score[][4] {{50,43,56,23},{65,7556,56},{11,22,34,65},{55,66,77,88}};int m;float *p;printf(输入学生序号(0~2)\n);scanf(%d,m);printf(第%d个娃考了%d分,m);//用来接收某个学生的所有成绩p search(score,m);//遍历成绩for(int i 0;i 4;i){printf(%-5.2f\t,*(pi));}printf(\n);return 0;
}
3区别总结
特性函数指针Function Pointer指针函数Pointer Function声明格式返回值类型 *变量名形式参数列表指针类型 函数名形参列表用途指向一个函数可以通过指针调用函数返回一个指针通常用于动态分配或返回指针示例int (*p)(int a ,int b)int* p(int a,int b)调用方式p(int a,int b)int* p p(int a,int b)返回值指向函数的指针返回一个指针
3.1使用场景
3.1.1函数指针
• 回调函数
• 在事件处理、多态实现或算法选择中使用函数指针。
• 例如标准库中的qsort函数使用函数指针作为比较函数。
• 动态函数调用
• 在运行时根据条件选择调用不同的函数。
3.1.2指针函数
• 动态内存分配
• 返回动态分配的内存块的指针。
• 例如malloc和calloc返回指向动态分配内存的指针。
• 链表或树的节点创建
• 返回指向新创建的节点的指针。
4总结
• 函数指针是一个指针指向一个函数可以通过指针调用函数。
• 指针函数是一个函数返回一个指针。
• 它们的声明和使用方式不同用途也不同。理解它们的区别可以帮助你更灵活地使用 C 语言。
6字符数组和字符指针
1字符串的实现
再c语言中表示一个字符串有以下两种形式
①用字符串数组存放一个字符串。
②用字符指针指向字符串。
1.1案例两种实现方式
//字符串的两种实现方式
//第一种:使用 字符数组 实现字符串
void str_test1()
{//定义一个伪字符串char str[] I LOVE YOU;printf(%s\n.str);
}
//第二种使用 字符指针变量 指向字符串
void str_test2()
{//定义一个伪字符串char *str I LOVE YOU;printf(%s\n,str);}
int main()
{str_test1();str_test2();
}
2字符数组与字符指针的联系
2.1字符数组
由元素构成每个元素存放一个字符
只能对字符数组中的各个元素赋值不能用赋值语句对整个字符数组赋值
2.2字符指针
字符类型的指针变量存放的是地址也能作为函数
#include stdio.h
//字符数组和字符指针的联系
int main()
{char str1[] 你好,xb;char *str2 你好,xb;//我们将数据类型为char的指针变量称为字符指针//测试赋值//srt1 你好,xw;不能对字符数组整体赋值 如果要赋值 使用string 下的 strcpy()str2 你好,xw;printf(%s\n%s\n,str1,str2);char a[] I LOVE YOU;char *b I LOVE YOU;//printf(%c,%c,%c,%c\n,a[2],*(a2),b[2],*(b2));
}3字符串作为形式参数
3.1实参与形参都可以是字符数组
void fun(char str[],int len){……}
void main()
{char str[] I LOVE YOU;fun(str,sizeof(str) /sizeof(str[0]);
}3.2实参用字符数组形参用字符指针再函数内部不能对字符串中的字符做修改
void fun(char *str,int len){}//(str已经是一个常量了不能修改)
void main()
{char *str I LOVE YOU;fun(str,sizeof str /sizeof(str[0]);
}3.3实参是指针类型形参是字符数组
void fun(char str[],int len){str[2] A;
}//(str已经是一个常量了不能修改)
void main()
{char *str I LOVE YOU;fun(str,sizeof(str) /sizeof(str[0]);
}字符数组在创建的时候会在内存中开辟内存空间 内存空间可以存放内存数据字符指针再创建的时候需要依赖于字符数组在内存开辟的内存空间中存放的是数组元素的地址。字符指针的创建依赖于字符数组字符数组可以独立存在而字符指针不能独立存在。 字符数组可以初始化但不能赋值字符指针可以初始化也可以赋值。 3.4案例
一、
#include stdio.h
//字符指针作为函数参数
//用函数调用实现字符串的复制以及长度计算
//定义一个函数实现字符串的拷贝返回字符串长度
//source 拷贝的源字符串
//target 需要保存拷贝数据的目标字符串
//return 字符串的大小int str(char *source,char *target)
{int i 0;while(source[i] ! \0){*(targeti) *(source i);i;}target[i] \0;return i;}int main(int argc,char *argv[])
{char source[20],target[20];printf(输入字符串);scanf(%s,source);int len str(source,target);//int len sizeof(source) / sizeof(source[0]);printf(%s%s,%d,source,target,len);return 0;
} 二、
#include stdio.h//字符指针作为函数参数
//给定一个字符串截取 start 到 end之间的字符串含头不含尾
//定义一个函数实现字符串的截取
//param source 源字符串
//start 开始截取的位置
//end 截取结束的位置
//target 截取后的字符串int str_split(char *source,char *target,int start,int end)
{int i 0,k 0;while(source[i] ! \0){if(i start i end ){target[k] source[i];k;}i;}return k;}int main(int argc,char *argv[])
{char *source sdfafdsfgfer;char target[20];int len str_split(source,target,2,5);printf(%-20s,%-20s,%-20d\n,source,target,len);return 0;
}
3.5main函数原型 argcargv是形参他们俩可以修改 main函数的扩展写法有些编译器不支持编译报警 argc和argv的常规写法 argc存储了参数个数它至少为 1 argv存储了所有参数的字符串形式 参数通过控制台输入 main函数是系统通过函数指针的回调形式调用的
7野指针、空指针、空悬指针
1野指针
1.1定义
指向一块位置区域已经销毁或者访问受限的内存区域外的已经存在或者不存在的内存区域的指针称为野指针。野指针是危险的。
1.2危害 引用野指针相当于访问了非法的内存常常会导致段错误也有可能编译运行不报错。 引用野指针可能会破环系统的关键数据导致系统崩溃等严重后果
2野指针产生的场景
2.1遍历未初始化通过指针访问该变量
int a;
int* p a;//野
pritnf(%d\n,*p);//野2.2指针未初始化
int *p;//野
printf(%d\n,p);//野2.3指针指向的内存空间被free回收了
int* p malloc(4);
*p 12;free(p);
printf(%d\n,*p);//野2.4指针函数中 直接返回了局部变量的地址
int* get_num()
{int a 15;int* p a;//此时p对应的是一个局部变量return 0;
}
main()
{int* p get_num();//此时p是野指针
}3如何避免野指针 指针变量要及时初始化如果暂时没有对应的值建议赋初值NULL。 数组操作主要是遍历和指针运算时注意数组的长度避免越界 指针指向的内存空间被回收建议给这个指针变量赋值为NULL。
int* p (int* )malloc(10);
free(p);
p NULL;指针变量使用之前要检查它的有效性非空校验
int* p NULL;
//if(pNULL)
if(!p)
{return -1;
}2空指针 很多情况下我们不可避免的会遇到野指针比如刚定义的指针无法立即为其分配一块恰当的内存又或者指针指向的内存已经被释放了等等。一般的做法是将这些危险的野指针指向一块确定的内存比如零地址内存NULL。 1定义
空指针即保存了零地址的指针赋值为NULL的指针也就是指向零地址的指针。NULL是空常量它的值是0这个NULL一般存放在内存0x0000 0000的位置这个地址只能存NULL不能被其他程序修改
案例
// 1. 刚定义的指针让其指向零地址以确保安全
char* p1 NULL;
int* p2 NULL;
// 2. 被释放了内存的指针让其指向零地址以确保安全
char *p3 malloc(100);
free(p3);
p3 NULL;
int sum 0;3空悬指针 在C语言中悬空指针指的是指向已删除或释放的内存位置的指针。如果一个指针指向的内存已经被释放但指针本身并未重新指向其他有效的内存地址那么这个指针就变成了悬空指针。悬空指针会引发不可预知的错误并且如果一旦发生就很难定位因此在编程中尽量避免使用悬空指针。 // 2. 被释放了内存的指针让其指向零地址以确保安全
char *p3 malloc(100);
free(p3);
printf(%p,%c\n,p3,*p3);// 此时地址依然可以访问但是地址对应的原本数据不可访问4void 与void* 的区别
4.1定义
void是空类型是数据类型的一种
void*是指针类型是指针类型的一种可以匹配任意类型的指针类似与通配符又被叫做万能指针。
4.2void:
4.2.1说明
void作为返回值类型使用表示没有返回值作为形参表示形参列表为空在调用的时候不能给实参
案例
// 函数定义
void fun(void){..} // 等效于 void fun(){..}
// 函数调用
fun();4.3void*:
4.3.1说明
void是一个指针类型但该指针的数据类型不明确无法通过解引用获取内存中的数据因为 void 不知道访问几个内存单元。void*是一种数据类型可以作为函数 返回值类型 也可以作为 形参类型void*类型的变量在使用之前必须强制类型转换明确它能够访问几个自己的内存空间
4.3.2举例:
#include stdio.h
#include stdlib.h
// 函数定义
void* fun(void* p) // 指针函数返回值类型是指针的函数此时返回的是不明确类型需要外部强转
{int *p;// double *p;// long *p;// char *p;return p;
}
// 函数调用
void main()
{int m 10;int *p m;void* a fun(p);// 这种接收方式实际上没有意义推荐int *a (int*)fun(p);printf(%p\n,a);// 可以正常打印打印出一个地址*a 10;// 编译报错void*变量不能解引用访问数据int *w (int*)a;*w 10;// 编译和运行正常void*变量a在使用前已经强制类型转换了数据类型明确了访问的内存单元明确了。
}注 void*作为返回值类型这个函数可以返回任意类型的指针 void*作为形参类型这个函数在调用时可以给任意类型的指针 void类似于通配符不能对 void 类型的变量解引用因为不明确数据类型所以无法确定内存单元的大小 void*在间接访问解引用前要强制类型转换但不能太随意否则存和取的数据类型不一致 8动态内存分配
1malloc
1.1说明
原型 void* malloc(size_t size);功能分配一块指定大小的内存返回指向该内存的指针。返回值成功时返回指向分配内存的指针失败时返回 NULL 。注意分配的内存未初始化内容是未定义的。
1.2案例
#include stdio.h
#include stdlib.h
#include errno.h
#include string.hint main()
{int* p (int*)malloc(8*sizeof(int));if(!p)//if(p NULL){printf(申请失败);printf(%s\n,strerror(errno));}else{int i 0;for(i 0; i 10;i){*(pi) i;}for(i 0;i 10;i){printf(%d,*(pi));}}free(p);p NULL;return 0;
}2calloc
2.1说明
原型 void* calloc(size_t num, size_t size);功能分配一块足够存储 num 个大小为 size 的元素的内存并将内存初始化为零。返回值成功时返回指向分配内存的指针失败时返回 NULL 。注意分配的内存会被初始化为零。
2.2案例
#include stdio.h
#include stdlib.h
#include errno.h
#include string.h
int main()
{//在堆内申请10块int类型的内存大小int* p (int*)calloc(10,sizeof(int));//如果申请失败 返回-1if(p NULL){printf(申请失败);return -1;}else{int i 0;for(i 0;i 10;i){*(pi) i;}for(i 0; i 10;i){printf(%d,*(pi));}}free(p);p NULL;return 0;
}3realloc
3.1说明
原型 void* realloc(void* ptr, size_t size);功能重新分配一块内存调整其大小为 size 。如果 ptr 是 NULL 行为等同于 malloc(size) 。返回值成功时返回指向新分配内存的指针失败时返回 NULL 原始内存保持不变。注意如果内存块被移动 realloc 会复制原始内存的内容到新位置。
3.2案例
#include stdio.h
#include string.h
#include errno.h
#include stdlib.hint main()
{int* p (int*)malloc(10);//int* p (int*)realloc(p,50);//第一种情况//申请了10块空间//用了5 块//发现不够用//需要在加 少于5个空间的时候//用recalloc//返回的地址仍然是 定义的 p的地址//第二种情况//需要再加超出5 个地址的时候//超出的空间就属于被非法访问//这个时候 realloc就会 重新开辟一块包含空间//这个空间 里边 包含着之前开辟的 p//如下图//这个时候超过五个空间的部分//如果没有赋值//系统就会自动 赋随机值//返回新的地址 旧的地址 ralloc 会释放if(p NULL)//if(!p){printf(申请失败);printf(%s,strerror(errno));}else{int i 0;for(i 0;i 5;i){*(pi) i;}}int* ptr realloc(p, 50);if(ptr ! NULL){ptr p ;int i 0;for(i 5;i 10;i){*(ptri) i;}for(i 0;i 10;i){printf(%d,*(ptri));}}free(ptr);ptr NULL;return 0;
}4free
4.1:说明
原型 void free(void* ptr);功能释放之前分配的内存块。 ptr 必须是之前通过 malloc 、 calloc 或 realloc 分配的内存块的指针返回值无返回值。注意释放内存后 ptr 指向的内存不再属于程序应将 ptr 设置为 NULL 避免悬空指针
5:注意
1: 检查内存分配是否成功
在使用 malloc 、 calloc 或 realloc 后必须检查返回值是否为 NULL 以确保内存分配成功。
2.:释放内存
使用 free 释放动态分配的内存避免内存泄漏。释放内存后应将指针设置为 NULL 避免悬空指针
3.:内存初始化
malloc 分配的内存内容是未定义的如果需要初始化为零可以使用 calloc 或手动初始化。
4: realloc 的使用
如果 realloc 返回 NULL 原始内存仍然有效需要手动释放。如果内存块被移动 realloc 会复制原始内存的内容到新位置
9:内存操作
1:常用的内存操作函数
1:内容填充memset
头文件 #include string.h函数原型 void* memset(void* s,int c,size_t n)函数功能填充s开始的堆内存空间前n个字节使得每个字节值为c函数参数
void* s代操作内存首地址
int c填充的字节数据
size_t n填充的字节数
返回值返回s注意c常常设置为0用于动态内存初始化
案例
#include stdio.h
#include string.h
/*void *memset(void *s,int c,size_t n)
*s:指向要填充的内存区域的指针
*c 要填充的值无符号值
*n 要填充的字节数
*/
int fun1()
{//初始化数组int arr[] {1,2,3,4,5};//将arr 中的前三个元素初始化为0memset(arr,0,3*sizeof(int));int len sizeof(arr) / sizeof(arr[0]);for(int i 0;i len;i){printf(%d,arr[i]);}printf(\n);return 0;
}//初始化数组
int fun2()
{int arr[10];//将arr中的元素都初始化为0memset(arr,0,sizeof(arr));int i 0;for(i 0;i 10;i){printf(%d,arr[i]);}printf(\n);return 0;
}
//设置特定值
int main()
{char str[10];//将字符串初始化为 *memset(str,*,sizeof(str)-1);str[9] \0;printf(%s\n,str);fun1();fun2();return 0;
}
2:内容拷贝(memcpy || memmove) 头文件 string.h 函数原型 void* memcpy(void* dest,const void* src,size_t n) 适合目标地址与源地址内存无重叠的情况 void* memmove(void* dest,const void* src,size_t n)函数功能拷贝src开始的堆内存空间前n个字节到dest对应的内存中。 函数参数
void* dest目标内存首地址
void* src源内存首地址
size_t n拷贝的字节数
返回值返回dest注意内存申请了几个内存空间就访问几个内存空间否则数据不安全注意memcpy与memmove一般情况下是一样的更建议使用memmove进行内存拷贝因为memmove函数是从自适应从后往前或者从前往后拷贝当被拷贝的内存和目的地的内存有重叠时数据不会出现拷贝错误。而memcpy函数是从前往后拷贝当被拷贝的内存和目的地内存有重叠时数据会出现拷贝错误。
案例
#include stdio.h
#include string.h
int main(int argc,char *argv[])
{// 创建源空间和目标空间int src[4] {11,22,33,44};int dest[6] {111,222,333,444,555,666};// 将src中的数据拷贝dest中//dest1 -222 src1 - 22memmove(dest1,src1,2 * sizeof(int));// 测试输出printf(源数组-src:\n);//遍历数组for(int i 0;i 4;i)printf(%-5d,src[i]);printf(\n目标数组-dest:\n);for(int i 0;i 6;i)printf(%-5d,dest[i]);printf(\n);return 0;
}3:内存比较(memcmp) 头文件 #include string.h 函数原型 int memcmp(void *dest,const void *src,size_t n)函数功能比较src和dest所代表的内存前n个字节的数据 函数参数
void* dest目标内存首地址
const void* src源内存首地址
size_t n比较的字节数
返回值
0 数据相同
0 dest中的数据大于src正值
0 dest中的数据小于src 负值
注意n一般和srcdest的总容量一致如果不一致内存比较的结果就不确定了。
#include stdio.h
#include stdlib.h
#include string.h
int main(int argc,char *argv[])
{// 申请内存int* src (int*)malloc(3*sizeof(int));int* dest (int*)calloc(4,sizeof(int));if(!src || !dest){perror(内存申请失败);return -1;}// 对使用malloc申请的空间清零memset(src,0,3*sizeof(int));//初始化src he dest的指向区域*src 65;*(src1) 66;*dest 70;*(dest1) 5;//使用memcmp比较src 和dest的前8个字节int result memcmp(dest,src,2 * sizeof(int));//将src 和 dest 转化为char* 类型的指针a和bchar *a (char*)dest;char *b (char*)src;//用memcmp 比较a 和 b的前一个字节int result2 memcmp(b,a,sizeof(char));printf(%d,%d\n,result,result2);//释放申请的src dest内存free(src);free(dest);//将src dest 置空src NULL;dest NULL;return 0;
}4:内存查找(memchr || memrchr) 头文件 #include string.h 函数原型 void *memrchr(const void *s, int c, size_t n);
void *memchr(const void *s, int c, size_t n);函数功能 memchr: 在 s 指向的内存区域的前 n 个字节中正向查找最后一次出现的字符 c 。如果找到匹配的字符返回指向该字符的指针如果未找到则返回 NULL 。 memrchr: 在 s 指向的内存区域的前 n 个字节中反向查找最后一次出现的字符 c 。如果找到匹配的字符返回指向该字符的指针如果未找到则返回 NULL 。 函数参数
const void *s代操作内存首地址
int c待查找的字节数据
size_t n查找的字节数
返回值返回查找到的字节数据地址注意如果内存中没有重复数据memchr和memrchr结果是一样的如果内存中有重复数据memchr和memrchr结果就不一样
案例(memchr)
#include stdio.h
#include string.h
int main(int argc, char const *argv[])
{//初始化字符串char ch[] hello world;//定义要查找的字符char ch_s o;//计算字符串的长度不包括\0int len strlen(ch);//用 menchr 查找o在字符串的位置char * s (char*)memchr(ch,ch_s,len);//找到了打印位置if(s ! NULL){printf(%d,*s);printf(%c的位置在:%ld\n,ch_s,(long)(s-ch));}else{printf(没找到%c!\n,ch_s);}return 0;
}
案例memrchr
#include stdio.h
#include string.h
#include strings.hint main(int argc, char const *argv[])
{//初始化字符串char ch[] hello world;//定义要查找的字符char char_s o;//计算字符串的长度不包括\0int len strlen(ch);//用memrchr函数查找字符char* s (char*)memrchr(ch,char_s,len);if(s ! NULL){printf(%c的位置在%ld\n,char_s,(long)(s-ch));}else{printf(没找到%c\n,char_s);}return 0;
} 注意 est 置空 src NULL; dest NULL; return 0; } #### 4:内存查找(memchr || memrchr)- 头文件 #include string.h- 函数原型cvoid *memrchr(const void *s, int c, size_t n);void *memchr(const void *s, int c, size_t n);函数功能 memchr: 在 s 指向的内存区域的前 n 个字节中正向查找最后一次出现的字符 c 。如果找到匹配的字符返回指向该字符的指针如果未找到则返回 NULL 。 memrchr: 在 s 指向的内存区域的前 n 个字节中反向查找最后一次出现的字符 c 。如果找到匹配的字符返回指向该字符的指针如果未找到则返回 NULL 。 函数参数
const void *s代操作内存首地址
int c待查找的字节数据
size_t n查找的字节数
返回值返回查找到的字节数据地址注意如果内存中没有重复数据memchr和memrchr结果是一样的如果内存中有重复数据memchr和memrchr结果就不一样
案例(memchr)
#include stdio.h
#include string.h
int main(int argc, char const *argv[])
{//初始化字符串char ch[] hello world;//定义要查找的字符char ch_s o;//计算字符串的长度不包括\0int len strlen(ch);//用 menchr 查找o在字符串的位置char * s (char*)memchr(ch,ch_s,len);//找到了打印位置if(s ! NULL){printf(%d,*s);printf(%c的位置在:%ld\n,ch_s,(long)(s-ch));}else{printf(没找到%c!\n,ch_s);}return 0;
}
案例memrchr
#include stdio.h
#include string.h
#include strings.hint main(int argc, char const *argv[])
{//初始化字符串char ch[] hello world;//定义要查找的字符char char_s o;//计算字符串的长度不包括\0int len strlen(ch);//用memrchr函数查找字符char* s (char*)memrchr(ch,char_s,len);if(s ! NULL){printf(%c的位置在%ld\n,char_s,(long)(s-ch));}else{printf(没找到%c\n,char_s);}return 0;
} 注意 memrchr 是 POSIX 标准的一部分而不是 C 标准库的一部分。所以在某些编译器下不支持如果你的编译器不支持 最好使用标准C库函数 memchr