命令行参数、环境变量与虚拟地址空间:从 main 的形参说起

必背八股文-C/C++(4) 面试常见C/C++问题 阅读详情

从开始学习C语言以来我们已经写惯了 int main() ,但是别忘了,main函数也是可以有参数的。那么main函数的参数是谁传进来的?程序还没开始跑,谁在调用它?

答案是,可执行文件被 exec 加载时,内核除了把代码和数据搬进新的地址空间,还要把命令行参数和环境变量一起放到栈顶,然后才把控制权交给程序的入口点。main 只是从那里把这两样东西接过来。

这篇文章会具体介绍命令行参数、环境变量和地址空间。

一、main 的两个形参

1. 一次运行

写一个最小的程序,把参数逐个打印出来:

#include <stdio.h>

int main(int argc, char *argv[])
{
    for (int i = 0; i < argc; i++) {
        printf("argv[%d]: %s\n", i, argv[i]);
    }
    return 0;
}

编译后带上几个词运行:

$ ./code a b c d
argv[0]: ./code
argv[1]: a
argv[2]: b
argv[3]: c
argv[4]: d

mian函数的参数中,argc是参数的个数(argument count),argv是参数向量(argument vector),也就是一个存放参数的指针数组。注意第一个参数不是 a,而是程序名 ./code。

2. 数组以 NULL 结尾

C 标准保证 argv[argc] 是一个空指针。所以下面两种写法等价:

for (int i = 0; i < argc; i++) { /* ... */ }
for (char **p = argv; *p != NULL; p++) { /* ... */ }

3. 命令行中的词是谁切开的?

./code a b c d 里那些空格不是你敲给 main 看的,是 bash 切开的。

bash 读入一整行,按空白字符拆成若干个词,第一个词当作命令名,剩下的整体交给这个程序。拆的时候会照顾引号,"./code" "a b" 里的空格不会被切开。你在 shell 里输入 ls -a -b -c,bash 切出四个词,ls 自己再去解析 -a、-b、-c。

这就是命令行选项的实现原理:程序拿到的是一串已经切好的字符串,怎么解释、哪些算选项、哪些算文件名,全由程序自己决定。ls 把以 - 开头的当作选项,grep 也有自己的一套,没有统一的内核机制在管这件事。

二、环境变量

1. 为什么需要它

编译一个链接了动态库的程序时,你并没有告诉链接器 libc 在哪个目录,链接照样成功。运行时也没有告诉加载器去哪找 .so,程序照样跑起来。这些“不用你说,系统自己知道”的信息,很多来自环境变量。

环境变量是一组 名称=内容 的字符串,由父进程传给子进程,用来描述运行环境。它和命令行参数的区别很直接:命令行参数是你这一次运行临时敲的,环境变量是进程一开始就继承下来的,通常在整个会话里保持不变。

2. PATH:为什么 ./hello 要带路径

先看一个现象:

$ ./hello
hello world
$ hello
bash: hello: command not found

同一个文件,带 ./ 能跑,不带就找不到。原因在于 bash 执行命令时,如果命令名里不含 /,它不会去当前目录找,而是到 PATH 列出的目录里逐个搜索。

$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/home/jys/bin

冒号分隔的一串目录。ls、grep 这些命令之所以能直接敲,是因为它们躺在 /usr/bin 这类目录里,而那个目录在 PATH 中。

把当前目录加进去就能直接运行:

$ export PATH=$PATH:.
$ hello

$PATH 会先被展开成原来的值,再拼上 :.,所以是追加而不是覆盖。不过把 . 加进 PATH 是有风险的:如果当前目录里有个叫 ls 的恶意文件,你敲 ls 就会执行它。这也是发行版默认不把当前目录放进 PATH 的原因。

另一个办法是把程序放进一个已经在 PATH 里的目录,比如 /usr/local/bin。

3. 几个常用变量

变量含义
PATH命令的搜索路径
HOME用户主目录,~ 展开成它
SHELL当前 shell 的路径,通常是 /bin/bash
USER当前用户名
PWD当前工作目录
LANG区域与字符编码设置

HOME 可以直接验证:

$ echo $HOME
/home/jys
$ cd ~
$ pwd
/home/jys

