solidity从入门到精通(持续更新)

ops/2024/10/21 11:31:27/

我一度觉得自己不知何时变成了一个浮躁的人,一度不想受外界干扰的我被干扰了,再无法平静的去看一本书,但我仍旧希望我能够克服这些,压抑着自己直到所有的冲动和奇怪的思想都无法再左右我行动。
自律会让你更加自律,放纵会让你更加放纵。
做人做事,都是如此。
简单的道理却要克服很多东西。做正确的事情而不是我想做的事情。发乎情,止乎理。


速成班


1全世界人们入门的统一标准hello world

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
contract HelloWeb3{string public _string = "Hello Web3!";
}

前两行类似于说明文件,contract说明你开始编写一个合约,这个合约的名字叫做helloweb3,合约内声明了一个字符串public _string叫做 “Hello Web3!”
这个hello world我编译失败了
莫名其妙又成功了,把编译的篮子放在了右边就成功了?

合约编译成功之后可以进行部署。
https://remix.ethereum.org/ remix合约的编译环境

大佬说是一个大佬的人:崔棉大师


2 值类型

bool类型
// 声明一个对外可访问的bool
bool public _bool = true;// 布尔运算
bool public _bool1 = !_bool; // 取非
bool public _bool2 = _bool && _bool1; // 与
bool public _bool3 = _bool || _bool1; // 或
bool public _bool4 = _bool == _bool1; // 相等
bool public _bool5 = _bool != _bool1; // 不相等
整型
// 整型
int public _int = -1; // 整数,包括负数
uint public _uint = 1; // 正整数
uint256 public _number = 20220330; // 256位正整数// 整数运算
uint256 public _number1 = _number + 1; // +,-,*,/
uint256 public _number2 = 2**2; // 指数
uint256 public _number3 = 7 % 2; // 取余数
bool public _numberbool = _number2 > _number3; // 比大小
地址类型

地址类型(address)有两类:
普通地址(address): 存储一个 20 字节的值(以太坊地址的大小)。
payable address: 比普通地址多了 transfer 和 send 两个成员方法,用于接收转账。

// 地址
address public _address = 0x7A58c0Be72BE218B41C608b7Fe7C5bB630736C71;
address payable public _address1 = payable(_address); // payable address,可以转账、查余额
// 地址类型的成员
uint256 public balance = _address1.balance; // balance of address

我有点不是太理解这个payable address

定长字节数组
// 固定长度的字节数组
bytes32 public _byte32 = "MiniSolidity"; 
bytes1 public _byte = _byte32[0]; 

32个字节组成的数组,也就是可以容纳32个元素,很明显后面的并没有使用,u8类型就是32个数字,hex
64类型就是32的元素组成的数组,0-f

枚举enum
// 用enum将uint 0, 1, 2表示为Buy, Hold, Sell
enum ActionSet { Buy, Hold, Sell }
// 创建enum变量 action
ActionSet action = ActionSet.Buy;

枚举的使用方法和各种语言的枚举方式几乎是相同的。
但是这段代码我有点没看明白。

// enum可以和uint显式的转换
function enumToUint() external view returns(uint){return uint(action);
}

在 Solidity 中,external 是一个函数修饰符,用于定义函数的可见性。函数的可见性决定了函数可以从哪里被调用。也就是external和public其实是同级的。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
contract HelloWeb3{string public _string = "Hello Web3!";bool public _bool = true;int public _int = -1;uint public _unit = 1;uint256 public _unitlong = 2222;
}contract my_accout{// 地址是160bits,最后压缩为40个16进制address public my_address = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;address payable public money_address = payable(my_address);uint256 public balance = money_address.balance;bytes32 public _byte32 =  "here is a hero";bytes1 public _byte1 = _byte32[0];enum ActionSet{Buy,Hold,Sell}ActionSet action = ActionSet.Buy;// 这个里面也存在类型转换// 这个操作是需要汽油费的function enumtounit() external view returns(uint){return uint(action);}
}

3函数function

function <function name>(<parameter types>) {internal|external|public|private} [pure|view|payable] [returns (<return types>)]

看着有一些复杂,让我们从前往后逐个解释(方括号中的是可写可不写的关键字):

function:声明函数时的固定用法。要编写函数,就需要以 function 关键字开头。

:函数名。

