electron基础使用

安装以及运行

当前node版本18+,按照官网提供操作,npm init进行初始化操作,将index.js修改为main.js,执行npm install --save-dev electron。(这里我挂梯子下载成功了。),添加如下代码至package.json

  "scripts": {"start": "electron ."},

新建一个index.html文件,内容随意。新建一个main.js文件,内容如下。主要意思是将一个界面加载到一个应用窗口中。createWindow()方法来将index.html加载进一个新的BrowserWindow实例。在 app 模块的 ready 事件被激发后才能创建浏览器窗口。 可以通过使用 app.whenReady() API来监听此事件。 在whenReady()成功后调用createWindow()函数来打开窗口。

  • app 模块,它控制应用程序的事件生命周期。
  • BrowserWindow 模块,它创建和管理应用程序 窗口。
const { app, BrowserWindow } = require("electron");const createWindow = () => {const win = new BrowserWindow({width: 800,height: 600,});win.loadFile("index.html");
};app.whenReady().then(() => {createWindow();
});

添加关闭应用窗口事件,若输出有乱码则参考该博客输出乱码解决

app.on("window-all-closed", () => {if (process.platform !== "darwin") {console.log("关闭");app.quit();}
});

在这里插入图片描述
添加代码至BrowserWindow中,可以打开控制台操作,应用开启后可以使用ctrl+shift+i打开控制台

    webContents: {openDevTools: true,},

在这里插入图片描述

vue3中运行

基于@quick-start/electron框架快速创建一个基于vue3+electron的项目结构。

npm create @quick-start/electron

最近又新看见一个框架electron-gg地址,感觉挺好的也是基于vue3

预加载脚本

Electron 的主进程是一个拥有着完全操作系统访问权限的 Node.js 环境。 除了 Electron 模组 之外,您也可以访问 Node.js 内置模块 和所有通过 npm 安装的包。 另一方面,出于安全原因,渲染进程默认跑在网页页面上,而并非 Node.js里。

主进程负责管理整个应用程序的生命周期,包含创建窗口,运行在nodejs环境中,每次打开一个窗口,实际是创建一个渲染进程。主进程和渲染进程之间通过IPC通信。

为了将 Electron 的不同类型的进程桥接在一起,我们需要使用被称为 预加载 的特殊脚本。

BrowserWindow 的预加载脚本运行在具有 HTML DOM 和 Node.js、Electron API 的有限子集访问权限的环境中。预加载脚本默认 沙盒化不再拥有完整 Node.js 环境的访问权。 实际上,这意味着你只拥有一个 polyfilled 的 require 函数,这个函数只能访问一组有限的 API。
在这里插入图片描述
预加载脚本在渲染器加载网页之前注入。 如果你想为渲染器添加需要特殊权限的功能,可以通过 contextBridge 接口定义 全局对象。

创建preload.js文件,该脚本通过 versions 这一全局变量,将 Electron 的 process.versions 对象暴露给渲染器。

const { contextBridge } = require('electron')
contextBridge.exposeInMainWorld('versions', {node: () => process.versions.node,chrome: () => process.versions.chrome,electron: () => process.versions.electron// 除函数之外,我们也可以暴露变量
})

将脚本附在渲染进程上,在 BrowserWindow 构造器中使用 webPreferences.preload 传入脚本的路径。webPreferences对象是用来定制渲染进程中的特性,其中preload属性是在网页运行其他脚本之前执行的脚本,用来提前注入Nodejs api或者其他需要再渲染进程中运行的脚本。

const { app, BrowserWindow } = require('electron')
const path = require('node:path')const createWindow = () => {const win = new BrowserWindow({width: 800,height: 600,webPreferences: {preload: path.join(__dirname, 'preload.js')}})win.loadFile('index.html')
}app.whenReady().then(() => {createWindow()
})

现在渲染器能够全局访问 versions 了,但是还需要将版本等数据渲染到界面中,可以通过window.versions访问,也可以很简单地使用 versions 来访问。 新建一个 renderer.js 脚本, 使用 document.getElementById DOM API 来替换 id 属性为 info 的 HTML 元素的文本。

const information = document.getElementById('info')
information.innerText = `本应用正在使用 Chrome (v${versions.chrome()}), Node.js (v${versions.node()}), 和 Electron (v${versions.electron()})`

然后再index.html文件中引入

  <script src="./renderer.js"></script>

在这里插入图片描述

进程之间通信(简略)