~ 是 shell 展开成 $HOME 的结果,不是文件系统里的特殊名字。

4. 操作环境变量的命令

$ env                 # 列出所有环境变量
$ echo $PATH          # 查看某一个
$ export MYENV=hello  # 定义并导出
$ unset MYENV         # 删除
$ set                 # 列出本地变量和环境变量

set 和 env 的差别值得留意:set 会连本地变量一起列出来,env 只列环境变量。这个差别后面还会提到。

三、程序怎么拿到环境表

环境变量在内存里是一张表:一个字符指针数组,每个指针指向一个以 \0 结尾的 名称=内容 字符串,数组最后以空指针收尾。

拿到它有几种方式。

1. main 的第三个形参

main函数的第三个形参就是环境变量表。

#include <stdio.h>

int main(int argc, char *argv[], char *env[])
{
    for (int i = 0; env[i] != NULL; i++) {
        printf("%s\n", env[i]);
    }
    return 0;
}

这个写法在 Linux 上普遍可用,但它不属于 C 标准。标准只规定了 argc 和 argv,第三个参数靠的是编译器和 C 库的实现约定。换到别的平台不一定认,写可移植代码时要留意。

2. environ

glibc 有一个全局变量 environ,指向同一张表:

#include <stdio.h>

extern char **environ;

int main(void)
{
    for (int i = 0; environ[i] != NULL; i++) {
        printf("%s\n", environ[i]);
    }
    return 0;
}

environ 在 glibc 的 <unistd.h> 里有声明,自己再 extern 声明一次也能编译通过,类型一致即可。两种写法指向同一块数据。

3. getenv

大多数时候你只关心某一个变量,不需要遍历整张表:

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    const char *path = getenv("PATH");
    if (path == NULL) {
        printf("PATH is not set\n");
        return 1;
    }
    printf("%s\n", path);
    return 0;
}

getenv 找不到时返回 NULL,一定要判断,不要直接拿去 printf("%s"),那是空指针解引用。

要修改环境,可以用 putenv 或 setenv。两者的差别在所有权:putenv 把传入的字符串本身挂进环境表,调用之后那块内存归环境表管,不能再改;setenv 会复制一份。多线程程序里更推荐 setenv。

四、本地变量与环境变量

1. 赋值与 export

$ i=10
$ echo $i
10

变量确实存在,但它只活在当前 shell 里。子进程看不到。

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    const char *v = getenv("MYENV");
    printf("%s\n", v ? v : "(not found)");
    return 0;
}
$ MYENV=hello
$ ./code
(not found)
$ export MYENV
$ ./code
hello

同样是赋值,加了 export 之后子进程就能看到。这说明 bash 内部维护着两张表:一张本地变量表,一张环境变量表。i=10 只写本地表,export i 把变量搬进环境表。env 看的是后者,set 看的是两张表合起来。

2. 子进程为什么能看到

fork 出来的子进程一开始和父进程共享同一份数据,随后 exec 换掉整个程序。换程序的时候,地址空间里的代码和数据都被替换,只有内核记录下来的、要传给新程序的那部分内容会保留,环境表就是其中之一。

换句话说,环境变量能被子进程继承,是因为内核在 exec 时把它当作参数传了过去,而不是因为它存在某个全局变量里。

五、这些数据放在哪里

现在可以回答开头那个问题了:argc、argv、envp 这些数据,在进程的地址空间里处于什么位置。

它们全都在栈顶。
在这里插入图片描述

图:从低地址往上依次是 argc、argv 数组、NULL、envp 数组、NULL、辅助向量,字符串内容在最上面。

排序从低地址到高地址:

  • argc,一个整数。
  • argv[0] 到 argv[argc-1],每个元素是指向字符串的指针。
  • 一个 NULL,标记 argv 结束。
  • envp[0] 到 envp[m-1],环境表。
  • 再一个 NULL,标记 envp 结束。
  • 辅助向量(auxv),内核传给动态链接器的信息,比如页大小、程序入口地址、随机数种子。
  • 最后是那些字符串本身。

表的每个格子只存指针,字符串内容在更高的地方,两者不相邻。相邻两个元素指向的字符串之间隔着前一个字符串的长度,所以它们的地址差不是固定的 8 字节,而是随着字符串长短变化。