():圆括号内写入函数的参数,即输入到函数的变量类型和名称。

{internal|external|public|private}:函数可见性说明符,共有4种。
public:内部和外部均可见。
private:只能从本合约内部访问,继承的合约也不能使用。
external:只能从合约外部访问(但内部可以通过 this.f() 来调用,f是函数名)。
internal: 只能从合约内部访问,继承的合约可以用。
注意 1:合约中定义的函数需要明确指定可见性,它们没有默认值。
注意 2:public|private|internal 也可用于修饰状态变量。public变量会自动生成同名的getter函数,
用于查询数值。未标明可见性类型的状态变量,默认为internal。

[pure|view|payable]:决定函数权限/功能的关键字。payable(可支付的)很好理解,带着它的函数,运行的时候可以给合约转入 ETH。pure 和 view 的介绍见下一节。

[returns ()]:函数返回的变量类型和名称。

有关于pure 和view

刚开始学习 solidity 时,pure 和 view 关键字可能令人费解,因为其他编程语言中没有类似的关键字。solidity 引入这两个关键字主要是因为 以太坊交易需要支付气费(gas fee)。合约的状态变量存储在链上,gas fee 很贵,如果计算不改变链上状态,就可以不用付 gas。
包含 pure 和 view 关键字的函数是不改写链上状态的,因此用户直接调用它们是不需要付 gas 的
(注意,合约中非 pure/view 函数调用 pure/view 函数时需要付gas直接调用不需要支付,但是非pure/view函数间接调用是需要支付gas fee的)。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
contract FunctionTypes{uint256 public number = 5;constructor() payable {}// 函数类型// function (<parameter types>) {internal|external} [pure|view|payable] [returns (<return types>)]// 默认functionfunction add() external{number = number + 1;}// pure: 纯纯牛马// 没有读取权,因此什么东西都是别人传给他的// 瞎子function addPure(uint256 _number) external pure returns(uint256 new_number){new_number = _number+1;}// view: 看客function addView() external view returns(uint256 new_number) {new_number = number + 1;}// internal: 内部函数function minus() internal {number = number - 1;}// 合约内的函数可以调用内部函数function minusCall() external {minus();}// payable: 递钱,能给合约支付eth的函数function minusPayable() external payable returns(uint256 balance) {minus();    balance = address(this).balance;}
}
internal和external

internal是只可以被内部调用的函数方法,external是可以被外部调用的。
可以把内部调用函数写给外部方法里面,通过外部方法间接调用


4函数return

返回值:return 和 returns

Solidity 中与函数输出相关的有两个关键字:return和returns。它们的区别在于:
returns:跟在函数名后面,用于声明返回的变量类型及变量名。
return:用于函数主体中,返回指定的变量。

contract return_test{// 这种返回符合日常的使用function retrun_multiple() public pure returns (uint256,bool,uint256[3] memory){return (1,true,[uint256(2),3,5]);}// 这种属于是命名式的返回function return_withname() public pure returns (uint256 _int,bool _bool,uint256[3] memory _arr){_int = 1;_bool = false;_arr = [uint256(3),6,9];//return(1, true, [uint256(1),2,5]); 这种脱裤子放屁的行为也被支持}// 粘贴来的函数不能用,了解以下解构是怎么回事就好了// 读取返回值,解构式赋值function readReturn() public pure{// 读取全部返回值uint256 _number;bool _bool;bool _bool2;uint256[3] memory _array;(_number, _bool, _array) = returnNamed();// 读取部分返回值,解构式赋值(, _bool2, ) = returnNamed();}
}

这里uint256[3]声明了一个长度为3且类型为uint256的数组作为返回值。因为[1,2,3]会默认为uint8(3),因此[uint256(1),2,5]中首个元素必须强转uint256来声明该数组内的元素皆为此类型。数组类型返回值默认必须用memory修饰。
我有点没看明白这个memory
和rust有一个类似的地方,可以不接受不需要的参数

(, _bool2, ) = returnNamed();

5变量数据存储和作用域

引用类型:包括数组(array)和结构体(struct),由于这类变量比较复杂,占用存储空间大,我们在使用时必须要声明数据存储的位置。

Solidity数据存储位置有三类:storage,memory和calldata。不同存储位置的gas成本不同。storage类型的数据存在链上,类似计算机的硬盘,消耗gas多;memory和calldata类型的临时存在内存里,消耗gas少。大致用法:

