【动态内存管理助力程序优化与性能飞升】(中)

简介: 【动态内存管理助力程序优化与性能飞升】

【动态内存管理助力程序优化与性能飞升】(上):https://developer.aliyun.com/article/1424811


3. 常见的动态内存错误


3.1 对NULL指针的解引用操作


void test()
{
  int* p = (int*)malloc(INT_MAX / 4);
    //malloc函数开辟失败就会返回NULL
  *p = 20;//如果p的值是NULL,就会有问题
  free(p);
}


3.2 对动态开辟空间的越界访问


void test()
{
  int i = 0;
  int* p = (int*)malloc(10 * sizeof(int));
  if (NULL == p)
  {
    exit(EXIT_FAILURE);
  }
  for (i = 0; i <= 10; i++)
  {
    *(p + i) = i;//当i是10的时候越界访问
  }
  free(p);
}


3.3 对非动态开辟内存使用free释放


void test()
{
  int a = 10;
  int* p = &a;
  free(p);//ok?
}


3.4 使用free释放一块动态开辟内存的一部分


void test()
{
  int* p = (int*)malloc(100);
  p++;
  free(p);//p不再指向动态内存的起始位置
}


3.5 对同一块动态内存多次释放


void test()
{
  int* p = (int*)malloc(100);
  free(p);
  free(p);//重复释放
}


3.6 动态开辟内存忘记释放(内存泄漏)


void test()
{
  int* p = (int*)malloc(100);
  if (NULL != p)
  {
    *p = 20;
  }
}
int main()
{
  test();
  while (1);
}


忘记释放不再使用的动态开辟的空间会造成内存泄漏。


切记:动态开辟的空间一定要释放,并且正确释放 。


4. 几个经典的笔试题


demo1:


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void GetMemory(char* p)
{
  p = (char*)malloc(100);
}
void Test(void)
{
  char* str = NULL;
  GetMemory(str);
  strcpy(str, "hello world");
  printf(str);
}
int main()
{
  Test();
  return 0;
}


问题:对NULL解引用以及没有释放malloc申请的空间



解释:

GetMemory 函数: 这个函数接受一个字符指针(char*)作为参数,并尝试使用 malloc 来分配 100 字节的内存。然而,需要理解的是,在C语言中,函数参数是通过值传递的,这意味着 GetMemory 函数内部的 p 是从 Test 函数传递过来的指针的一个拷贝。对于拷贝的指针所做的更改不会影响 Test 函数中的原始指针。


Test 函数: 在 Test 函数中,声明并初始化了一个 char* 变量 str,并将其设置为 NULL。然后,调用 GetMemory 函数,并将 str 作为参数传递进去。由于参数是通过值传递的,GetMemory 函数只会修改它自己的指针拷贝,并不会改变 Test 函数中的原始 str 指针。


内存分配问题: 在 GetMemory 函数内部,内存被分配给局部指针 p,这是从 Test 函数的 str 指针拷贝过来的。这意味着在 Test 函数中,原始的 str 指针仍然是 NULL,并没有被赋予新分配的内存地址。


缓冲区溢出: 在调用 GetMemory 函数后,有一个 strcpy 函数调用,试图将字符串 "hello world" 复制到 str 指针中。然而,由于 str 仍然是 NULL(没有指向已分配的内存),这将导致未定义行为,并可能导致段错误或其他错误,因为访问了无效的内存。


修改:


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void GetMemory(char** p)
{
    *p = (char*)malloc(100); // 分配 100 字节内存,并将地址存储在原始指针中
}
void Test(void)
{
    char* str = NULL;
    GetMemory(&str); // 传递指向指针的指针(双重指针),以修改原始指针
    strcpy(str, "hello world");
    printf("%s", str);
    free(str); // 使用完内存后别忘了释放它
    str == NULL;
}
int main()
{
    Test();
    return 0;
}


demo2:


#include<stdio.h>
char* GetMemory(void)
{
  char p[] = "hello world";
  return p;//返回局部变量的地址
}
void Test(void)
{
  char* str = NULL;
  str = GetMemory();
  printf(str);
}
int main()
{
  Test();
  return 0;
}


问题:返回局部变量的地址