envp 就是前面说的 environ,同一张表的不同名字。

六、进程地址空间

栈顶只是整个地址空间的一小部分。完整看一遍,才能理解后面关于虚拟地址的结论。
在这里插入图片描述

图:32 位 Linux 的经典布局。用户空间 3 GB,内核空间 1 GB,栈在高地址向下生长,堆在低地址向上生长。

从低到高:

  • 正文代码段(text),编译出来的机器指令,只读。
  • 已初始化数据段(data),比如 int g_val = 100;。
  • 未初始化数据段(bss),比如 int g_unval;,不占可执行文件的体积,加载时清零。
  • 堆(heap),malloc 从这里要空间,向高地址生长。
  • 共享库和文件映射区,mmap 的用武之地,动态库映射在这里。
  • 栈(stack),函数调用、局部变量,向低地址生长。
  • 栈顶之上是命令行参数和环境变量。
  • 最高的 1 GB 归内核,用户态代码访问不到。

这是 32 位的经典划分:总共 4 GB,用户 3 GB,内核 1 GB。64 位下地址空间大得多,用户空间的边界也不再是 3 GB,但各段的相对位置和生长方向是一样的。

内核怎么描述它

每个进程有一个 mm_struct 描述整个用户空间,task_struct 里有指针指向它:

struct task_struct {
    /* ... */
    struct mm_struct *mm;
    struct mm_struct *active_mm;
    /* ... */
};

普通进程的 mm 指向自己的地址空间;内核线程没有用户空间,mm 为 NULL,改用 active_mm 借用上一个进程的。

mm_struct 里记着各段的边界:

struct mm_struct {
    /* ... */
    unsigned long start_code, end_code, start_data, end_data;
    unsigned long start_brk, brk, start_stack;
    unsigned long arg_start, arg_end, env_start, env_end;
    /* ... */
};

注意最后一行:arg_start、arg_end、env_start、env_end。前面那两张表的位置,内核是明确记下来的,不是靠约定猜的。

每一段连续的虚拟内存区域用一个 vm_area_struct 描述,也就是 VMA。区域少时用单链表串起来,由 mmap 指向表头;区域多时改用红黑树,由 mm_rb 指向树根,查找更快。

七、父子进程的同一个地址

来看一个奇怪的现象:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int g_val = 100;

int main(void)
{
    pid_t id = fork();
    if (id < 0) {
        perror("fork");
        return 1;
    }

    if (id == 0) {
        g_val = 200;
        printf("child : %d, %p\n", g_val, (void *)&g_val);
    } else {
        sleep(1);
        printf("parent: %d, %p\n", g_val, (void *)&g_val);
    }
    return 0;
}
child : 200, 0x60104c
parent: 100, 0x60104c

子进程把 g_val 改成了 200,父进程读到的还是 100,两边打印的地址一模一样,值却不一样

如果这个地址是物理地址,同一块内存不可能同时存两个值。所以得出结论:它只能是虚拟地址。

在这里插入图片描述

图:fork 时父子共享物理页并置为只读;子进程一旦写入,内核复制出一页给它,虚拟地址不变,物理页变了。

fork 刚完成时,父子两边的页表都指向同一批物理页,这些页被标记为只读。谁尝试写入,就会触发缺页异常,内核复制一份新的物理页,把写者的页表改指向新页。这个过程叫写时拷贝(copy-on-write)。

所以地址相同、内容不同,是因为两个进程的页表不同:同一个虚拟地址,在两张页表里翻译出了不同的物理地址。

0x60104c 是虚拟地址,不是内存条的某个物理位置。你在 C/C++ 里见到的所有指针值,包括 &g_val、malloc 的返回值、printf("%p") 的输出,全是虚拟地址。物理地址只有内核和 MMU 知道,用户态程序看不到。

八、为什么不能直接用物理内存

假设没有虚拟地址,程序直接操作物理地址,会碰到三个问题。

第一个是安全。任何一个进程都能读写任意物理内存,一个恶意程序可以改掉别的进程甚至内核的数据。没有边界,也就没有隔离。