storage:合约里的状态变量默认都是storage,存储在链上。
memory:函数里的参数和临时变量一般用memory,存储在内存中,不上链。
尤其是如果返回数据类型是变长的情况下,
必须加memory修饰,例如:string, bytes, array和自定义结构。
calldata:和memory类似,存储在内存中,不上链。
与memory的不同点在于calldata变量不能修改(immutable),
一般用于函数的参数。

我的感觉就是calldata是栈数据,虽然不知道为什么不可变,然后memory就类似于堆数据的可变数据

function fCalldata(uint[] calldata _x) public pure returns(uint[] calldata){//参数为calldata数组,不能被修改// _x[0] = 0 //这样修改会报错return(_x);
}
变量的作用域

合约里定义的变量就是所谓的状态变量,修改这些状态变量是昂贵的

contract Variables {uint public x = 1;uint public y;string public z;// 这种pure的计算是不上链的,无需给矿工缴纳gas feefunction bar() external pure returns(uint){uint xx = 1;uint yy = 3;uint zz = xx + yy;return(zz);
}
}

全局变量是全局范围工作的变量,都是solidity预留关键字。他们可以在函数内不声明直接使用:

function global() external view returns(address, uint, bytes memory){address sender = msg.sender;uint blockNum = block.number;bytes memory data = msg.data;return(sender, blockNum, data);
}

例子里,我们使用了3个常用的全局变量:msg.sender,block.number和msg.data,他们分别代表请求发起地址,当前区块高度,和请求数据。

以太单位

Solidity中不存在小数点,以0代替为小数点,来确保交易的精确度,并且防止精度的损失,利用以太单位可以避免误算的问题,方便程序员在合约中处理货币交易。
wei: 1
gwei: 1e9 = 1000000000
ether: 1e18 = 1000000000000000000

function weiUnit() external pure returns(uint) {assert(1 wei == 1e0);assert(1 wei == 1);return 1 wei;
}function gweiUnit() external pure returns(uint) {assert(1 gwei == 1e9);assert(1 gwei == 1000000000);return 1 gwei;
}function etherUnit() external pure returns(uint) {assert(1 ether == 1e18);assert(1 ether == 1000000000000000000);return 1 ether;
}

6引用类型 数组和结构体

这里不是很难,但是我不是很理解这个memory的array和正常的到底有啥子区别?

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
contract ArrayTypes {// 固定长度 Arrayuint[8] array1;bytes1[5] array2;address[100] array3;// 可变长度 Arrayuint[] array4;bytes1[] array5;address[] array6;bytes array7;// 初始化可变长度 Arrayuint[] array8 = new uint[](5);bytes array9 = new bytes(9);//  给可变长度数组赋值function initArray() external pure returns(uint[] memory){uint[] memory x = new uint[](3);x[0] = 1;x[1] = 3;x[2] = 4;return(x);}  function arrayPush() public returns(uint[] memory){uint[2] memory a = [uint(1),2];array4 = a;array4.push(3);return array4;}
}contract StructTypes {// 结构体 Structstruct Student{uint256 id;uint256 score; }Student student; // 初始一个student结构体//  给结构体赋值// 方法1:在函数中创建一个storage的struct引用function initStudent1() external{Student storage _student = student; // assign a copy of student_student.id = 11;_student.score = 100;}// 方法2:直接引用状态变量的structfunction initStudent2() external{student.id = 1;student.score = 80;}// 方法3:构造函数式function initStudent3() external {student = Student(3, 90);}// 方法4:key valuefunction initStudent4() external {student = Student({id: 4, score: 60});}
}contract EnumTypes {// 将uint 0, 1, 2表示为Buy, Hold, Sellenum ActionSet { Buy, Hold, Sell }// 创建enum变量 actionActionSet action = ActionSet.Buy;// enum可以和uint显式的转换function enumToUint() external view returns(uint){return uint(action);}
}

7映射 hash

contract hashmap_test{// 创建了一个hashmap并定义了key和value的类型mapping (uint => address) public idToaddress;mapping (address => address) public swappair;// 规则1. _KeyType不能是自定义的 下面这个例子会报错// 我们定义一个结构体 Struct// struct Student{//    uint256 id;//    uint256 score; //}// mapping(Struct => uint) public testVar;// 调用一个写这个hashmap的函数function writemao(uint key,address value) public {idToaddress[key] = value;}
}