Electron 的主进程和渲染进程有着清楚的分工并且不可互换,无论是从渲染进程直接访问 Node.js 接口,亦或者是从主进程访问 HTML 文档对象模型 (DOM),都是不可能的。
解决这一问题的方法是使用进程间通信 (IPC)。可以使用 Electron 的 ipcMain 模块 ipcRenderer 模块来进行进程间通信。 为了从你的网页向主进程发送消息,你可以使用ipcMain.handle设置一个主进程处理程序(handler),然后在预处理脚本中暴露一个被称为 ipcRenderer.invoke 的函数来触发该处理程序(handler)。

const { contextBridge, ipcRenderer } = require("electron");
contextBridge.exposeInMainWorld("versions", {node: () => process.versions.node,chrome: () => process.versions.chrome,electron: () => process.versions.electron,ping: () => ipcRenderer.invoke("ping"),
});

主进程中设置你的 handle 监听器,在 HTML 文件加载之前完成了这些,所以才能保证在你从渲染器发送 invoke 调用之前处理程序能够准备就绪。
渲染主进程中引入const { app, BrowserWindow, ipcMain } = require("electron");

app.whenReady().then(() => {ipcMain.handle("ping", () => "pong");createWindow();
});

将发送器与接收器设置完成之后,现在你可以将信息通过刚刚定义的 ‘ping’ 通道从渲染器发送至主进程当中。修改renderer中的文件

const func = async () => {const response = await window.versions.ping()console.log(response) // 打印 'pong'
}func()

在这里插入图片描述

打包程序

Electron 的核心模块中没有捆绑任何用于打包或分发文件的工具。 如果您在开发模式下完成了一个 Electron 应用,需要使用额外的工具来打包应用程序 (也称为可分发文件) 并分发给用户 。 可分发文件可以是安装程序 (例如 Windows 上的 MSI) 或者绿色软件 (例如 macOS 上的 .app 文件)。Electron Forge 是一个处理 Electron 应用程序打包与分发的一体化工具。 在工具底层,它将许多现有的 Electron 工具 (例如 @electron/packager、 @electron/osx-sign、electron-winstaller 等) 组合到一起。

安装打包工具npm install --save-dev @electron-forge/cli,然后输入npx electron-forge import等待初始化完毕,之后package会变为如下图所示
在这里插入图片描述

创建一个可分发版本

要创建可分发文件,请使用项目中的 make 脚本,该脚本最终运行了 electron-forge make 命令。npm run make注意如果npm init 的时候,author 与 description 可为任意值,但对于应用打包是必填项。如果没有则会出现如下错误
在这里插入图片描述
注意,项目打包路径中不能包含中文字符,否则报错。 打包成功如下
在这里插入图片描述

  1. npm run make它将首先运行 electron-forge package ,把您的应用程序 代码与 Electron 二进制包结合起来。
  2. 完成打包的代码将会被生成到一个特定的文件夹中。 然后它将使用这个文件夹为每个 maker 配置生成一个可分发文件。
    在这里插入图片描述
    打包完成后查看.exe文件运行。
    在这里插入图片描述

代码签名

为了将桌面应用程序分发给最终用户,我们 强烈建议 您对 Electron 应用进行 代码签名。 代码签名是交付桌面应用程序的重要组成部分,并且它对于应用程序的自动更新功能 (将会在教程最后部分讲解) 来说是必需的。

代码签名是一种可用于证明桌面应用程序是由已知来源创建的安全技术。 Windows 和 macOS 拥有其特定的代码签名系统,这将使用户难以下载或启动未签名的应用程序。代码签名在forge.config.js文件中添加

流程模型

多进程模型:在早期,浏览器通常使用单个进程来处理所有这些功能。 虽然这种模式意味着您打开每个标签页的开销较少,但也同时意味着一个网站的崩溃或无响应会影响到整个浏览器。Chrome 团队决定让每个标签页在自己的进程中渲染, 从而限制了一个网页上的有误或恶意代码可能导致的对整个应用程序造成的伤害。 然后用单个浏览器进程控制这些标签页进程,以及整个应用程序的生命周期。
在这里插入图片描述
electron包含两种进程:主进程和渲染进程。每个 Electron 应用都有一个单一的主进程,作为应用程序的入口点。 主进程在 Node.js 环境中运行,这意味着它具有 require 模块和使用所有 Node.js API 的能力。主进程的主要目的是使用 BrowserWindow 模块创建和管理应用程序窗口。BrowserWindow 类的每个实例创建一个应用程序窗口,且在单独的渲染器进程中加载一个网页。 您可从主进程用 window 的 webContent 对象与网页内容进行交互。

