如何释放动态链接库中申请的内存
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何释放动态链接库中申请的内存相关的知识,希望对你有一定的参考价值。
char *str = NULL;
plist_get_string_val(node, &str);
char *path = string_build_path(backup_dir, str, NULL);
free(str);
plist_get_string_val是动态链接库中导出的一个函数,
free(str)会导致应用程序失败,请问这个应该如何设置?
动态库是别人编译好的
LIBEXPORT_API int mySum(int a,int b) return a+b;
C# 导入定义:
public class RefComm
[DllImport("LibEncrypt.dll",
EntryPoint=" mySum ",
CharSet=CharSet.Auto,CallingConvention=CallingConvention.StdCall)]
public static extern int mySum (int a,int b);
在C#中调用测试:
int iSum = RefComm.mySum(,);
运行查看结果iSum为5,调用正确。第一步试验完成,说明在C#中能够调用自定义的动态链接库函数。
第二步,我定义了字符串操作的函数(简单起见,还是采用前面的函数名),返回结果为字符串:
LIBEXPORT_API char *mySum(char *a,char *b)sprintf(b,"%s",a); return a;
C# 导入定义:
public class RefComm
[DllImport("LibEncrypt.dll",
EntryPoint=" mySum ",
CharSet=CharSet.Auto,
CallingConvention=CallingConvention.StdCall)]
public static extern string mySum (string a, string b);
在C#中调用测试:
string strDest="";
string strTmp= RefComm.mySum("45", strDest);
运行查看结果 strTmp 为"45",但是strDest为空。我修改动态链接库实现,返回结果为串b:
LIBEXPORT_API char *mySum(char *a,char *b)sprintf(b,"%s",a) return b;
修改 C# 导入定义,将串b修改为ref方式:
public class RefComm
[DllImport("LibEncrypt.dll",
EntryPoint=" mySum ",
CharSet=CharSet.Auto,CallingConvention=CallingConvention.StdCall)]
public static extern string mySum (string a, ref string b);
在C#中再调用测试:
string strDest="";
string strTmp= RefComm.mySum("45", ref strDest);
运行查看结果 strTmp 和 strDest 均不对,含不可见字符。再修改 C# 导入定义,将CharSet从Auto修改为Ansi:
public class RefComm
[DllImport("LibEncrypt.dll",
EntryPoint=" mySum ",
CharSet=CharSet.Ansi,CallingConvention=CallingConvention.StdCall)]
public static extern string mySum (string a, string b);
在C#中再调用测试:
string strDest="";
string strTmp= RefComm. mySum("45", ref strDest);
运行查看结果 strTmp 为"45",但是串 strDest 没有赋值。第二步实现函数返回串,但是在函数出口参数中没能进行输出。再次修改 C# 导入定义,将串b修改为引用(ref):
public class RefComm
[DllImport("LibEncrypt.dll",
EntryPoint=" mySum ",
CharSet=CharSet.Ansi,CallingConvention=CallingConvention.StdCall)]
public static extern string mySum (string a, ref string b);
运行时调用失败,不能继续执行。
第三步,修改动态链接库实现,将b修改为双重指针:
LIBEXPORT_API char *mySum(char *a,char **b)sprintf((*b),"%s",a); return *b;
C#导入定义:
public class RefComm
[DllImport("LibEncrypt.dll",
EntryPoint=" mySum ",
CharSet=CharSet.Ansi,CallingConvention=CallingConvention.StdCall)]
public static extern string mySum (string a, ref string b);
在C#中调用测试:
string strDest="";
string strTmp= RefComm. mySum("45", ref strDest);
运行查看结果 strTmp 和 strDest 均为"45",调用正确。第三步实现了函数出口参数正确输出结果。
第四步,修改动态链接库实现,实现整数参数的输出:
LIBEXPORT_API int mySum(int a,int b,int *c) *c=a+b; return *c;
C#导入的定义:
public class RefComm
[DllImport("LibEncrypt.dll",
EntryPoint=" mySum ",
CharSet=CharSet.Ansi,CallingConvention=CallingConvention.StdCall)]
public static extern int mySum (int a, int b,ref int c);
在C#中调用测试:
int c=0;
int iSum= RefComm. mySum(,, ref c);
运行查看结果iSum 和c均为5,调用正确。
经过以上几个步骤的试验,基本掌握了如何定义动态库函数以及如何在 C# 定义导入,有此基础,很快我实现了变长加密函数在 C# 中的调用,至此目标实现。
三、结论
在 C# 中调用 C++ 编写的动态链接库函数,如果需要出口参数输出,则需要使用指针,对于字符串,则需要使用双重指针,对于 C# 的导入定义,则需要使用引用(ref)定义。
对于函数返回值,C# 导入定义和 C++ 动态库函数声明定义需要保持一致,否则会出现函数调用失败。定义导入时,一定注意 CharSet 和 CallingConvention 参数,否则导致调用失败或结果异常。运行时,动态链接库放在 C# 程序的目录下即可,我这里是一个 C# 的动态链接库,两个动态链接库就在同一个目录下运行。
动态内存管理详解
申请内存的方式
最简单的申请内存方式:创建变量
(1)局部变量:出了当前代码块就释放内存
(2)全局变量:程序运行结束就释放内存
(3)静态变量:程序运行结束就释放内存
动态内存管理 (即内存的申请和释放 )
目的:
程序运行过程中,更灵活的进行内存的申请和释放~
即:随时需要就申请,随时不要就释放
(一般这样理解)
动态往往是和“运行时”相关~
静态往往是和“编译器”相关~
申请 / 释放操作
由C语言提供的几个库函数完成
1.申请内存:malloc函数
函数原型:
void* malloc (size_t size);
malloc注意事项:
- size表示申请多少个字节的内存
- malloc申请到的是一块连续的内存空间
- 申请到的内存空间并没有指定类型
- 返回值为void*类型,只关注内存大小,不关注大小(大小隐含在参数size里)
- 如果申请成功,则返回申请到内存的起始位置的地址
- 如果申请失败,就会返回一个NULL,在笔试/面试中使用malloc时一定要检查
- malloc申请到的内存,内存会一直存在,直到手动释放(free)或者程序结束为止
代码示例:
int main() {
//malloc申请到的这块空间,可以当成一个“数组”去使用
//其用法类似于数组
int* p = (int*) malloc(4 * sizeof(int));
for (int i = 0; i < 4; i++) {
p[i] = i;
}
for (int i = 0; i < 4; i++) {
printf("%d\\n", p[i]);
}
system("pause");
return 0;
}
2.释放内存:free函数
函数原型:
void free (void* ptr);
功能:释放一个通过动态内存方式申请到的内存空间
free注意事项:
- 若参数 ptr 指向的空间不是动态申请的,会发生未定义行为
- 参数必须传入malloc等动态申请内存函数返回的地址,返回的是什么,就传入什么
- 不能重复释放同一块内存空间
错误代码示例
//错误代码1
int num = 10;
free(&num);
//错误代码2
int* p = (int*)malloc(4 * sizeof(int));
free(p + 1);
//错误代码3
int* p = (int*)malloc(4 * sizeof(int));
free(p);
free(p);
//错误代码4
int* p = (int*)malloc(4 * sizeof(int));
p = (int*)malloc(4 * sizeof(int));
free(p);
等进程结束来释放内存,大概率会发生内存泄漏(memory leak)
内存泄漏的危害:
如果一直持续不断的申请内存而不主动释放,系统中剩余的内存就会越来越少,最终会导致没有内存可以使用,此时程序也会崩溃,若是服务器程序,会更严重
在C语言中,没有太好的方法避免内存泄漏,最稳妥的方法就是不用malloc
C++中,可以使用智能指针,一定程度可以缓解内存泄漏问题
Java / Python / Go / JS等中,通过垃圾回收机制(GC),来解决内存泄露问题
垃圾回收机制,相对于智能指针,能够很大程度的解决内存泄漏问题
3.calloc函数
函数原型:
void* calloc (size_t num, size_t size);
calloc注意事项:
- calloc申请到的内存空间,会自动初始化为全0
- 为num个元素的数组分配一块内存,每个元素是size字节长,并将其所有位初始化为零。
实际开发中并不常用calloc 简单介绍一下即可
4.realloc函数
函数原型:
void* realloc (void* ptr, size_t size);、
功能:针对当前malloc / calloc申请到的内存空间进行扩容
realloc注意事项:
- realloc后的内存空间仍然是连续的内存空间
- ptr是要调整的内存地址
- size是调整之后的内存大小
- realloc得到的地址,最终不用时,也需要通过free来释放
- 故:void * ptr2 = realloc(ptr,size),此处的ptr和ptr2可能相等,也可能不相等
realloc进行扩容的两种情况:
(从10扩容到20为例:)
情况一:原有空间足够大
情况二:原有空间大小不够
realloc两种情况总结:
- 原有的空间有足够大,够容纳下size个字节的内存,直接在原有内存后继续追加即可,原来的空间数据不发生改变
- 原有的空间不够大,要扩容的话,必须在堆上另外找一个连续的内存空间,可以存放size个字节大小,把原来内存的数据拷贝过去,并且释放旧空间
动态内存管理缺点
动态内存管理太过灵活,故太容易出错
但C语言中没有其他方案,故还是需广泛使用动态内存管理~
后面会发一些常见的错误代码~~
以上是关于如何释放动态链接库中申请的内存的主要内容,如果未能解决你的问题,请参考以下文章