嵌入式初学-C语言-二四

Void与void*的区别

定义:

Void:空类型,是数据类型的一种

Void*:是指针类型,是指针类型的一种,可以匹配任何类型的指针,类似于通配符

Void

说明:void作为返回值类型使用,表示没有返回值,作为参数,表示形参列表为空,在调用函数时不能给实参

// 函数声明
void fun(void); // 等效于 void fun();
// 函数调用
fun();

Void*

说明:

  1. void*是一个指针类型,但是该指针的数据类型是不明确的,无法通过解引用获取内存中的数据,因为void*不知道访问几个内存单元
  2. void*是一种数据类型,可以作为函数返回值类型,也可以作为形参类型
  3. void*类型的变量在使用之前必须强制类型转换
int *p = (int*)malloc(4);
double *p2 = (double*)malloc(8);

举例:

#include <stdio.h>
#include <stdlib.h>
// 函数定义
void* fun(void* p) // 指针函数(返回值类型是指针的函数,此时返回的是不明确类型,需要外部强转)
{
int *p;
// double *p;
// long *p;
// char *p;
return p;
}
// 函数调用
void main()
{
int *p;
void* a = fun(p);// 这种接收方式,实际上没有意义
printf("%p\n",a);// 可以正常打印,打印出一个地址
*a = 10;// 编译报错,void*变量不能解引用访问数据
int *w = (int*)a;
*w = 10;// 编译和运行正常,void*变量a在使用前已经强制类型转换了,数据类型明确了,访问的内存
单元明确了。
}

说明:

  1. void*作为返回值类型:这个函数可以返回任何类型(char*  int*  double*)的指针
  2. void*作为形参的类型:这个函数在调用时,可以给任意类型的指针

总结:

  1. void*类似于通配符,不能对void*类型的变量解引用(因为不明确内存单元的大小)
  2. void*在间接访问(解引用)前要强制类型转换,但不能太随意,否则存和取的数据类型不一致

内存操作

我们对于内存的操作借助于string.h这个库提供的内存操作函数。

内存填充

头文件:#include<string.h>

函数原型:void*memset(void*s,int c,size_t n);

函数功能:填充s开始的堆内存前n个字节,使得每个字节值为c

函数参数:

  1. void*s:待操作内存的首地址
  2. int c:填充的字节数据
  3. size_t n:被填充的字节数

返回值:返回s

注意:c常常设置为0,用于动态内存初始化