const { BrowserWindow, app } = require("electron");const createWindow = () => {const win = new BrowserWindow({width: 800,height: 800,});win.loadURL("https://github.com"); //网页以地址的形式引入console.log(win.webContents); //主进程中输出
};app.whenReady().then(() => {createWindow();
});

在这里插入图片描述
当一个 BrowserWindow 实例被销毁时,与其相应的渲染器进程也会被终止。

渲染进程:每个 Electron 应用都会为每个打开的 BrowserWindow ( 与每个网页嵌入 ) 生成一个单独的渲染器进程。 洽如其名,渲染器负责 渲染 网页内容。 所以实际上,运行于渲染器进程中的代码是须遵照网页标准的 (至少就目前使用的 Chromium 而言是如此) 。渲染器无权直接访问 require 或其他 Node.js API

Preload 脚本:预加载(preload)脚本包含了那些执行于渲染器进程中,且先于网页内容开始加载的代码 。 这些脚本虽运行于渲染器的环境中,却因能访问 Node.js API 而拥有了更多的权限。预加载脚本可以在 BrowserWindow 构造方法中的 webPreferences .preload选项里被附加到主进程。预加载脚本与浏览器共享同一个全局 Window 接口,并且可以访问 Node.js API,所以它通过在全局 window 中暴露任意 API 来增强渲染器,以便你的网页内容使用。

虽然预加载脚本与其所附着的渲染器在共享着一个全局 window 对象,但您并不能从中直接附加任何变动到 window 之上,因为 contextIsolation 是默认的,因此下面这段代码在窗口的控制台中输出undefined

//preload.js
window.myAPI = {desktop: true
}
//renderer.js
console.log(window.myAPI)
// => undefined

因此需要使用 contextBridge 模块来安全地实现交互.

const { contextBridge } = require('electron')
contextBridge.exposeInMainWorld('myAPI', {desktop: true
})

在这里插入图片描述

上下文隔离

上下文隔离功能将确保您的 预加载脚本 和 Electron的内部逻辑 运行在所加载的 webcontent网页 之外的另一个独立的上下文环境里。 这对安全性很重要,因为它有助于阻止网站访问 Electron 的内部组件 和 您的预加载脚本可访问的高等级权限的API

这意味着,实际上,您的预加载脚本访问的 window 对象并不是网站所能访问的对象。 例如,如果您在预加载脚本中设置 window.hello = ‘wave’ 并且启用了上下文隔离,当网站尝试访问window.hello对象时将返回 undefined。

自 Electron 12 以来,默认情况下已启用上下文隔离,并且它是 所有应用程序推荐的安全设置。

其实不使用contextBridge 创建预加载脚本就是没有开启上下文的方式。

进程通信

进程通信的表现形式例如 UI 调用原生 API 或从原生菜单触发 Web 内容的更改。

IPC通道

在 Electron 中,进程使用 ipcMainipcRenderer模块,通过开发人员定义的“通道”传递消息来进行通信。 这些通道是 任意 (您可以随意命名它们)和 双向 (您可以在两个模块中使用相同的通道名称)的。

  1. 渲染器进程到主进程(单向)
    要将单向 IPC 消息从渲染器进程发送到主进程,您可以使用 ipcRenderer.send API 发送消息,然后使用 ipcMain.on API 接收。如下是一个事例,用户输入input内容,点击按钮会修改窗口的标题
index.htmlTitle: <input id="title"/><button id="btn" type="button">Set</button><script src="./renderer.js"></script>
renderer.js
const setButton = document.getElementById('btn')
const titleInput = document.getElementById('title')
setButton.addEventListener('click', () => {const title = titleInput.valuewindow.aaa.setTitle(title) // 渲染主线程使用预加载脚本暴露的通道,使用其提供的方法
})
const { contextBridge, ipcRenderer } = require("electron");
contextBridge.exposeInMainWorld("aaa", {//aaa是自定义的通道名//定义一个setTitle函数,通过send方法向主进程发生一个set-title消息,并携带参数setTitle: (title) => ipcRenderer.send("set-title", title), //send方法是一个异步行为
});
main.js
const { BrowserWindow, app, ipcMain } = require("electron");
const path = require("path");function handleSetTitle(e, title) {/* 事件对象 e 中获取发送该事件的 webContents 对象。在 Electron 中,webContents 代表了渲染器进程内的网页内容,每个 Electron 窗口里都有一个与其关联的 webContents 实例*/const webContents = e.sender;/* BrowserWindow.fromWebContents 方法,并传入前面获得的 webContents 对象,可以获取到关联该 webContents 的 BrowserWindow 实例。基本上这行代码是根据渲染器进程(webContents)找到对应的窗口对象(BrowserWindow 实例)。*/const w = BrowserWindow.fromWebContents(webContents);w.setTitle(title);
}const createWindow = () => {const win = new BrowserWindow({width: 800,height: 800,webPreferences: {preload: path.join(__dirname, "preload.js"),},webContents: {openDevTools: true,},});win.loadFile("index.html");
};app.whenReady().then(() => {//ipcMain.on监听set-title行为,箭头从渲染器进程中使用setTitle方法发送而来的set-title消息(可以理解vue的事件监听emit)ipcMain.on("set-title", handleSetTitle);createWindow();
});

