「探索C语言内存:动态内存管理解析」

🌠先赞后看,不足指正!🌠

🎈这将对我有很大的帮助!🎈

📝所属专栏:C语言知识

📝阿哇旭的主页:Awas-Home page

目录

 

引言

1. 静态内存

2. 动态内存

2.1 动态内存开辟函数

2.1.1 malloc函数

2.1.2 calloc函数

2.1.3 realloc函数

2.2 动态内存释放函数

2.2.1 free函数

3. 动态内存的常见错误

3.1 对NULL指针的解引用

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

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

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

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

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

4. 动态内存管理经典题目分析

4.1 出现悬空指针和访问无效内存

4.2 局部变量的生命周期

4.3 内存开辟后未释放

4.4 内存释放后再次使用

5. 柔性数组

5.1 柔性数组是什么

5.2 柔性数组的特点

5.3 柔性数组的使用

5.4 柔性数组的优势

6. 在C/C++中程序内存区域划分

7. 总结


引言

        什么是内存?内存,也称为主存或随机存储器(RAM),是计算机中用于存储数据和程序的临时存储设备。在下文中,我将讲解到内存的开辟方式,分为静态内存开辟和动态内存开辟两部分。

        那么,话不多说,我们一起来看看吧!


1. 静态内存

        在前面的学习中,我们掌握的内存开辟方式有两种:

#include<stdio.h>
int main()
{int val = 20;      //1. 在栈空间上开辟 4 个字节char a[10] = { 0 };//2. 在栈空间开辟 10 个字节的连续空间return 0;
}

        但静态内存开辟的内存空间会存在一定的缺陷:

  • 空间开辟大小是固定的。
  • 数组在声明的时候,必须指定数组的长度,数组空间一旦确定了大小是不能调整的。 

2. 动态内存

        为了解决静态内存开辟的内存空间所存在的问题。为此,C语言中引入了动态内存开辟,让程序员可以手动管理内存,包括分配和释放内存空间,这样就比较灵活了。

2.1 动态内存开辟函数

2.1.1 malloc函数

(1)头文件:在标头<stdlib.h>中定义

(2)函数原型:void* malloc (size_t size);

  • size_t 是一个无符号整数类型,size 表示需要分配的字节数。
  • 如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。

(3)作用:向内存申请一块连续可用的空间,并返回指向这块空间的指针。

  • 开辟成功时,返回指向新分配内存的指针。
  • 开辟失败时,返回空指针(NULL),因此 malloc 的返回值一定要做检查。

(4)返回值:该函数返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候由使用者来确定。

补充:perror函数

(1)头文件:在标头<stdio.h>中定义

(2)函数原型:void perror(const char *s);

  • 如果s为NULL,则只输出错误信息而不输出自定义信息。

(3)作用:用于将错误码转换为对应的错误信息并输出到标准错误流(stderr)中。

(4)返回值:无返回值。

#include <stdio.h>
#include <errno.h>int main() {FILE* fp = fopen("file.txt", "r");if (fp == NULL) {perror(fp);}return 0;
}

运行结果:

malloc 函数的具体使用举例:

int main()
{//开辟一个大小为10个整形的空间//返回值类型强制转换为 (int*)int* arr = (int*)malloc(10 * sizeof(int));if (arr == NULL)//开辟空间失败{perror("malloc failed");//打印错误信息return 1;//返回}int i = 0;//读取存入数据for (i = 0; i < 10; i++){arr[i] = i + 1;}//打印输出数据for (i = 0; i < 10; i++){printf("%d ", arr[i]);}return 0;
}

输出结果:

监视窗口:读取存入数据

  • 动态存储的数据存放在内存的堆区。 

2.1.2 calloc函数

(1)头文件:在标头<stdlib.h>中定义

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

  • nitems表示要分配的元素个数。
  • size 表示元素的大小。

(3)作用:分配所需的空间,并返回指向这块空间的指针。