第二个是地址不确定。程序加载到哪块物理内存,取决于当时内存的占用情况。第一次运行可能被放到 0x00000000,第十次运行时内存里已经有别的进程,位置就变了。编译器没法在编译期定下地址,链接器的工作也做不下去。

第三个是效率。物理内存不够时要把进程换到磁盘。如果按物理地址管理,一个进程就是一大块连续内存,换出就得整块搬走;有虚拟地址和分页,就可以按页换,只搬真正需要的那几页。

虚拟地址空间加页表解决了这些。进程看到的是一段连续、独立、从固定起点开始的地址空间,翻译工作交给 MMU 和页表,内核负责维护页表。

顺带解决了一个问题:malloc 申请内存时,内核可能一个字节的物理内存都没给,只在虚拟地址空间里划了一段区间、记下一个 VMA。真正访问到某一页时,才触发缺页,分配物理页、建立映射。这叫延迟分配,用户和程序都察觉不到。

九、总结

本文从main函数的形参写起,交代了它们是 exec 加载程序时由内核放在栈顶的。接着讲环境变量:怎么定义,export 之后子进程为什么就能看见,程序怎样分别用第三个形参、environ、getenv 拿到这些变量,以及 set 和 env 列出的为什么不是同一批变量。

然后讲了地址。先给出栈顶那段数据的排列顺序,再往下看整个进程地址空间,从 text、data、bss 一直到栈和最高的内核空间,也顺带看了内核用 mm_struct 和 VMA 记录这些区域的方式。最后解释了为什么fork 之后父子进程打印出同一个地址,值却不一样以及程序为什么不能直接操作物理内存。
希望对你的学习有所帮助!

C/C++ 栈帧笔记 原创链接 非原创, 有删改,仅做个人笔记 1.我们在调用它的时候系统做了什么? 2.main函数中如果还有另一个函数,在跳转后运行完这个函数时,编译器怎么知道下一行执行哪个语句呢?会不会又从头执行了? 3.函数在结束之后(运行到反花括号“}”处),系统又是怎么处理的? 4.不同的语言对函数形参内存的处理都是一样的吗? 5.函数的返回值有哪些类型?都是怎么从函数中返回回来的呢? 示例代码: #... 阅读详情

相关推荐

Go程序编译运行全解析:从源码到可执行文件的完整生命周期

在计算机编程领域,编译是将高级语言源代码转换为机器可执行代码的核心过程,其原理涉及词法分析、语法分析、语义检查和代码生成等多个阶段。Go语言通过其高度集成的工具链,将这一复杂过程封装为简洁的`go build`命令,实现了极快的编译速度和静态链接的部署优势。从技术价值看,Go编译器采用手写词法分析器和递归下降语法分析器生成抽象语法树,并在语义分析阶段进行严格的类型检查,随后转换为静态单赋值中间表示进行多轮优化,最终生成平台相关的机器码。这种设计在工程实践中显著提升了开发效率,尤其适用于需要快速迭代和便捷部署

weixin_34348174的博客 766

Linux进程(八).进程程序替换

fork() 之后,⽗⼦各⾃执⾏⽗进程代码的⼀部分,如果⼦进程就想执⾏⼀个全新的程序呢?进程的程序 替换来完成这个功能!那么覆盖具体是靠什么实现的呢?答案是exec系列接口:首个参数path是新程序所在路径(字符串)。注意:exec系列函数只有失败返回值,没有替换成功返回值!也就是不做返回值判断,只要有返回值,就是就是替换失败!如果调⽤成功则加载新的程序从启动代码开始执⾏,不再返回。使用该函数,需要程序所在路径(path)。同时“..."代表该函数的参数列表arg是个可变参数列表(是个链表),形参的个数可变

2401_87941709的博客 362

C语言底层揭秘:从GCC编译流程到内存布局与指针本质

C语言的高效来自于对底层机制的把握,理解程序从源代码到可执行文件的过程是第一步。GCC编译流程涵盖预处理、编译、汇编与链接,每个阶段都影响最终指令的生成。程序运行时的内存布局分为代码段、数据段、栈与堆,指针作为保存地址的变量,在反汇编视角下仅是寄存器与内存间的地址传递。掌握这些概念,能帮助开发者快速定位段错误、野指针和数组越界问题,也能更好地理解程序性能瓶颈。在系统开发、嵌入式和高性能计算等场景中,这种底层视角尤为关键。本文以C语言为入口,串联编译流程、内存分区、指针运算与反汇编解读,配合实例演示,为进阶C