在这里插入图片描述
其中事件对象的结构如下
在这里插入图片描述

  1. 渲染器进程到主进程(双向):如下是一个事例,用户选中一个文件,显示该文件的路径。
index.html<button type="button" id="btn">Open a File</button>File path: <strong id="filePath"></strong><script src="./renderer.js"></script>
renderer.js
const btn = document.getElementById("btn");
const filePathElement = document.getElementById("filePath");btn.addEventListener("click", async () => {const filePath = await window.electronAPI.openFile();filePathElement.innerText = filePath;
});
preload.js
const { contextBridge, ipcRenderer } = require("electron/renderer");
contextBridge.exposeInMainWorld("electronAPI", {/* 异步发送一个需要响应的消息,并且等待主进程的回复。这是异步操作,但允许以类似于同步调用的方式(使用 async/await 或者基于 Promise 的方法)等待响应。*/openFile: () => ipcRenderer.invoke("dialog:openFile"),
});
const { app, BrowserWindow, ipcMain, dialog } = require("electron/main");
const path = require("node:path");async function handleFileOpen() {/* dialog 模块用于显示原生的对话框,例如文件打开、保存、消息提示等。dialog 模块是 Electron 提供的一个系统对话框接口,可以在主进程中使用,用于与用户进行交互。dialog 模块通过其 showOpenDialog 方法用于显示一个文件选择对话框。这个方法是异步的,它会返回一个 Promise。当用户选择文件并关闭对话框时,Promise 被解决(resolved),并带有一个对象,这个对象包含了两个属性:canceled 和 filePaths。canceled 是一个布尔值,表示用户是否取消了对话框而没有选择任何文件filePaths 是一个数组,包含用户选择的一个或多个文件的完整路径。*/const { canceled, filePaths } = await dialog.showOpenDialog();if (!canceled) {return filePaths[0];}
}function createWindow() {const mainWindow = new BrowserWindow({webPreferences: {preload: path.join(__dirname, "preload.js"),},});mainWindow.loadFile("index.html");
}app.whenReady().then(() => {//会把处理完成的消息返回给渲染进程ipcMain.handle("dialog:openFile", handleFileOpen);createWindow();
});
  1. 主进程到渲染器进程:将消息从主进程发送到渲染器进程时,需要指定是哪一个渲染器接收消息。 消息需要通过其 ·WebContents 实例·发送到渲染器进程。 此 WebContents 实例包含一个 ·send ·方法,其使用方式与· ipcRenderer.send ·相同。如下实例是一个原生操作系统菜单控制的数字计数器。