(4)返回值:函数返回一个指向新分配的内存空间的指针,如果分配失败则返回NULL。

        calloc函数与malloc函数类似,不同之处在于calloc函数会在分配内存空间后将其初始化为0,而malloc函数不会做这个操作。因此,如果需要分配一段内存空间并将其初始化为0,可以使用calloc函数。

calloc 函数的具体使用举例:

int main()
{//开辟一个大小为十个整形的空间//返回值类型强制转换为 int*int* arr = (int*)calloc(10, sizeof(int));if (arr == NULL)//开辟空间失败{perror("calloc failed");//打印错误信息return 1;//返回}return 0;
}

监视窗口:项全初始化为 0

2.1.3 realloc函数

(1)头文件:在标头<stdlib.h>中定义

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

  • ptr 表示要重新分配内存空间的指针。
  • size 表示新分配的内存空间大小。

(3)作用:用于调整 malloccalloc 所分配的 ptr 指向已经分配的内存空间大小。

(4)返回值:函数返回一个指向新分配的内存空间的指针,如果分配失败则返回NULL。

  1.  realloc函数的出现让动态内存管理更加灵活。
  2. 有时会我们发现过去申请的空间太小或太大了,那么为了合理利用内存,我们会对内存的大小做一些灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小的调整。
  3. realloc 函数的扩容机制:
  • 原有空间扩容:原有空间之后有足够大的空间,直接在原有内存之后直接追加空间,原来空间的数据不发生变化。
  • 新开空间扩容:原有空间之后没有足够大的空间,在堆空间上另找⼀个合适大小的连续空间来使用。同时将新增数据和原有数据拷贝到新的空间,并释放原有空间

realloc 函数的具体使用举例: 

int main()
{//开辟一个大小为十个整形的空间//返回值类型强制转换为 int*int* arr = (int*)calloc(10, sizeof(int));if (arr == NULL)//开辟空间失败{perror("calloc failed");//打印错误信息return 1;//返回}//扩大空间//不使用int* arr,是为了防止内存开辟失败,被置为NULLint* tmp = (int*)realloc(arr, 80 * sizeof(int));if (tmp == NULL)//开辟空间失败{perror("realloc failed");//打印错误信息return 1;//返回}arr = tmp;return 0;
}

  • 当新增内存比较小时,一般是在原有空间基础上开辟新的空间。二者的地址相同。 
int* tmp = (int*)realloc(arr, 80 * sizeof(int));

  •  当新增内存比较大时,则会开辟一个新的空间,并将原有空间释放。二者的地址不同。

2.2 动态内存释放函数

2.2.1 free函数

        我们要知道,动态开辟的空间不会像静态开辟的空间那样随程序的结束就自动回收。这就需要我们去手动回收,避免造成内存泄漏

  • 内存泄漏是指程序在运行过程中,动态分配的内存空间没有被释放,导致该内存空间一直被占用,无法被其他程序使用,从而造成内存资源的浪费。如果内存泄漏严重,会导致程序崩溃或者操作系统崩溃。

(1)头文件:在标头<stdlib.h>中定义

(2)函数原型:void free(void *ptr);

  • ptr 是指向之前分配的内存空间的指针。调用 free 函数会释放该内存空间,使其可以被重新使用。

(3)作用:用于释放之前通过 malloc、calloc、realloc 等函数动态分配的内存空间。

(4)返回值:无返回值。

free 函数的具体使用举例:

int main()
{//开辟一个大小为十个整形的空间//返回值类型强制转换为 int*int* arr = (int*)calloc(10, sizeof(int));if (arr == NULL)//开辟空间失败{perror("calloc fail");//打印错误信息return 1;//返回}//扩大空间int* tmp = (int*)realloc(arr, 20 * sizeof(int));if (tmp == NULL)//开辟空间失败{perror("realloc fail");//打印错误信息return 1;//返回}arr = tmp;free(arr);//释放arr所指向的动态内存arr = NULL;return 0;
}
  •  在使用free函数释放动态分配的内存空间之后,将指针置为NULL是一种良好的编程习惯,可以避免悬空指针的问题。
  • 悬空指针是指已经被释放的内存空间的指针,如果在指针被释放后继续使用该指针,就会导致未定义的行为。将指针置为NULL可以避免这种情况的发生,因为在使用空指针时,程序会抛出异常或崩溃,从而提醒程序员出现了问题。

