指针(5.动态内存分配、内存函数等)

动态内存分配

malloc
  • 头文件:#include <stdlib.h>

  • 函数功能:void *malloc(size_t size)分配所需的内存空间,并返回一个指向它的指针

  • 函数原型:

    • 函数名:malloc

    • 形参:size_t size内存块大小,以字节为单位。本质是unsigned int

    • 返回值类型:void*返回一个指针,指向已分配大小的内存,若请求失败,返回NULL

int *p = (int *)malloc(4);	//注意刚开始为void类型,需要强制转换
  • 说明:

    1. malloc函数分配的内存没有默认值;

    2. malloc函数申请的内存是连续的;

calloc
  • 头文件:#include <stdlib.h>

  • 函数功能:void *calloc(size_t nitems,size_t size)分配所需的内存空间,并返回一个指向它的指针

    malloccalloc的不同是calloc可以为0

  • 函数原型:void *calloc(size_t nitems,size_t size)

    • 函数名:calloc

    • 形参:

      1. size_t nitems申请多少个;

      2. size_t size一个占多少内存单元

    • 返回值类型:void*,请求失败,返回NULL

int *p = (int *)calloc(3,4);//3个元素,每个4字节
if(!p)printf("内存申请失败!\n");
  • 说明

    1. calloc分配的内存有默认值,默认为0;

    2. calloc申请的内存空间连续;

    3. calloc大多数为数组申请内存;

int a[3] = {1,2,3};//在栈区
int *p = (int *)calloc(3,4);//在堆区
if(!p)printf("申请失败!\n");
//转存
for(int i = 0; i < 3; i++)
{
    p[i] = a[i];
}

free(p);
p = NULL;
realloc
  • 头文件: #include <stdlib.h>

  • 函数功能:重新调整之前调用malloccalloc所分配的ptr所指向内存块的大小。

  • 函数原型: void *realloc(void *ptr,size_t size)

    • 函数名:realloc

    • 形参:

      • void *ptr:是malloc或者calloc的返回值

      • size_t size:重新分配后的内存大小

    • 返回值:void*:该函数返回一个指针,指向已分配大小的内存。如果请求失败,返回NULL

int *p = (int*)malloc(4);
int *w = (int*)realloc(p,20);
// int *q = (int*)realloc(p,0); // 等效于free(p)
free
  • 头文件: #include <stdlib.h>

  • 函数功能:释放之前调用 malloc、calloc、realloc所分配的内存空间,是访问完记得使用NULL置空。

  • 函数原型: void free(void *ptr)

    • 函数名:free

    • 形式参数: void *ptr:calloc,malloc.realloc的返回值

    • 返回值类型:void:没有返回值

  • 注意:

      1. 堆内存中的指针才需要回收,栈中系统会自动回收

      2. 堆内存不能重复回收,运行会报错

说明:

1.堆的内存空间相比较栈要大很多

2.内存分配函数返回的指针变量可以参与运算(只读),但不能被修改(p++或者p+=i 是错误的)

void与void*区别(了解)
  • 定义:

    • void:空类型,数据类型的一种

    • void*:空指针类型,指针类型的一种,可匹配任意类型的指针,类似于通配符

  • void

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

  • 举例:

// 函数声明
void fun(void); 
 // 函数调用
fun();
  • void*

    • 说明:

      • void*是一个指针类型,但该指针的数据类型不明确,无法通过解引用获取内存中的数据, 因为void*不知道访问几个内存单元。

      • void*是一种数据类型,可以作为函数返回值类型,也可以作为形参类型

      • void*类型的变量在使用之前必须强制类型转换,明确它能够访问几个字节的内存空间

#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在使用前已经强制类型转换了,数据类型明确了,访问的内存单元明确了。
}
  • 说明:

    • void*作为返回值类型:这个函数可以返回任意类型( char*,int*,double*等 )的指针。

    • void*作为形参类型:这个函数在调用时,可以给任意类型( char*,int*,double*等 )的指针。

  • 总结:

    • void* 类似于通配符,不能对void*类型的变量解引用(因为不明确内存单元的大小)。

    • void*在间接访问(解引用)前要强制类型转换,但不能太随意,否则存和去的数据类型不 一致。

内存

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 <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;
}
memcpy/memmove拷贝
  • 头文件:#include <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

  • 注意:访问内存空间需和申请的内存空间相同,否则数据不安全

  • 注意:memcpymemmove一般情况下是一样的,建议使用memmove进行内存拷贝;

  • 因为memmove函数是自适应(从后往前或者从前往后)拷贝,当被拷贝的内存和目的地的内存有重叠时,不会拷贝错误。而memcpy函数是从前往后拷贝,被拷贝的内存和目的地内存有重叠时,拷贝错误。

    案例

#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));
    
	// 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;
}
memcmp比较
  • 头文件:#include <string.h>

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

  • 函数功能:比较srcdest内存前n个字节的数据;

  • 函数参数:

    • void *dest:目标内存首地址 *

    • const void* src:源内存首地址

    • size_t n:比较的字节数

  • 返回值:

    • 0 :数据相同

    • >0dest中的数据大于src

    • <0dest中的数据小于src

  • 注意:n一般和srcdest的总容量一样;如果不一样,比较结果不确定。

    案例

#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();
}
memchr/memrchr查找
  • 头文件:#include <string.h>

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

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

  • 函数参数:

    • const void *s:待操作内存首地址;

    • int c:待查找的字节数据

    • size_t n:查找的字节数

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

  • 注意:若内存中无重复数据,memchrmemrchr结果相同;若内存中有重复数据, memchrmemrchr结果不同;

    举例

void *memrchr(..);// 使用时编译会报错,需要使用外部声明
 
// 外部申请
extern void *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();
}

main函数的原型

main函数有多种定义格式,凡是函数相关的结论对main函数同样有效。

  • main函数的完整写法

int main(int argc, char *argv[]){}
int main(int argc, char **argv){}
argc:表示命令行参数的数量。至少为1,因为第一个参数(argv[0]是程序名称或路径)
argv:参数数组,argv[0]为第一个参数,就是程序的名称或路径

案例:

若有一个example程序
    ./example arg1 arg2 arg3	//此时argc的值为4,argv[0] = ./example

扩展写法

 int main(){}
 int main(void){}
 void main(){}
 main(){} ---- int main(){}
 void main(void){}
 int main(int a){}
 int main(int a,int b,int c){}
  • 注意:若函数没有返回值类型,默认返回类型是int

#include  "stdio.h"

int main(int argc,char **argv)
{ 
	int  k;
	for (k=1;k < argc;k++)
	printf("%s\n",argv[k]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值