weixin_34292402的博客 361

Linux系统(03):进程概念

本文系统讲解 Linux 进程全套核心知识点,包含进程概念、fork 创建子进程、僵尸 / 孤儿进程、进程优先级、环境变量、虚拟地址空间与写时拷贝,附带实操代码、命令与面试题,梳理校招高频考点,理解 Linux 进程调度与内存底层原理。

2401_86650143的博客 327

64位Windows内核驱动开发实战:进程内存读写与安全通信

在Windows系统开发中,内核模式驱动程序是连接硬件与操作系统的核心技术组件,运行在Ring 0特权级,拥有直接访问系统资源和物理内存的权限。其工作原理基于Windows Driver Model (WDM) 或 Kernel-Mode Driver Framework (KMDF),通过处理I/O请求包(IRP)和定义设备控制码(IOCTL)实现与用户态程序的安全通信。这项技术的核心价值在于为系统监控、性能调试、安全软件等提供底层支持,广泛应用于反作弊系统、硬件加速接口和高级调试工具等场景。本文聚焦于6

weixin_34025051的博客 340

利用PV操作控制进程的并发进行---经典题解

前趋图 先理清楚前趋图中的逻辑关系:P1没有前驱,P2的前驱是P1,P3的前驱是P1、P2,P4的前驱是P2,P5的前驱是P3、P4。 前驱就是指只有在前驱进程完成后,该进程才能开始执行。由图可知,这里进程之间有6条有向弧,分别表示为P1→P2,P1→P3,P2→P3,P2→P4,P3→P5,P4→P5, 各个进程间的逻辑关系,那么我们需要设定6个信号量(S1、S2、S3、S4、S5、S6),利用PV操作来控制这些过程。 对于第一个空,P1执行完成之后,需要通知P2、P3可以开始,此处需要V(S

Yan Ka Kue的博客 6135

C中的指针

指针 指针是C语言的特色,为什么C语言会有指针?因为C语言是底层语言,底层语言时时刻刻不可避免的会与内存打交道,指针带来了操作内存的强大能力和编写代码的便利性,缺点是大大增加了代码的复杂性和阅读难度,且增大了bug出现的机率,从某些方面来说非常考验程序员的功底。所谓指针就是访问内存地址,一个内存单元为1字节,但是单元编号却很大,64位程序用8个字节编号,32位程序用4字节编号,4字节最大能表示4294967295,这就是为什么32位操作系统最大支持4G内存的原因。内存控制器通过地址编号来存取内存单元,变量名

jaj2003的博客 663

深入解析SerDes系统中的时钟数据恢复电路(CDR)——基于Cadence的PLL/Bang-Bang/PS-PI架构实现与仿真

本文深入解析SerDes系统中的时钟数据恢复电路(CDR),重点探讨基于Cadence平台的PLL、Bang-Bang和PS-PI三种架构的实现与仿真。通过详细的技术对比和工程实践案例,揭示CDR在高速串行通信中的关键作用,并提供TSMC 28nm工艺下的优化策略与故障排查经验,助力工程师应对10Gbps以上SerDes设计挑战。

weixin_29202687的博客 249

C语言学习/复习8----函数栈帧(汇编指令、寄存器、内存模型)

1.1函数的局部变量/参数/调用ebp:栈基址指针(栈帧底部固定锚点);esp:栈栈顶指针(栈当前顶端位置)。每一次函数调用都会在开辟一块独立内存区域,称为,局部变量、形参、返回地址、旧 ebp 等全部存放在对应栈帧里。吃透与,就能彻底理解局部变量、传参、函数调用、返回的全部底层原理,这就是 C 语言程序运行的栈层面底层核心逻辑。

Hncj2022的博客 378

实战指南:基于华为Ascend NPU的大模型W8A8量化调优与精度提升

本文提供了一份基于华为Ascend NPU进行大模型W8A8量化的实战调优指南。详细介绍了从环境搭建、使用msmodelslim框架进行基础量化,到通过离群值抑制、校准集优化和量化回退等关键技术提升精度的全流程。旨在帮助开发者在华为NPU硬件上实现模型推理的显著加速,同时有效控制精度损失。

weixin_29227425的博客 270

从零到一:手把手教你用YonBuilder for NCC搭建NC Cloud 2021.11开发环境(含避坑指南)

本文详细指导如何使用YonBuilder for NCC搭建NC Cloud 2021.11开发环境,涵盖硬件要求、软件安装、YonBuilder配置及常见故障排除。特别提供避坑指南,帮助开发者快速上手NCC开发,确保环境配置的原子化分解和开发思维的体系化建立。

weixin_42523326的博客 340

Linux系统(09):线程概念与控制

本文系统讲解Linux线程核心原理,阐明线程本质为内核轻量级进程(LWP),依托共享进程地址空间实现高效调度。文中深入解析虚拟内存管理机制,涵盖页表结构、二级页表寻址、MMU与TLB作用及缺页异常处理;对比进程与线程在资源分配、上下文切换开销、通信方式上的核心差异,明确线程低开销、高并发的优势,以及单线程崩溃影响全进程的风险。同时详细梳理线程创建、终止、等待、分离四大核心操作。

2401_86650143的博客 395

skywalking各项指标说明

skywalking各项指标说明

没有简介 2486

深入理解函数栈帧的创建与销毁——从汇编视角彻底搞懂函数调用

函数栈帧(stack frame)是函数调用过程中在调用栈(call stack)上开辟的一块连续空间。每次函数调用都会创建自己的栈帧,函数返回时销毁。函数参数(调用方压入的实参副本)返回地址(call 指令压入,告诉函数调用结束后回到哪里)上一层函数的 ebp(用于恢复调用方的栈帧)临时数据:非静态局部变量、编译器生成的临时变量被保存的寄存器(函数执行期间可能用到 ebx/esi/edi 等,需先保存原值以便返回时恢复)一个函数的栈帧 = 从它的 ebp 到它的 esp 之间的那块内存。;

YSL070124的博客 411

为 ABAP BSP 与 ICF 量身打造的错误页实践:从 CX_ROOT 到 SICF 的端到端落地指南

摘要 本文系统讲解SAP ABAP平台中BSP与ICF层的错误页定制方法。在BSP层,通过标记错误页属性和绑定ERROR_OBJECT异常对象,实现友好的错误展示与日志记录。ICF层通过SICF事务码统一配置错误页、登录与登出逻辑,支持服务树继承。文章包含可直接落地的代码示例,并针对现代Fiori架构给出演进建议,帮助开发者提升用户体验与系统安全性。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 219

【人工智能之深度学习】21. 自动驾驶感知实战:YOLOv4复杂天气优化方案(BDD100K全代码+效果对比)

摘要:自动驾驶摄像头在雨雾天气下易受图像模糊、目标遮挡影响,导致YOLOv4检测精度骤降。本文基于BDD100K公开数据集(含多天气场景),设计三阶段优化方案:1)物理建模生成雨雾效果(雨滴线条+大气散射模型),将恶劣天气样本占比从20%提升至40%;2)在YOLOv4颈部嵌入CBAM注意力机制,强化模糊区域特征提取;3)L1剪枝(30%)+TensorRT量化实现轻量化。需特别说明:本文为教学虚拟案例,不可直接用于车载部署,实际落地需结合多传感器融合与ISO 26262认证。