8初始化变量值

bool public _bool; // false
string public _string; // ""
int public _int; // 0
uint public _uint; // 0
address public _address; // 0x0000000000000000000000000000000000000000enum ActionSet { Buy, Hold, Sell}
ActionSet public _enum; // 第1个内容Buy的索引0function fi() internal{} // internal空白函数
function fe() external{} // external空白函数 // delete操作符
bool public _bool2 = true; 
function d() external {delete _bool2; // delete 会让_bool2变为默认值,false
}

9常数constant和immutable

只有数值变量可以声明constant和immutable;string和bytes可以声明为constant,但不能为immutable。

// constant变量必须在声明的时候初始化,之后不能改变
uint256 constant CONSTANT_NUM = 10;
string constant CONSTANT_STRING = "0xAA";
bytes constant CONSTANT_BYTES = "WTF";
address constant CONSTANT_ADDRESS = 0x0000000000000000000000000000000000000000;// immutable变量可以在constructor里初始化,之后不能改变
uint256 public immutable IMMUTABLE_NUM = 9999999999;
// 在`Solidity v8.0.21`以后,下列变量数值暂为初始值
address public immutable IMMUTABLE_ADDRESS; 
uint256 public immutable IMMUTABLE_BLOCK;
uint256 public immutable IMMUTABLE_TEST;

构造函数和修饰器
构造函数每个合约只能定义一个,我认为这个就类似rust里的懒加载lazy_Static,用来初始化一些合约的参数。

address owner; // 定义owner变量// 构造函数
constructor(address initialOwner) {owner = initialOwner; // 在部署合约的时候,将owner设置为传入的initialOwner地址
}

看看完全的代码

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;contract Owner {address public owner; // 定义owner变量// 构造函数constructor(address initialOwner) {owner = initialOwner; // 在部署合约的时候,将owner设置为传入的initialOwner地址}// 定义modifiermodifier onlyOwner {require(msg.sender == owner); // 检查调用者是否为owner地址_; // 如果是的话,继续运行函数主体;否则报错并revert交易}// 定义一个带onlyOwner修饰符的函数function changeOwner(address _newOwner) external onlyOwner{owner = _newOwner; // 只有owner地址运行这个函数,并改变owner}
}

10控制流

contract control_stream{function ifelsetest(uint256 _number) public pure returns (bool){if(_number == 1){return(true);}else {return(false);}}function looptest() public pure returns(uint){uint num = 1;for(uint i = 0;i < 10;i++){num +=1;}return(num);}function whileloop() public pure returns(uint){uint num = 0;while(num < 10){num += 1;}return num;}
}

练习:尝试写排序

目前是失败的 后续会重新写一下这个问题

