目录
前言
一.进程
1.1 进程的映射
1.2 进程的虚拟空间
二.函数传参
2.1 函数传参
2.2 函数传值
2.2.1 函数传值案例1
2.2.2 函数传值案例2
2.2.3 返回值为常量
2.3 函数传送地址
2.3 字符串使用
前言
详细介绍函数传值和传地址区别:进行数据操作的区别,函数传值为临时拷贝,不会对实参影响。而传地址时,就是传实参,是可以影响到实参的..........
一.进程
1.1 进程的映射
物理空间映射到虚拟空间是计算机内存管理中的一个重要概念,它允许程序使用的地址(虚拟地址)与计算机物理内存的地址(物理地址)之间进行转换。
这种映射机制主要涉及以下几个关键点:
- 虚拟地址与物理地址:用户在编程时使用的地址称为虚拟地址或逻辑地址,而计算机物理内存中的地址称为物理地址。虚拟地址通过内存管理单元(MMU)的转换,映射到物理内存的实际位置。
- 虚拟存储空间与物理存储空间:虚拟地址对应的存储空间称为虚拟存储空间或逻辑地址空间,而物理地址对应的存储空间称为物理存储空间或主存空间。
- 虚拟页与物理页:在虚拟内存系统中,虚拟页是虚拟地址空间的一部分,它们会映射到物理内存中的物理页。这种分页机制使得程序可以拥有比实际物理内存更大的地址空间。
- 固定映射区与动态映射区:在固定映射区中,虚拟内存地址固定映射到物理内存的高端地址上,而在动态映射区和永久映射区中,虚拟地址不是固定的,可以被动态改变以映射到不同的物理地址上。
- 内存映射mmap:内存映射是一种将文件或其他资源映射到进程的虚拟地址空间的技术,这样进程就可以像访问内存一样访问这些资源。这通常用于优化文件I/O操作,因为它减少了数据从磁盘到内存的复制次数。
1.2 进程的虚拟空间
进程的虚拟空间是一个逻辑意义上的内存空间概念,它允许每个进程拥有独立的地址空间,使得程序的运行更加安全和高效。
二.函数传参
2.1 函数传参
函数传参时可以通过传递地址的方式来实现对实际参数的修改。具体来说:
- 指针传递:在函数定义中,参数使用指针类型,调用函数时传递变量的地址。在函数内部,通过解引用操作可以访问和修改该地址指向的数据。这样,当函数返回时,原变量的值可能会被改变。
- 数组传参:在C语言中,当数组作为函数参数时,实际上传递的是数组首元素的地址。这是因为数组在内存中是连续存储的,所以传递首元素地址足以让函数访问整个数组。
- 引用传递:在某些编程语言(如C++)中,可以使用引用传递的方式。引用本质上是一个别名,它允许函数直接操作传递给它的变量。在函数内部对引用参数的任何修改都会反映到原始变量上。
- const修饰符:如果形参是指针并且加了const修饰,则该指针指向的数据是不可修改的。这有助于保护数据不被意外修改。
- 值传递:与地址传递相对的是值传递,值传递会创建一个实参的副本,函数内部的操作不会影响原始变量。但对于数组,由于发生了“降维”,实际上传入的是数组首元素的地址,因此即使是值传递,函数内部对数组元素的修改也是真实存在的。
总之,在实际应用中,选择哪种传递方式取决于具体的编程需求。例如,如果需要在函数内部修改外部变量的值,通常会选择指针传递或引用传递。而如果只是需要读取数据而不做修改,可能会选择值传递。
2.2 函数传值
2.2.1 函数传值案例1
这是最常见的传参方式,当调用函数时,实参的值被复制给形参,形参和实参分别占用不同的存储空间。在函数内部对形参的修改不会影响到外部的实参。这种方式简单且安全,但可能会因为参数复制而降低效率。
#include<stdio.h> #include <stdlib.h> #include <string.h> void GetMemory1(char *p) // p ==== NULL { *p = (char *)malloc(100); // 向系统申请100个字节的堆空间, 让p 指向该区域 } void Test1(void) { char *str = NULL; GetMemory1(str); /// str ==== NULL // 经过GetMemory1 的操作后 str的指向依然没有变换还是指向NULL strcpy(str, "hello world"); // 提示: 拷贝字符串, 把"hello world" 拷贝到str 所指向的内存空间中 // 拷贝函数出现段错误 printf("%s\n" , str); } int main(int argc, char const *argv[]) { Test1(); return 0; }
函数传值进行操作内存时,需要注意,如下:调用开辟内存函数时,没有传入地址,会出现野指针问题。这样传值就是错误的,正确的写法写在后面传地址案例中。
2.2.2 函数传值案例2
这里参考使用static数据段的操作
#include<stdio.h> #include <stdlib.h> #include <string.h> char *GetMemory2(void) { // 数组 p 所存放的位置为 栈空间, 当函数 GetMemory2 退出返回时, 该区域会被系统回收 // 不应该返回该内存中的地址 // 可以使用 static 来修饰该数组, 使其的内存区域改为数据段 static char p[] = "hello world"; return p; } void Test2(void) { char *str = NULL; str = GetMemory2(); printf("TEST‐2:%s\n",str); } int main(int argc, char const *argv[]) { Test2(); return 0; }
这里如果不使用static,程序会报错,因为它是传送值的
2.2.3 返回值为常量
这里的函数调用的返回值为常量,也就是存储在虚拟空间数据段
#include<stdio.h> #include <stdlib.h> #include <string.h> // TEST3 char *GetMemory3(void) { // 直接返回 常量区的内存地址 , 注意该区域只读 return "hello world"; } void Test3(void) { char *str = NULL; str = GetMemory3(); printf("TEST‐3:%s\n",str); } int main(int argc, char const *argv[]) { Test3(); return 0; }
2.3 函数传送地址
在这种传参方式中,实参的地址被传递给形参,形参通常是指针类型。函数内部通过指针来访问和修改实参的值。这种方式允许函数修改外部变量的内容,适用于需要输出结果或者大型数据结构的情况。
#include<stdio.h> #include <stdlib.h> #include <string.h> void GetMemory1(char **p) // p ==== NULL { *p = (char *)malloc(100); // 向系统申请100个字节的堆空间, 让p 指向该区域 } void Test1(void) { char *str = NULL; GetMemory1(&str); /// str ==== NULL strcpy(str, "hello world"); // 提示: 拷贝字符串, 把"hello world" 拷贝到str 所指向的内存空间中 printf("%s\n" , str); } int main(int argc, char const *argv[]) { Test1(); return 0; }
这里的传值为传地址,把char* str 的地址&str 传入GetMemonry1(char **p)作为形参,
解引用后*p 其实就是str 。调用的函数进入栈中进行内存的开辟操作,又因为str==*p,所以
即使栈销毁后str仍然指向malloc开辟的空间。
2.3 字符串使用
调用函数时,strcopy字符串时,存在'\0',
#include<stdio.h> #include <stdlib.h> #include <string.h> // TEST6 void Test6() { char *str=(char *)malloc(100); strcpy(str, "hello"); printf("TES1:%s\n" ,str); // 输出 hello str+=6; // strcpy(str, "world"); printf("TES2:%s\n" ,str); //输出 word str-=6; printf("TES3:%s\n" ,str); //输出 hello *(str+5)=' '; printf("TES4:%s\n" ,str); //输出 hello word } int main(int argc, char const *argv[]) { Test6(); return 0; }
以上是本期补齐内容,若有不懂或者错误,欢迎评论与私信!!!