专注于人工智能、软件开发、工控自动化、工厂数字化及智能化等领域,希望和大家共同进步! 1678

Linux 线程编程:从进程、分页到线程控制与封装

<think>我们要求根据内容生成≤150字的文章摘要。内容是关于Linux线程概念、实现、控制等。需要简洁概括核心要点。摘要要控制在150字以内。注意是“文章摘要”,提取关键信息:线程是进程内部执行流,Linux中通过轻量级进程实现,共享地址空间;线程优点、缺点;线程控制函数pthread系列;线程与进程区别。尽量精炼。</think>Linux线程是进程内的执行流,共享地址空间,内核以轻量级进程(LWP)实现。线程创建切换开销小,但缺乏隔离、健壮性低。通过pthread库封装cl

有且仅有质量分95以上的博客 431

JEDEC JESD51-14 瞬态双界面测试方法详解

(瞬态双界面测试方法,用于测量具有单路径热流的半导体器件的结到壳热阻 RθJC,包括结到壳顶部 RθJCT 和结到壳底部 RθJCB)。该标准于 2010 年发布(版本 JESD51-14.1),旨在提供一种准确、可重复的瞬态方法来测量功率半导体器件(如 MOSFET、IGBT)的结到壳热阻(RthJC),取代传统的稳态测量方法(如 MIL-STD-883E METHOD 1012.1)。方法,通过比较两种不同热界面材料(TIM)下的瞬态温度响应曲线,提取 RthJC,而无需直接测量壳温(Tc)。