/**
* 内存操作函数-memset
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void test1()
{
// 在堆内存申请了一块存储空间
int *p = (int*)malloc(4 * sizeof(int));
if(!p)
{
puts("内存分配失败!");
return;// 后续代码不需要执行
}
// 给这块内存进行初始化操作(填充)
memset(p,0, 4 * sizeof(int));
printf("%d\n",*(p+1));
// 内存使用完毕,进行回收
free(p);
p = NULL;
}
int main()
{
test1();
return 0;
}

内存拷贝

头文件:#include<string.h>

函数原型:

  1. void*memcpy(void*dest,const void*src,size_t n);适合目标地址与源地址内存无重叠的情况
  2. void*memmove(void*dest,const void*src,size_t n);

函数功能:拷贝src开始的堆内存空间前n个字节到dest对应的内存中

函数参数:

  1. void*dest:目标内存的首地址
  2. void* src:源内存首地址
  3. size_t n:被拷贝的字节数

返回值:返回dest

注意:内存申请了几个内存空间就访问几个内存空间,否则数据不安全

注意:memcpy与memmove一般情况下是一样的,更建议使用memmove进行内存拷贝因为mememove函数是从自适应(从后往前,从前往后)拷贝,当我们被拷贝的内存和目的地的内存有重叠时数据不会出现拷贝错误,而memecpy函数是从前往后拷贝,当被拷贝的内存和目的地有重叠时数据会出现拷贝错误

/**
* 内存操作函数-memcpy|memmove
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void test1()
{
// 申请内存
// int *p1 = (int*)malloc(4 * sizeof(int));
// int *p2 = (int*)malloc(6 * sizeof(int));
// 给p1,p2填充数据,可以使用for循环..
// for(int i = 0; i < 4; i++)
// p1[i] = 10 + i;
// memset(p2,0,6 * sizeof(int));
// 创建数组
int p1[4] = {11,12,13,14};
int p2[6] = {21,22,23,24,25,26};
// 将p1中的数据通过内存拷贝函数,拷贝到p2
// memcpy(p2+2,p1+1,2*sizeof(int)) // int p2[6] = {21,22,12,13,25,26}
memmove(p2+2,p1+1,2*sizeof(int));
// 测试输出数组
for(int i = 0; i < 4; i++)
printf("%4d",p1[i]);
printf("\n");
for(int j = 0; j < 6; j++)
printf("%4d",p2[j]);
printf("\n");
// 如果使用手动分配的指针,一定要记得释放内存
// free(p1);
// free(p2);
// p1 = NULL;
// p2 = NULL;
}
int main()
{
test1();
return 0;
}

内存比较

头文件:#include<string.h>

函数原型:int memcmp(void*dest,const void*src,size_t n)

函数功能:比较src和dest所代表的内存前n个字节数据

函数参数:

  1. void*dest:目标内存首地址
  2. const void*src:源内存首地址
  3. size_t n:比较的字节数

返回值:

  1. 0:数据相同
  2. >0:dest中的数据大于src(目标大于源)
  3. <0:dest中的数据小于src

注意:n一般和src,dest的总容量一样,如果不一样内存比较的结果就不确定

案例:

/**
* 内存操作-memcmp
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void test1()
{
// 申请内存
int *p1 = (int*)malloc(3*sizeof(int));
int *p2 = (int*)malloc(4*sizeof(int));
// int p1[4] = {1,0,3,6};
// int p2[4] = {1,2,3,4};
// int result = memcmp(p1,p2,4*sizeof(int));
*p1 = 65;
*p2 = 70;
char *a = (char*)p1;// 类型转换
char *b = (char*)p2;
printf("%c,%c\n",*a,*b);
int result = memcmp(a+1,b+1,1*sizeof(char));
printf("%d\n",result);
}
int main()
{
test1();
}

内存查找

头文件:#include<string.h>

函数原型:void *memrchr | *memchr(const void *s, int c, size_t n);

函数功能:在s开始的堆内存空间前n个字节中查找字节数据c

函数参数:

  1. const void *s:待操作内存首地址;
  2. int c:待查找的字节数据
  3. size_t n:查找的字节数

返回值:返回查找到的字节数据地址

注意:如果内存中没有重复数据,memchr和memrchr结果是一样的,若果有重复数据memchr和memrchr结果就不一样

案例:

/**
* 内存操作-memchr | memrchr
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 声明外部函数
extern void *memrchr(const void *s,int c,size_t n);
void test1()
{
// 申请内存
int *p = (int*)malloc(4*sizeof(int));
if(!p)
{
puts("内存分配失败!");
return;
}
// 给变量赋值
for(int i = 0; i < 4; i++)
{
p[i] = i * 2;
}
p[3] = 4;
// 输出
for(int i = 0; i < 4; i++)
{
printf("%d,",p[i]);
}
printf("\n");
// 内存查找 memchr
int *x = (int*)memchr(p,4,4*sizeof(int));
printf("%p--%p--%d\n",x,p,*x);
// 内存查找 memrchr
int *y = (int*)memrchr(p,4,4*sizeof(int));
printf("%p--%p--%d\n",y,p,*y);
// 回收内存
free(p);
p = NULL;
}
int main()
{
test1();
}

本章小结

1.  指针就是地址。变量的指针就是变量的地址。指针变量就是地址变量。

     指针是地址本身,指针变量是用来存放地址的变量。

2.  指向   把谁的地址存放在指针变量中,该指针变量就指向谁

3.  深入掌握对数组操作中怎样正确使用指针。

4.  有关指针变量的定义形式归纳比较,见表8-4。

5.  指针运算小结

  ① 指针变量加(减)一个整数

       p++、p--、p+i、p-i、p+=i、p-+i 等均使指针变量p加(减)一个整数。改变了指向的地址值。

  ②  指针变量赋值 

         不要把一个整型数赋给指针变量

   ③  指针变量可以赋空值

          p=NULL    即不指向任何变量

   ④  两个指针变量可以相减

         两个指向同一数组的指针变量可以相减,得到两个指针之间元素的个数,但相加无意义

   ⑤  两个指针变量比较

         两个指向同一数组的指针变量可以比较,指向前面元素的指针“小于”指向后面元素的指针变量

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值