contract sort{uint[5] public arr = [7,8,9,2,1];function popsort() public{for (uint i = 0;i<arr.length-1;i++){for(uint j = i;j<arr.length-1;j++){// 如果第一个大于第二个if (arr[j] > arr[j+1]){swap(j,j+1);}}}}function swap(uint index1,uint index2) internal{uint temp = arr[index1];arr[index1] = arr[index2];arr[index2] = temp;}function get_num() public returns(uint){return(arr[4]);}
}

11构造函数和修饰器

contract Owner {address public owner; // 定义owner变量// 构造函数constructor(address initialOwner) {owner = initialOwner; // 在部署合约的时候,将owner设置为传入的initialOwner地址}// 定义modifiermodifier onlyOwner {require(msg.sender == owner); // 检查调用者是否为owner地址_; // 如果是的话,继续运行函数主体;否则报错并revert交易}// 定义一个带onlyOwner修饰符的函数function changeOwner(address _newOwner) external onlyOwner{owner = _newOwner; // 只有owner地址运行这个函数,并改变owner}
}

12事件

Solidity中的事件(event)是EVM上日志的抽象,它具有两个特点:

响应:应用程序(ethers.js)可以通过RPC接口订阅和监听这些事件,并在前端做响应。
经济:事件是EVM上比较经济的存储数据的方式,每个大概消耗2,000 gas;相比之下,链上存储一个新变量至少需要20,000 gas。

13继承

简单的继承

contract Yeye{event log(string msg);function hip() public virtual {emit log("yeye");}function pop() public virtual {emit log("yeye");}function yeye() public virtual {emit log("yeye");}
}contract baba is Yeye{function hip() public virtual override {emit log("baba");}function pop() public virtual override {emit log("baba");}
}

修饰器也是可以继承的

14抽象合约和接口

首先了解一下抽象合约,如果一个合约中的某个函数缺少主体,就把这个合约定义为abstract,否则会产生编译错误。

abstract constract insert_test{function pop_sort(uint[] memory a) public pure virtual returns(uint[] memory);
}
// 上述的合约中的函数代码是;结尾的

接口的定义:

1.不能包含状态变量
2.不能包含构造函数
3.不能继承除接口外的其他合约
4.所有的函数都应该是external的
5.非抽象合约继承接口必须实现接口定义的所有功能

我们以ERC721接口合约IERC721为例,它定义了3个event和9个function,所有ERC721标准的NFT都实现了这些函数。我们可以看到,接口和常规合约的区别在于每个函数都以;代替函数体{ }结尾。

interface IERC721 is IERC165 {event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);event ApprovalForAll(address indexed owner, address indexed operator, bool approved);function balanceOf(address owner) external view returns (uint256 balance);function ownerOf(uint256 tokenId) external view returns (address owner);function safeTransferFrom(address from, address to, uint256 tokenId) external;function transferFrom(address from, address to, uint256 tokenId) external;function approve(address to, uint256 tokenId) external;function getApproved(uint256 tokenId) external view returns (address operator);function setApprovalForAll(address operator, bool _approved) external;function isApprovedForAll(address owner, address operator) external view returns (bool);function safeTransferFrom( address from, address to, uint256 tokenId, bytes calldata data) external;
}

15异常

本章的主要目的时了解三种抛出异常的方法,并比较对应的gas fee消耗:

error 
require
assert

异常的主要目的是为了debug
写到这里发现后期的一些内容总是无法督促自己把样例代码写完,现在改为按章节去更新。并新开一个专栏。

筑基期

首先看一个存储合约示例

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;contract SimpleStorage {uint storedData;function set(uint x) public {storedData = x;}function get() public view returns (uint) {return storedData;}
}

第一行告诉您,源代码是根据GPL3.0版本授权的。 在发布源代码是默认的情况下,机器可读的许可证说明是很重要的。
下一行指定源代码是为Solidity 0.4.16版本编写的,或该语言的较新版本,直到但不包括0.9.0版本。 这是为了确保合约不能被新的(有重大改变的)编译器版本编译,在那里它可能会有不同的表现。 Pragmas 是编译器关于如何处理源代码的常用指令 (例如, pragma once )。
这两行类似于配置,contract就类似于结构体或者类了。
我们在类中定义了一个unit数据,并设置了set和get函数来获取这个值。我一度很好奇这个view,同时这个public 放置的位置也很让我疑惑。搜索后发现

public 关键字用于声明智能合约的状态变量。当一个状态变量被声明为 public 时,
它会自动生成一个同名的getter函数,允许外部访问这个状态变量的值。
此外,public 状态变量的值会被存储在区块链上,并且可以通过合约的接口进行访问。
view 关键字用于修饰智能合约的函数。一个被标记为 view 的函数表示这个函数不会修改区块链上的任何状态,
它只用于读取数据。调用这样的函数不会产生交易,也就是说,
它不会消耗gas(以太坊网络的费用),因为它不会引起状态变化。这使得 view 函数非常适合用于查询操作。

我目前把这个public理解为普遍的public,因为我不知道他在智能合约里起到一个什么样的作用。

该合约能完成的事情并不多(由于以太坊构建的基础架构的原因), 它能允许任何人在合约中存储一个单独的数字,并且这个数字可以被世界上任何人访问, 且没有可行的办法阻止您发布这个数字。 当然,任何人都可以再次调用 set ,传入不同的值,覆盖您的数字, 但是这个数字仍会被存储在区块链的历史记录中。
神奇之处来了,修改的历史居然会被保存。

接下来我们继续看下一个例子:

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.4;contract Coin {// 关键字 "public" 使变量可以从其他合约中访问。address public minter;mapping(address => uint) public balances;// 事件允许客户端对您声明的特定合约变化做出反应event Sent(address from, address to, uint amount);// 构造函数代码只有在合约创建时运行constructor() {minter = msg.sender;}// 向一个地址发送一定数量的新创建的代币// 但只能由合约创建者调用function mint(address receiver, uint amount) public {require(msg.sender == minter);balances[receiver] += amount;}// 错误类型变量允许您提供关于操作失败原因的信息。// 它们会返回给函数的调用者。error InsufficientBalance(uint requested, uint available);// 从任何调用者那里发送一定数量的代币到一个地址function send(address receiver, uint amount) public {if (amount > balances[msg.sender])revert InsufficientBalance({requested: amount,available: balances[msg.sender]});balances[msg.sender] -= amount;balances[receiver] += amount;emit Sent(msg.sender, receiver, amount);}
}

address public minter; 这一行声明了一个可以被公开访问的 address 类型的状态变量。 address 类型是一个160位的值,且不允许任何算数操作。 这种类型适合存储合约地址或 外部账户 的密钥对。


http://www.ppmy.cn/ops/109771.html

相关文章

Vue Echarts报错Initialize failed: invalid dom解决方法

此问题是图表初始化时 找不到dom&#xff0c;以下是解决方法 1、不要用created&#xff08;用mounted&#xff09;&#xff0c;created这时候还只是创建了实例&#xff0c;但模板还没挂载完成&#xff1b; created&#xff1a; 在模板渲染成 html 前调用&#xff0c;通常初始…

【GeekBand】C++设计模式笔记1_介绍

课程目标 理解松耦合设计思想掌握面向对象设计原则掌握重构技法改善设计掌握GOF核心设计模式 什么是设计模式 目标&#xff1a;复用&#xff0c;以不变应万变 GOF设计模式 从面向对象谈起 深入理解面向对象 向下&#xff1a;深入理解三大面向对象机制 封装&#xff1a;隐藏…

百度Apollo打通与ROS的通信,扩展自动驾驶系统生态

技术文档&#xff5c;打通与ROS的通信&#xff0c;扩展自动驾驶系统生态_Apollo开发者社区 (baidu.com)

细说STM32F407通用定时器的基础知识

目录 一、通用定时器功能概述 二、细说2通道定时器的功能 1.时钟信号和触发控制器 2.时基单元工作原理 (1)计数寄存器(CNT) (2)预分频寄存器(PSC) (3)自动重载寄存器(ARR) (4)时基单元的控制位 3.捕获/比较通道 三、生成PWM波 1.生成PWM波的原理 2.与生成PWM波相关的HA…

结构性慢增长中,白色家电行业的“红利牌”还能撑多久?

正如“华尔街教父”本杰明格雷厄姆所言&#xff0c;“分红回报是公司成长中最为可靠的部分”。 近几年整个全球都在目睹一系列的不确定性因素频频闪现&#xff0c;加上国内经济的发展进入新常态阶段&#xff0c;“高确定性”俨然成为市场更普遍的追求。 红利风格也由此扶摇直…

Chainlit集成Langchain并使用通义千问实现和数据库交互的网页对话应用(text2sql)

LangChain 简介 LangChain 是一个开源框架&#xff0c;设计用于开发和部署与语言模型&#xff08;如大型语言模型LLM&#xff09;交互的应用程序。它提供了一种简便的方法来构建基于自然语言处理&#xff08;NLP&#xff09;的系统&#xff0c;这些系统可以执行各种任务&#…

VMware Fusion 虚拟机Mac版 安装CentOS系统教程

Mac分享吧 文章目录 CentOS安装完成&#xff0c;软件打开效果一、Mac中使用虚拟机安装CentOS系统1️⃣&#xff1a;下载镜像2️⃣&#xff1a;创建虚拟机3️⃣&#xff1a;设置虚拟机4️⃣&#xff1a;安装虚拟机5️⃣&#xff1a;设置成从磁盘启动 安装完成&#xff01;&…

【QT】基础入门学习

文章目录 浅析Qt应用程序的主函数使用qDebug()函数常用快捷键Qt 编码风格信号槽连接模型实现方案 信号和槽的工作机制Qt对象树机制 浅析Qt应用程序的主函数 #include "mywindow.h"#include <QApplication>// 程序的入口 int main(int argc, char *argv[]) {//…