专注于前沿技术的探索与实践,热衷于从代码逻辑、架构设计到行业应用等多维度深挖技术本质。期待在技术之路上与你共同成长~ 603

Qt 项目代码解释(4)

当我们创建项目成功后会自动生成一些文件,那文件中的代码是什么意思呢?

2402_83272216的博客 1399

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

环形缓冲区实战:TuxManager用HistoryBuffer高效存储性能曲线的原理

TuxManager 是一款类似 Windows 任务管理器的 Linux 系统监控工具,它的 CPU、内存、磁盘曲线能连续滚动 15 分钟而不占用额外内存,靠的就是一个仅 175 行的环形缓冲区类——[historybuffer.h](https://link.gitcode.com/i/534b2bb78060ced70d36f122893eb809) 中的 `HistoryBuffer`。本

gitblog_00209的博客 435

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

C++学习(1) ——C与C++

命名空间又称为名字空间,是程序员命名的内存区域,程序员根据需要指定一些有名字的空间域,把一些全局实体分别存放到各个命名空间中,从而与其他全局实体分隔开。通俗的说,每个名字空间都是一个名字空间域,存放在名字空间域中的全局实体只在本空间域内有效。名字空间对全局实体加以域的限制,从而合理的解决命名冲突。char val2;在声明一个命名空间时,大括号内不仅可以存放变量,还可以存放以下类型:变量、常量、函数、结构体、引用、类、对象、模板、命名空间等,它们都称为实体//定义变量//定义常量。

weixin_46640900的博客 368

RAPR 驱动存储清理完全指南:20 分钟给 C 盘腾出几个 GB

Driver Store Explorer(RAPR)是一款免费开源的 Windows 驱动存储清理工具,可以完整列出、备份并批量删除 DriverStore 中积压的旧驱动包。这篇指南带你走完"安装 → 备份 → 识别旧版 → 删除 → 验证"的完整流程,C 盘通常能释放 1~5GB 甚至更多空间,全程不需要命令行基础。 ## 那个只剩 1.2GB 的 C 盘,被谁吃掉了 🍂 周一上午 9

gitblog_00811的博客 183

C语言程序内存分段

本文详细讲解了C语言程序的内存分段模型,主要包括代码段(.text)、只读数据段(.rodata)、初始化数据段(.data)、未初始化数据段(.bss)、堆(heap)、栈(stack)和内核空间。通过示例代码展示了各段的内存地址分布,并特别说明了static、const、字符串字面量等特殊情况的存储位置。文章还总结了实际开发中的注意事项:避免栈溢出、不要修改字符串常量、及时释放堆内存、区分全局和局部变量的初始化行为等。这些内存分段知识对理解程序运行机制和编写高效、安全的C代码非常重要。

qq_44801116的博客 220

C语言指针详解 函数指针 指针函数

C语言中,定义变量时,在变量名 前 写一个 * 星号,这个变量就变成了对应变量类型的指针变量。必要时要加( ) 来避免优先级的问题。引申:C语言中,定义变量时,在定义的最前面写上typedef ,那么这个变量名就成了一种类型,即这个类型的同义词。

qq_33552379的博客 353

【编译基础】程序从出生到入土

学完直接飞升仙界

qq_43840665的博客 1356
上一篇: Linux 进程状态:R、S、D、T、Z 与僵尸孤儿进程
下一篇: Linux 虚拟内存:页表、缺页与写时复制
jinyishu_
博客等级 码龄1年 53粉丝 82原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值