解释:

  1. GetMemory 函数: 这个函数声明了一个字符数组 p 并初始化为 "hello world"。然后它试图返回 p 的地址。但是,需要注意的是,p 是一个局部变量,它在函数结束时会被销毁。因此,将局部变量的地址返回给调用者是不安全的,因为在调用者函数中访问返回的地址将指向无效的内存区域。


  1. Test 函数: 在 Test 函数中,声明了一个字符指针 str 并将其初始化为 NULL。然后,调用 GetMemory 函数,将返回的地址赋值给 str。


  1. 错误的返回局部变量地址: 在 GetMemory 函数中,由于返回局部变量 p 的地址,str 指针现在指向了一个不再有效的内存地址,因为 p 在 GetMemory 函数返回后已经被销毁。


  1. printf 函数: 在 printf 中尝试打印 str 指向的字符串时,由于 str 指向无效内存地址,代码的行为将是未定义的。这可能导致程序崩溃、输出奇怪的字符或其他不确定的结果。


修改:


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* GetMemory(void)
{
    char* p = (char*)malloc(12); // 在堆上分配内存以容纳 "hello world" 和空结束符
    strcpy(p, "hello world"); // 将 "hello world" 复制到新分配的内存块中
    return p; // 返回指向分配内存的指针
}
void Test(void)
{
    char* str = NULL;
    str = GetMemory();
    printf("%s", str);
    free(str); // 使用完内存后别忘了释放它
}
int main()
{
    Test();
    return 0;
}


demo3:


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void GetMemory(char** p, int num)
{
  *p = (char*)malloc(num);
}
void Test(void)
{
  char* str = NULL;
  GetMemory(&str, 100);
  strcpy(str, "hello");
  printf(str);
}
int main()
{
  Test();
  return 0;
}


问题:malloc申请的空间没有释放


修改:


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
void GetMemory(char** p, int num)
{
  *p = (char*)malloc(num);
}
void Test(void)
{
  char* str = NULL;
  GetMemory(&str, 100);
  strcpy(str, "hello");
  printf(str);
    free(str);
    str = NULL;
}
int main()
{
  Test();
  return 0;
}


【动态内存管理助力程序优化与性能飞升】(下):https://developer.aliyun.com/article/1424821

相关文章
|
1月前
|
存储 算法 Java
Java内存管理深度剖析与优化策略####
本文深入探讨了Java虚拟机(JVM)的内存管理机制,重点分析了堆内存的分配策略、垃圾回收算法以及如何通过调优提升应用性能。通过案例驱动的方式,揭示了常见内存泄漏的根源与解决策略,旨在为开发者提供实用的内存管理技巧,确保应用程序既高效又稳定地运行。 ####
|
1月前
|
存储 缓存 JavaScript
如何优化Node.js应用的内存使用以提高性能?
通过以上多种方法的综合运用,可以有效地优化 Node.js 应用的内存使用,提高性能,提升用户体验。同时,不断关注内存管理的最新技术和最佳实践,持续改进应用的性能表现。
122 62
|
28天前
|
存储 缓存 监控
如何使用内存监控工具来优化 Node.js 应用的性能
需要注意的是,不同的内存监控工具可能具有不同的功能和特点,在使用时需要根据具体工具的要求和操作指南进行正确使用和分析。
70 31
|
25天前
|
存储 缓存 监控
Docker容器性能调优的关键技巧,涵盖CPU、内存、网络及磁盘I/O的优化策略,结合实战案例,旨在帮助读者有效提升Docker容器的性能与稳定性。
本文介绍了Docker容器性能调优的关键技巧,涵盖CPU、内存、网络及磁盘I/O的优化策略,结合实战案例,旨在帮助读者有效提升Docker容器的性能与稳定性。
66 7
|
25天前
|
存储 算法 Java
Java 内存管理与优化:掌控堆与栈,雕琢高效代码
Java内存管理与优化是提升程序性能的关键。掌握堆与栈的运作机制,学习如何有效管理内存资源,雕琢出更加高效的代码,是每个Java开发者必备的技能。
49 5
|
26天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
59 1
|
1月前
|
缓存 算法 Java
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
52 6
|
1月前
|
监控 安全 程序员
如何使用内存池池来优化应用程序性能
如何使用内存池池来优化应用程序性能
|
1月前
|
存储 监控 Java
深入理解计算机内存管理:优化策略与实践
深入理解计算机内存管理:优化策略与实践
|
1月前
|
缓存 Prometheus 监控
Elasticsearch集群JVM调优设置合适的堆内存大小
Elasticsearch集群JVM调优设置合适的堆内存大小
284 1

热门文章

最新文章