3. 动态内存的常见错误

        动态内存的管理和指针类似,利用不当会出现错误,以下是一些常见错误的例子:

3.1 对NULL指针的解引用

void test()
{int* pf = (int*)malloc(INT_MAX / 4);*pf = 20; //如果p的值是NULL,就会有问题 free(pf);
}
  • INT_MAX 是一个宏定义(常量),表示的是整型数据类型的最大值,一般情况下是 2147483647
  • 当 malloc 函数申请的空间过大时,则会出现空间开辟失败的情况,此时返回空指针(NULL)
  • 编译器无法访问空指针(NULL),此时编译器会报错。

正确方法:

void test()
{int* pf = (int*)malloc(INT_MAX / 4);if (pf == NULL){perror("malloc failed!");return 1;}*pf = 20; //如果p的值是NULL,就会有问题 free(pf);pf = NULL;
}
  • 综上,检查开辟的空间是否为空指针是十分重要的。 

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

void test()
{int i = 0;int* pf = (int*)malloc(10 * sizeof(int));if (pf == NULL){perror("malloc failed!");return 1;}for (i = 0; i <= 10; i++){*(pf + i) = i;//当i是10的时候越界访问 }free(pf);pf = NULL;
}
  • malloc函数只申请了10个整型大小空间的内存。
  • for循环循环访问来十一次空间,造成越界访问,错误信息如下:

正确方法:

void test()
{int i = 0;int* pf = (int*)malloc(10 * sizeof(int));if (pf == NULL){perror("malloc failed!");return 1;}for (i = 0; i <= 10; i++){*(pf + i) = i;//当i是10的时候越界访问 }for (i = 0; i < 10; i++){printf("%d ", *(pf + i));}free(pf);pf = NULL;
}

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

void test()
{int a = 10;int* pf = &a;free(pf);//是否可行?
}
  • free函数,是专门用来做动态内存的释放和回收的。
  • 指针 pf 所指向的空间是静态内存开辟的,若强行释放,编译器就会报错:

正确方法:

void test()
{int a = 10;int* pf = &a;
}
  • 静态内存开辟的空间在程序结束时会自动释放回收。 

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

void test()
{int* pf = (int*)malloc(100);pf++;free(pf);//p不再指向动态内存的起始位置 pf = NULL;
}
  • pf++ 后,pf 指向的地址增加了一个 int 类型的大小,这样 pf 不再指向动态内存的起始位置,而是指向了动态分配的内存块的下一个 int 类型的位置。
  • free() 函数只能释放动态分配的内存空间的起始位置,而不能释放中间或结尾位置的内存。

正确方法:

void test()
{int* pf = (int*)malloc(100);free(pf);//p不再指向动态内存的起始位置 pf = NULL;
}
  • 注意:不要随意改变 pf 所指向的位置,开辟多少内存就释放多少内存。

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

void test()
{int* pf = (int*)malloc(100);free(pf);free(pf);//重复释放 
}
  • 重复使用 free() 函数尝试释放 pf 所指向的内存空间。这是一个错误的操作,因为在第一次调用 free() 函数后,内存空间已经被释放,pf 指向的内存不再有效。
  • 重复释放已经释放的内存空间会导致程序运行时出现未定义行为,可能会导致内存错误或程序崩溃。

正确方法:

void test()
{int* pf = (int*)malloc(100);free(pf);pf = NULL;
}
  •  在每次释放完内存后,将 pf 设置为 NULL,可以避免出现悬空指针(dangling pointer)的问题。

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

void test()
{int* pf = (int*)malloc(100);if (pf != NULL){*pf = 20;}
}
  •  在申请的空间使用完毕后,应该使用 free() 函数释放动态分配的内存空间。避免内存泄漏。

正确方法:

void test()
{int* pf = (int*)malloc(100);if (pf != NULL){*pf = 20;}free(pf);pf = NULL;
}

4. 动态内存管理经典题目分析

4.1 出现悬空指针和访问无效内存

问题一:

void GetMemory(char* p)
{p = (char*)malloc(100);
}
void Test(void)
{char* str = NULL;GetMemory(str);strcpy(str, "hello world");printf(str);
}

错误:

  • 在 GetMemory 函数中,p 是一个指针的拷贝,当在函数内部对 p 进行赋值(分配内存)时,只是修改了拷贝的值,并不会影响原始指针 str。
  • 在 Test 函数中,尝试使用未分配有效内存空间的 str 指针来存储字符串 "hello world",这将导致未定义行为,可能会导致程序崩溃。

        正确方法:

  1. 我们要想改变 str 就需要传址调用,而 str 本身就是个指针变量,传指针变量的地址需要二级指针来接收
  2. 使用完之后必须释放内存。
void GetMemory(char** p)
{*p = (char*)malloc(100);
}void Test(void)
{char* str = NULL;GetMemory(&str);strcpy(str, "hello world");printf(str);free(str);//释放str = NULL;
}

4.2 局部变量的生命周期

问题二:

char* GetMemory(void)
{char p[] = "hello world";return p;
}void Test(void)
{char* str = NULL;str = GetMemory();printf(str);
}

错误: 

  1. 在函数 GetMemory 中,数组 p 是一个局部数组,它的生命周期仅限于函数 GetMemory 的执行期间。当函数返回后,p的内存空间将被释放,因此返回的指针将指向无效的内存区域,这是一种未定义行为。
  2. 为了避免这些问题,应该避免返回局部变量的地址。如果需要在函数中动态分配内存并返回指针,可以使用 malloc 分配内存,然后在适当的时候释放这块内存。

        正确方法:

char* GetMemory(void)
{char* p = (char*)malloc(12);//分配足够的内存空间,包括字符串结束符if (p == NULL){perror("malloc fail!");return 1;}strcpy(p, "hello world");return p;
}void test(void)
{char* str = NULL;str = GetMemory();if (str != NULL){printf("%s", str);free(str); // 释放内存}
}

4.3 内存开辟后未释放

问题三:

void GetMemory(char** p, int num)
{*p = (char*)malloc(num);
}void test(void)
{char* str = NULL;GetMemory(&str, 100);strcpy(str, "hello");printf(str);
}

        错误:上面的代码,可以正常的运行。但要注意的是,程序动态开辟内存后未释放回收,造成内存泄漏。

正确方法:

void test(void)
{char* str = NULL;GetMemory(&str, 100);strcpy(str, "hello");printf(str);free(str);str = NULL;
}

4.4 内存释放后再次使用

问题四:

void Test(void)
{char* str = (char*)malloc(100);strcpy(str, "hello");free(str);//?if (str != NULL){strcpy(str, "world");printf(str);}
}

        错误:运行上述代码,我们会发现,这是一个经典的野指针问题,由于 str 指向的内存空间已经被释放,再次调用 strcpy 函数向其写入字符串 "world" 会导致问题。

正确方法:

        内存释放后,将其置为 NULL (空指针),使后续语句不再执行。

void test(void)
{char* str = (char*)malloc(100);strcpy(str, "hello");free(str);str = NULL;//将指针置为 NULLif (str != NULL){strcpy(str, "world");printf(str);}
}

5. 柔性数组

5.1 柔性数组是什么

        在C99中,结构中的最后一个元素允许是未知大小的数组,这就叫做柔性数组。比如:

typedef struct st_type
{int i;int a[0];//柔性数组成员 
}type_a;

        有些编译器会报错无法编译可以换一种写法:

typedef struct st_type
{int i;int a[];//柔性数组成员 
}type_a;

5.2 柔性数组的特点

  • 结构中的柔性数组成员前面必须至少⼀个其他成员。
  • sizeof 返回的这种结构大小不包括柔性数组的内存。
typedef struct st_type
{int i;int a[0];//柔性数组成员 
}type_a;
int main()
{printf("%zd\n", sizeof(type_a));return 0;
}

  • 包含柔性数组成员的结构用malloc()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。

5.3 柔性数组的使用

         柔性数组的使用与结构体十分相似,具体例子如下:

// 定义柔性数组的结构体
typedef struct FlexArray 
{int len; // 数组的长度int data[]; // 可变长度的整型数组
}type_a;
int main() 
{int len = 100; // 柔性数组的长度// 动态分配内存空间type_a* flexArray = malloc(sizeof(type_a) + len * sizeof(int));if (flexArray == NULL){perror("malloc fail!");return 1;}flexArray->len = len;// 输出柔性数组的长度printf("柔性数组的长度: %d\n", flexArray->len);// 释放内存free(flexArray);flexArray = NULL;return 0;
}

  •  这样柔性数组成员 data,相当于获得了100个整型元素的连续空间。

具体使用如下:

#include <stdio.h>
#include <stdlib.h>// 定义柔性数组的结构体
typedef struct FlexArray 
{int len; // 数组的长度int data[]; // 可变长度的整型数组
}type_a;
int main() 
{int len = 100;// 动态分配内存空间type_a* pf = malloc(sizeof(type_a) + len * sizeof(int));if (pf == NULL){perror("malloc fail!");return 1;}pf->len = 100;//为柔性数组赋值for (int i = 0; i < 100; i++){pf->data[i] = i;}//输出柔性数组的值printf("柔性数组的值: ");for (int i = 0; i < pf->len; i++){printf("%d ", pf->data[i]);}printf("\n");//释放内存free(pf);pf = NULL;return 0;
}

5.4 柔性数组的优势

  • 访问元素方便:由于柔性数组是结构体的一部分,因此可以通过结构体指针直接访问柔性数组的元素,而不需要额外的指针操作。
  • 提高访问速度:由于柔性数组在内存中是连续存储的,可以提高数据的访问速度,从而提高程序的性能。
  • 内存管理效率:柔性数组允许将结构体和数组的内存分配在一次连续的操作中完成,这有助于减少内存碎片化,提高内存管理的效率。

6. 在C/C++中程序内存区域划分

        在 C/C++ 程序中,内存区域通常被划分为以下几个部分:

栈区(Stack)

  • 栈区用于存储函数的局部变量函数参数、函数调用的返回地址等。
  • 栈区是一种后进先出的数据结构,每次函数调用时会在栈上分配一块内存空间,函数返回时会释放该空间。
  • 栈区的大小是有限的,通常在编译时确定,如果栈区溢出会导致程序崩溃。

堆区(Heap)

  • 堆区用于动态分配内存,程序员可以通过 malloccalloc 等函数在堆上分配内存。
  • 堆区的大小通常比栈区大得多,可以根据需要动态地分配和释放内存。
  • 堆区的内存由程序员手动管理,需要注意避免内存泄漏和内存访问越界等问题。

全局区/静态区(Global/Static)

  • 全局区用于存储全局变量静态变量、常量等。
  • 全局区在程序启动时分配,在程序结束时释放。
  • 全局变量和静态变量的生命周期与程序的运行周期相同,常量存储在只读内存区域。


7. 总结

        希望这篇文章对大家有所帮助,如果你有任何问题和建议,欢迎在评论区留言,这将对我有很大的帮助。

        完结!咻~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/549.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

超越现实的展览体验,VR全景展厅重新定义艺术与产品展示

随着数字化时代的到来&#xff0c;VR全景展厅成为了企业和创作者展示作品与产品的新兴选择。通过结合先进的虚拟现实技术&#xff0c;VR全景展厅不仅能够提供身临其境的观展体验&#xff0c;而且还拓展了传统展示方式的界限。 一、虚拟现实技术的融合之美 1、高度沉浸的观展体验…

本地项目如何设置https——2024-04-19

问题&#xff1a;由于项目引用了html5-qrcode插件&#xff0c;但是该插件在本地移动端调试时只能使用https访问&#xff0c;所有原本的本地地址是http&#xff0c;就需要改成https以方便调试。 解决方法&#xff1a;使用本地https证书 1&#xff09;从项目文件下打开cmd逐步输…

vulfocus靶场tomcat-cve_2017_12615 文件上传

7.0.0-7.0.81 影响版本 Windows上的Apache Tomcat如果开启PUT方法(默认关闭)&#xff0c;则存在此漏洞&#xff0c;攻击者可以利用该漏洞上传JSP文件&#xff0c;从而导致远程代码执行。 Tomcat 是一个小型的轻量级应用服务器&#xff0c;在中小型系统和并发访问用户不是很多…

基于达梦数据库开发-C#篇

文章目录 前言一、相关准备二、主要代码1.引入达梦类库2.连接达梦数据库3.DmCommand方式获取达梦数据库信息4.DmDataAdapter方式获取达梦数据库信息 总结 前言 达梦数据库是国产的新一代大型通用关系型数据库&#xff0c;全面支持 SQL 标准和主流编程语言接口/开发框架。其中.…

OpenHarmony实战开发-如何利用panel实现底部面板内嵌套列表。

介绍 本示例主要介绍了利用panel实现底部面板内嵌套列表&#xff0c;分阶段滑动效果场景。 效果图预览 使用说明 点击底部“展开”&#xff0c;弹出panel面板。在panel半展开时&#xff0c;手指向上滑动panel高度充满页面&#xff0c;手指向下滑动panel隐藏。在panel完全展开…

浏览器工作原理与实践--浏览上下文组:如何计算Chrome中渲染进程的个数

经常有朋友问到如何计算Chrome中渲染进程个数的问题&#xff0c;那么今天就来完整地解答这个问题。 在前面“04 | 导航流程”这一讲中我们介绍过了&#xff0c;在默认情况下&#xff0c;如果打开一个标签页&#xff0c;那么浏览器会默认为其创建一个渲染进程。不过我们在“04 |…

Echarts-知识图谱

Echarts-知识图谱 demo地址 打开CodePen 效果 思路 1. 生成根节点 2. 根据子节点距离与根节点的角度关系&#xff0c;生成子节点坐标&#xff0c;进而生成子节点 3. 从子节点上按角度生成对应的子节点 4. 递归将根节点与每一层级子节点连线核心代码 定义节点配置 functio…

基于springboot实现车辆管理系统设计项目【项目源码+论文说明】计算机毕业设计

基于springboot实现车辆管理系统演示 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了车辆管理系统的开发全过程。通过分析车辆管理系统管理的不足&#xff0c;创建了一个计算机管理车辆管理系统的方案。文章介…

黑洞路由、 DDoS 攻击 、 环路

黑洞路由 DDoS 攻击 DDoS 攻击是一种针对服务器、服务或网络的恶意行为。DDoS 攻击通过向目标发送大量流量&#xff0c;使其不堪重负&#xff0c;导致资源和带宽被耗尽。因此&#xff0c;目标可能会变慢或崩溃&#xff0c;无法正常处理合法的流量。DDoS 攻击通常是由僵尸网络…

在龙梦迷你电脑福珑2.0上使用Fedora 28 龙梦版

在龙梦迷你电脑福珑2.0上使用Fedora 28 龙梦版。这个版本的操作系统ISO文件是&#xff1a;Fedora28_for_loongson_MATE_Live_7.2.iso 。它在功能方面不错。能放音乐&#xff0c;能看cctv直播&#xff0c;有声音&#xff0c;能录屏&#xff0c;能在局域网里用PuTTY的ssh方式连接…

《苏东坡 传》一蓑烟雨任平生

《苏东坡 传》一蓑烟雨任平生 林语堂&#xff0c;中国现代著名作家、学者、翻译家、语言学家。 张振玉 译 文章目录 《苏东坡 传》一蓑烟雨任平生[toc]摘录小结感悟 摘录 苏东坡是个秉性难改的乐天派&#xff0c;是悲天悯人的道德家&#xff0c;是黎民百姓的好朋友&#xff0c;…

C语言C/S架构PACS影像归档和通信系统源码 医院PACS系统源码

C语言C/&#xff33;架构PACS影像归档和通信系统源码 医院PACS系统源码 医院影像科PACS系统&#xff0c;意为影像归档和通信系统。它是应用在医院影像科室的系统&#xff0c;主要的任务是把日常产生的各种医学影像&#xff08;包括核磁、CT、超声、各种X光机、各种红外仪、显微…

JAVA ASM总结篇-03

MethodVisitor ClassVisitor的visitMethod能够访问到类中某个方法的一些入口信息&#xff0c;那么针对具体方法中字节码的访问是由MethodVisitor来进行的 访问顺序如下&#xff0c;其中visitCode和visitMaxs仅调用一次&#xff0c;标志方法字节码访问的开始和结束 MethodVisi…

不羁联盟怎么参与测试 不羁联盟测试时间+参与测试方法分享

不羁联盟怎么参与测试 不羁联盟测试时间参与测试方法分享 《不羁联盟》是由育碧&#xff08;Ubisoft&#xff09;开发的一款6v6团队合作射击游戏。游戏的背景设定在一个后启示录时代的废土世界中&#xff0c;玩家能够身临其境地感受到废土世界的荒凉和残酷。游戏在内测时候就受…

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台级联时,下级平台未发流是什么原因?

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…

AUTOCAD输出或打印PDF文件时,如何将图形居中且布满图纸?

AUTOCAD输出或打印PDF文件时,如何将图形居中且布满图纸? 如下图所示,我们打开一份DWG格式的图纸文件,然后点击上方的“打印“图标, 如下图所示, 打印机/绘图仪这里选择“DWG To PDF“; 图纸尺寸:这里以普通的A4纸为例进行说明; 打印比例选择“布满图纸“; 打印偏移…

优维应用级数字化架构管理:让企业运维天堑变通途

在优维科技的产品视角中&#xff0c;数字化架构管理就像是一门精妙的艺术&#xff0c;它将上层应用模型的业务概念以可视化的方式呈现出来&#xff0c;使得业务逻辑和流程变得更加直观、清晰。我们将这样的管理方式理解为“给企业搭起一座桥梁”——在这座桥梁的搭建过程中&…

express服务器 authorization 前端获取不到的问题

服务器生成token 设置在响应头&#xff0c;但是前端获取不到 const token JWT.generate({ id: new Date().getTime(), userName }, 10s) res.header(Authorization, token) axios.interceptors.response.use((response) > {console.log(response);if (response.data?.co…

破解费用管理迷局,企业费用管理从不止于报销

数字化变革浪潮下&#xff0c;各种企业费用报销软件如雨后春笋般不断涌现&#xff0c;企业报销效率大幅提升&#xff0c;部分财务处理流程得到固化和优化&#xff0c;报销早已不再是企业费时费力的财务难题。那么&#xff0c;企业费用管里如何实现呢&#xff1f; 企业费用贯穿于…

ubuntu上安装调试SVN服务

刚成立团队需要临时搭建一台SVN服务器&#xff0c;所以对照网上的一些提示做了下&#xff0c;操作起来不复杂&#xff0c;还是踩了不少坑&#xff0c;顺便原理性了解了下。 主要操作步骤如下&#xff1a; 1&#xff1a;安装svn sudo apt-get install subversion 2: 创建svn版…