index.htmlCurrent value: <strong id="counter">0</strong><script src="./renderer.js"></script>
renderer.js
const counter = document.getElementById("counter");window.electronAPI.onUpdateCounter((value) => {const oldValue = Number(counter.innerText);const newValue = oldValue + value;counter.innerText = newValue.toString();window.electronAPI.counterValue(newValue);
});
preload.js
const { contextBridge, ipcRenderer } = require("electron/renderer");
contextBridge.exposeInMainWorld("electronAPI", {onUpdateCounter: (callback) =>ipcRenderer.on("update-counter", (_event, value) => callback(value)),counterValue: (value) => ipcRenderer.send("counter-value", value),
});
main.js
const { app, BrowserWindow, Menu, ipcMain } = require("electron/main");
const path = require("node:path");function createWindow() {const mainWindow = new BrowserWindow({webPreferences: {preload: path.join(__dirname, "preload.js"),},});const menu = Menu.buildFromTemplate([{label: app.name,submenu: [{click: () => mainWindow.webContents.send("update-counter", 1),label: "Increment",},{click: () => mainWindow.webContents.send("update-counter", -1),label: "Decrement",},],},]);Menu.setApplicationMenu(menu);mainWindow.loadFile("index.html");// Open the DevTools.mainWindow.webContents.openDevTools();
}app.whenReady().then(() => {ipcMain.on("counter-value", (_event, value) => {console.log(value); // 主进程中输出打印});createWindow();
});

在这里插入图片描述

  1. 渲染器进程到渲染器进程:没有直接的方法可以使用 ipcMain 和 ipcRenderer 模块在 Electron 中的渲染器进程之间发送消息。将主进程作为渲染器之间的消息代理。 这需要将消息从一个渲染器发送到主进程,然后主进程将消息转发到另一个渲染器。从主进程将一个 MessagePort 传递到两个渲染器。 这将允许在初始设置后渲染器之间直接进行通信。

进程沙盒化

从 Electron 20 开始,渲染进程默认启用了沙盒,无需进一步配置。

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

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

相关文章

ORB算法特征提取

声明&#xff1a;学习过程中的知识总结&#xff0c;欢迎批评指正。 ORB算法提取两路输入图像&#xff08;图像A&#xff0c;图像B&#xff09;的特征点&#xff0c;根据提取的特征点进行特征匹配得到特征对。 ​ 图像金字塔 因为在现实世界中&#xff0c;同一个物体可能会以…

文生视频新王登场:Luma官宣免费、电影级大片生成,Sora?可灵?SD3.0?(内附网址)

✨点击这里✨&#xff1a;&#x1f680;原文链接&#xff1a;&#xff08;更好排版、视频播放、社群交流、最新AI开源项目、AI工具分享都在这个公众号&#xff01;&#xff09; 文生视频新王登场&#xff1a;Luma官宣免费、电影级大片生成&#xff0c;Sora&#xff1f;可灵&am…

Ubuntu server 24 (Linux) 安装部署samba服务器 共享文件目录 windows访问

1 安装 sudo apt update sudo apt-get install samba #启动服务 sudo systemctl restart smbd.service sudo systemctl enable smbd.service #查看服务 2 创建用户 #创建系统用户 sudo useradd test2 #配置用户密码 sudo smbpasswd -a test2 # smbpasswd: -a添加用户 …

SD3开源:AI绘画的新纪元,出图效果巨好,不容错过!(附教程)

大家好&#xff0c;我是画画的小强。 这两天&#xff0c;Stability AI 将史上最牛的AI绘画模型SD3开源了&#xff0c;真是有格局&#xff01; 虽说只是中杯的20亿参数版本&#xff0c;但我已经很满足了&#xff0c;再高的版本&#xff0c;我这普通的16G 4070Ti Super 显卡也跑…

HAL库开发--串口

知不足而奋进 望远山而前行 目录 文章目录 前言 学习目标 学习内容 开发流程 串口功能配置 串口功能开启 串口中断配置 串口参数配置 查询配置结果 发送功能测试 中断接收功能测试 printf配置 DMA收发 配置 DMA发送 DMA接收(方式1) DMA接收(方式2) 总结 前言…

shell编程基础(第18篇:更多的文件操作命令介绍)

前言 对于文件来说&#xff0c;除了它的文件内容之外&#xff0c;就是对其文件本身的操作&#xff0c;比如我们想要重命名文件、移动文件、复制文件、已经获取文件所在目录&#xff0c;文件名等操作&#xff0c;今天一起学习更多的文件操作相关的命令 basename 用于获取文件名…

C++ 32 之 静态成员函数

#include <iostream> #include <string> using namespace std;// 特点: // 1.在编译阶段就分配了内存空间 // 2.类内声明&#xff0c;在类外进行初始化 // 3.所有对象共享一份静态成员数据 class Students02{ public:int s_c;static int s_d;// 静态成员函数&#…

YOLOv8可视化界面PYQT5

yolov8&#xff0c;可视化界面pyqt。支持图片检测&#xff0c;视频检测&#xff0c;摄像头检测等&#xff0c;实时显示检测画面。支持自定义数据集&#xff0c;计数&#xff0c;fps展示……,即插即用&#xff0c;无需更改太多代码

非关系型数据库NoSQL数据层解决方案 之 Mongodb 简介 下载安装 springboot整合与读写操作

MongoDB 简介 MongoDB是一个开源的面向文档的NoSQL数据库&#xff0c;它采用了分布式文件存储的数据结构&#xff0c;是当前非常流行的数据库之一。 以下是MongoDB的主要特点和优势&#xff1a; 面向文档的存储&#xff1a; MongoDB是一个面向文档的数据库管理系统&#xff0…

TLE9879的基于Arduino调试板SWD刷写接口

官方的Arduino评估板&#xff0c;如下图所示&#xff1a; 如果你有官方的调试器&#xff0c;应该不用关注本文章&#xff0c;如下图连接就是&#xff1a; 如果&#xff0c;您和博主一样需要自己飞线的话&#xff0c;如下图所示&#xff1a;PCB的名称在右边整理&#xff0c;SWD的…

Elasticsearch 认证模拟题 - 20

一、题目 定义一个 pipeline&#xff0c;并且将 earthquakes 索引的文档进行更新 pipeline 的 ID 为 earthquakes_pipeline将 magnitude_type 的字段值改为大写如果文档不包含 batch_number&#xff0c;增加这个字段&#xff0c;将数值设置为 1如果已经包含 batch_number&…

大模型系列:Prompt提示工程常用技巧和实践

前言 Prompt提示语是使用大模型解决实际问题的最直接的方式&#xff0c;本篇介绍Prompt提示工程常用的技巧&#xff0c;包括Zero-Shot、Few-Shot、CoT思维链、Least-to-Most任务分解。 内容摘要 Prompt提示工程简述Prompt的一般结构介绍零样本提示Zero-Shot少样本提示Few-Sho…

nginx配置https协议(测试环境)

第一步申请证书 首先申请证书这一步&#xff0c;晚上有很多种方式实现&#xff0c;可以自己用算法实现&#xff0c;也可以找在线生成的网站&#xff0c;我这里使用了在线网站 https://www.toolhelper.cn/SSL/SSLGenerate 第二步将证书放到对应的目录下 这里我们主要用cert.pe…

探索大数据在信用评估中的独特价值

随着我国的信用体系越来越完善&#xff0c;信用将影响越来越多的人。现在新兴的大数据信用和传统信用&#xff0c;形成了互补的优势&#xff0c;大数据信用变得越来越重要&#xff0c;那大数据信用风险检测的重要性主要体现在什么地方呢?本文将详细为大家介绍一下&#xff0c;…

03-appium环境配置和启动参数设置

参考文章&#xff1a;https://blog.csdn.net/lovedingd/article/details/110949993 一、appium介绍 Appium是一个开源、跨平台的自动化测试框架&#xff0c;支持Android、IOS等平台&#xff0c;同时也支持多语言&#xff0c;比如&#xff1a;Java、Python等。 Appiumu通过扩展…

图论(一)之概念介绍与图形#matlab

图论&#xff08;一&#xff09;之概念介绍与图形目录 前言 一、图论介绍 二、基本概念 2.1图的概念 2.2图形分类 2.3邻接矩阵 2.3.1无向图 2.3.2有向图 2.3.3有向赋权图 2.4出度&#xff08;Outdegree&#xff09; 2.5入度&#xff08;Indegree&#xff09; 3.四种…

C语言 | Leetcode C语言题解之第145题二叉树的后序遍历

题目&#xff1a; 题解&#xff1a; void addPath(int *vec, int *vecSize, struct TreeNode *node) {int count 0;while (node ! NULL) {count;vec[(*vecSize)] node->val;node node->right;}for (int i (*vecSize) - count, j (*vecSize) - 1; i < j; i, --j)…

结构体对齐,与 触发 segment fault 为什么是 1024*132 ,而不是1024*128

1, 简单的小示例代码 按理说 malloc 的size 是 1024*128&#xff0c;这里却需要 1024*132才能及时触发 segmentation fault #include <stdlib.h> #include <stdio.h> #define SIZE 1024*131int main() {char *p 0;p malloc(SIZE);p[SIZE -1] a;free(p);printf(…

java学习 项目篇 一

学习地址&#xff1a;https://www.bilibili.com/video/BV1TP411v7v6?p6&spm_id_frompageDriver&vd_sourcea6f7db332f104aff6fadf5b3542e5875 后端环境搭建 Entity 实体&#xff0c;通常和数据库的表对应DTO 数据传输对象&#xff0c;用于程序中各层之间传递数据 (前端…

C++ PDF转图片

C PDF转图片#include "include/fpdfview.h" #include <fstream> #include <include/core/SkImage.h>sk_sp<SkImage> pdfToImg(sk_sp<SkData> pdfData) {sk_sp<SkImage> img;FPDF_InitLibrary(nullptr);FPDF_DOCUMENT doc;FPDF_PAGE …