教你用go语言实现比特币交易功能(Transaction)

比特币交易

交易(transaction)是比特币的核心所在,而区块链唯一的目的,也正是为了能够安全可靠地存储交易。在区块链中,交易一旦被创建,就没有任何人能够再去修改或是删除它。
对于每一笔新的交易,它的输入会引用(reference)之前一笔交易的输出(这里有个例外,coinbase 交易),引用就是花费的意思。所谓引用之前的一个输出,也就是将之前的一个输出包含在另一笔交易的输入当中,就是花费之前的交易输出。交易的输出,就是币实际存储的地方。下面的图示阐释了交易之间的互相关联:

注意:

有一些输出并没有被关联到某个输入上

一笔交易的输入可以引用之前多笔交易的输出

一个输入必须引用一个输出

贯穿本文,我们将会使用像“钱(money)”,“币(coin)”,“花费(spend)”,“发送(send)”,“账户(account)” 等等这样的词。但是在比特币中,其实并不存在这样的概念。交易仅仅是通过一个脚本(script)来锁定(lock)一些值(value),而这些值只可以被锁定它们的人解锁(unlock)。

每一笔比特币交易都会创造输出,输出都会被区块链记录下来。给某个人发送比特币,实际上意味着创造新的 UTXO 并注册到那个人的地址,可以为他所用。
交易的主函数:

func (cli *CLI) send(from, to string, amount int, nodeID string, mineNow bool) {
    if !ValidateAddress(from) {
        log.Panic("ERROR: Sender address is not valid")
    }
    if !ValidateAddress(to) {
        log.Panic("ERROR: Recipient address is not valid")
    }
    bc := NewBlockchain(nodeID)    //获取区块链实例
    UTXOSet := UTXOSet{bc}    //创建UTXO集
    defer bc.Db.Close()
    wallets, err := NewWallets(nodeID)
    if err != nil {
        log.Panic(err)
    }
    wallet := wallets.GetWallet(from)
    tx := NewUTXOTransaction(&wallet, to, amount, &UTXOSet)
    if mineNow {
        cbTx := NewCoinbaseTX(from, "")
        txs := []*Transaction{cbTx, tx}
        newBlock := bc.MineBlock(txs)
        UTXOSet.Update(newBlock)
    } else {
        sendTx(knownNodes[0], tx)
    }

    fmt.Println("Success!")
}

我们从头分析整个交易过程,首先利用ValidateAddress()方法判断输入的地址是否为有效的比特币地址,然后从我们的blotDB数据库中获取blockchain实例(我们利用一个数据库实现区块链数据的存储,这里读者可以忽略),其中读取数据库的代码如下

func NewBlockchain(nodeID string) *Blockchain {
    dbFile := fmt.Sprintf(dbFile, nodeID)
    if dbExists(dbFile) == false {
        fmt.Println("No existing blockchain found. Create one first.")
        os.Exit(1)
    }
    var tip []byte
    db, err := bolt.Open(dbFile, 0600, nil)    //打开数据库
    if err != nil {
        log.Panic(err)
    }
    err = db.Update(func(tx *bolt.Tx) error {
        b := tx.Bucket([]byte(blocksBucket))
        tip = b.Get([]byte("l"))  //读取最新的区块链
        return nil
    })
    if err != nil {
        log.Panic(err)
    }
    bc := Blockchain{tip, db}
    return &bc
}

其中我们的区块链的基本原型为

type Blockchain struct {
    tip []byte
    Db  *bolt.DB
}
type Block struct {
    Timestamp     int64
    Transactions  []*Transaction
    PrevBlockHash []byte
    Hash          []byte
    Nonce         int
    Height        int
}

获取完成区块链实例后,我们创建出一个utxo集合,其数据结构为

type UTXOSet struct {
    Blockchain *Blockchain
}

然后我们从钱包文件中获取我们的钱包集合(wallets),接着调用我们的转账函数。

func NewUTXOTransaction(wallet *Wallet, to string, amount int, UTXOSet *UTXOSet) *Transaction {
    var inputs []TXInput
    var outputs []TXOutput
    pubKeyHash := HashPubKey(wallet.PublicKey)
    acc, validOutputs := UTXOSet.FindSpendableOutputs(pubKeyHash, amount)    //找到能够使用的输出
    if acc < amount {    //如果能够使用的输出小于目标值,则返回错误
        log.Panic("ERROR: Not enough funds")
    }
    // Build a list of inputs
    for txid, outs := range validOutputs {
        txID, err := hex.DecodeString(txid)
        if err != nil {
            log.Panic(err)
        }
        for _, out := range outs {
            input := TXInput{txID, out, nil, wallet.PublicKey}
            inputs = append(inputs, input)
        }
    }
    // Build a list of outputs
    from := fmt.Sprintf("%s", wallet.GetAddress())
    outputs = append(outputs, *NewTXOutput(amount, to))    //创建新的交易输出
    if acc > amount {
        outputs = append(outputs, *NewTXOutput(acc-amount, from)) // a change    //找零输出
    }
    tx := Transaction{nil, inputs, outputs}
    tx.ID = tx.Hash()    //创建一笔交易
    UTXOSet.Blockchain.SignTransaction(&tx, wallet.PrivateKey)       //对交易签名
    return &tx
}

对于一笔交易来说,其数据结构为

type Transaction struct {
    ID   []byte
    Vin  []TXInput
    Vout []TXOutput
}
type TXInput struct {
    Txid      []byte
    Vout      int
    Signature []byte
    PubKey    []byte
}
type TXOutput struct {
    Value      int
    PubKeyHash []byte
}
type UTXOSet struct {
    Blockchain *Blockchain
}

一笔交易来说,输出主要包含两部分: 一定量的比特币(Value), 一个锁定脚本(ScriptPubKey),要花这笔钱,必须要解锁该脚本。一个输入引用了之前交易的一个输出:Txid 存储的是之前交易的 ID,Vout 存储的是该输出在那笔交易中所有输出的索引(因为一笔交易可能有多个输出,需要有信息指明是具体的哪一个)Signature是签名,而Pubkey是公钥,两者保证了用户无法花费属于其他人的币。

func HashPubKey(pubKey []byte) []byte {  // RIPEMD160(SHA256(PubKey))
    publicSHA256 := sha256.Sum256(pubKey)
    RIPEMD160Hasher := ripemd160.New()
    _, err := RIPEMD160Hasher.Write(publicSHA256[:])
    if err != nil {
        log.Panic(err)
    }
    publicRIPEMD160 := RIPEMD160Hasher.Sum(nil)
    return publicRIPEMD160
}
func (u UTXOSet) FindSpendableOutputs(pubkeyHash []byte, amount int) (int, map[string][]int) {
    unspentOutputs := make(map[string][]int)     //为输出开辟一块内存空间
    accumulated := 0
    db := u.Blockchain.db             //获取存取区块链的数据库
    err := db.View(func(tx *bolt.Tx) error {               //读取数据库
        b := tx.Bucket([]byte(utxoBucket))
        c := b.Cursor()
        for k, v := c.First(); k != nil; k, v = c.Next() {               //遍历数据库
            txID := hex.EncodeToString(k)
            outs := DeserializeOutputs(v)
            for outIdx, out := range outs.Outputs {
                if out.IsLockedWithKey(pubkeyHash) && accumulated < amount {          //如果能够解锁输出,代表utxo集中的输出是的所有者是该公钥所对应的人
                    accumulated += out.Value     //累加值
                    unspentOutputs[txID] = append(unspentOutputs[txID], outIdx)     //加到数组中
                }
            }
        }

        return nil
    })
    if err != nil {
        log.Panic(err)
    }
    return accumulated, unspentOutputs
}
func (out *TXOutput) IsLockedWithKey(pubKeyHash []byte) bool {      //判断输出是否能够被某个公钥解锁
    return bytes.Compare(out.PubKeyHash, pubKeyHash) == 0
}
func NewTXOutput(value int, address string) *TXOutput {
    txo := &TXOutput{value, nil}    //注册一个输出
    txo.Lock([]byte(address))    //设置输出的pubhashkey
    return txo
}
func (out *TXOutput) Lock(address []byte) {
    pubKeyHash := Base58Decode(address)
    pubKeyHash = pubKeyHash[1 : len(pubKeyHash)-4]
    out.PubKeyHash = pubKeyHash
}

在创建新的输出时,我们必须找到所有的为花费的输出,并且确保他们有足够的价值(value),这就是FindSpendableOutputs 要做的事情,随后,对于每个找到的输出,会创建一个引用该输出的输入。接下来,我们创建两个输出:

  1. 一个由接收者地址锁定。这是给其他地址实际转移的币。
  2. 一个由发送者地址锁定。这是一个找零。只有当未花费输出超过新交易所需时产生。记住:输出是不可再分的。
func (bc *Blockchain) SignTransaction(tx *Transaction, privKey ecdsa.PrivateKey) {
    prevTXs := make(map[string]Transaction)
    for _, vin := range tx.Vin {
        prevTX, err := bc.FindTransaction(vin.Txid)
        if err != nil {
            log.Panic(err)
        }
        prevTXs[hex.EncodeToString(prevTX.ID)] = prevTX
    }
    tx.Sign(privKey, prevTXs)
}
func (tx *Transaction) Sign(privKey ecdsa.PrivateKey, prevTXs map[string]Transaction) {//方法接受一个私钥和之前一个交易的map
    if tx.IsCoinbase() {
        return
    }//判断是是否为发币交易,因为发币交易没有输入,故不用进行签名

    for _, vin := range tx.Vin {
        if prevTXs[hex.EncodeToString(vin.Txid)].ID == nil {
            log.Panic("ERROR: Previous transaction is not correct")
        }
    }

    txCopy := tx.TrimmedCopy()  //将会被签名的是修剪后的交易副本,而不是一个完整的交易

    for inID, vin := range txCopy.Vin {
        prevTx := prevTXs[hex.EncodeToString(vin.Txid)]
        txCopy.Vin[inID].Signature = nil
        txCopy.Vin[inID].PubKey = prevTx.Vout[vin.Vout].PubKeyHash
//迭代副本中的每一个输入,在每个输入中,Pubkey 被设置为所引用输出的PubKeyHash
/
        dataToSign := fmt.Sprintf("%x\n", txCopy)
        r, s, err := ecdsa.Sign(rand.Reader, &privKey, []byte(dataToSign))//我们通过private对txCopy进行签名将这串数字连接起来储存在signature中
        if err != nil {
            log.Panic(err)
        }
        signature := append(r.Bytes(), s.Bytes()...)
        tx.Vin[inID].Signature = signature
        txCopy.Vin[inID].PubKey = nil
    }
}

func (tx *Transaction) TrimmedCopy() Transaction {
    var inputs []TXInput
    var outputs []TXOutput

    for _, vin := range tx.Vin {//将输入的TXInput.Signature 和TXIput.PubKey设置为空
        inputs = append(inputs, TXInput{vin.Txid, vin.Vout, nil, nil})
    }

    for _, vout := range tx.Vout {
        outputs = append(outputs, TXOutput{vout.Value, vout.PubKeyHash})
    }
    txCopy := Transaction{tx.ID, inputs, outputs}
    return txCopy
}

交易必须被签名,因为这是保证发送方不会花费其他人的币的唯一方式,如果一个签名是无效的,那么这笔交易也会被认为是无效的,因为这笔交易无法被加到区块链中。考虑到交易解锁的是之前的输出,然后重新分配里面的价值,并锁定新的输出,那么必须要签名一下的数据

  • 存储在已经解锁输出的公钥哈希,他识别了一笔交易的发送方
  • 存储在新的锁定输出里面的公钥哈希,他识别了一笔交易的接收方
  • 新的输出值

因此,在比特币里,所签名的并不是一个交易,而是一个去除部分签名的输入的副本,输入里面存储了被引用输出的ScriptPubKey

如果现在进行过挖矿

   cbTx := NewCoinbaseTX(from, "")
        txs := []*Transaction{cbTx, tx}
        newBlock := bc.MineBlock(txs)
        UTXOSet.Update(newBlock)

func NewCoinbaseTX(to, data string) *Transaction {
    if data == "" {  //如果数据为空生成一个随机数据
        randData := make([]byte, 20)
        _, err := rand.Read(randData)
        if err != nil {
            log.Panic(err)
        }
        data = fmt.Sprintf("%x", randData)
    }//生成一笔挖矿交易
    txin := TXInput{[]byte{}, -1, nil, []byte(data)}
    txout := NewTXOutput(subsidy, to)
    tx := Transaction{nil, []TXInput{txin}, []TXOutput{*txout}}
    tx.ID = tx.Hash()
    return &tx
}

func (bc *Blockchain) MineBlock(transactions []*Transaction) *Block {   //开始挖矿
    var lastHash []byte
    var lastHeight int
    for _, tx := range transactions {
        // TODO: ignore transaction if it's not valid
        if bc.VerifyTransaction(tx) != true {
            log.Panic("ERROR: Invalid transaction")   //对打包在区块中的交易进行认证
        }
    }

    err := bc.db.View(func(tx *bolt.Tx) error {
        b := tx.Bucket([]byte(blocksBucket))
        lastHash = b.Get([]byte("l"))   //获取最新的一个块的hash值
        blockData := b.Get(lastHash)
        block := DeserializeBlock(blockData)  //将最新的一个块解序列
        lastHeight = block.Height
        return nil
    })
    if err != nil {
        log.Panic(err)
    }
    newBlock := NewBlock(transactions, lastHash, lastHeight+1)
    err = bc.db.Update(func(tx *bolt.Tx) error {    //更新区块链数据库
        b := tx.Bucket([]byte(blocksBucket))
        err := b.Put(newBlock.Hash, newBlock.Serialize())
        if err != nil {
            log.Panic(err)
        }
        err = b.Put([]byte("l"), newBlock.Hash)
        if err != nil {
            log.Panic(err)
        }
        bc.tip = newBlock.Hash
        return nil
    })
    if err != nil {
        log.Panic(err)
    }
    return newBlock
}
func (bc *Blockchain) VerifyTransaction(tx *Transaction) bool {
    if tx.IsCoinbase() {
        return true
    }
    prevTXs := make(map[string]Transaction)
    for _, vin := range tx.Vin {
        prevTX, err := bc.FindTransaction(vin.Txid)
        if err != nil {
            log.Panic(err)
        }
        prevTXs[hex.EncodeToString(prevTX.ID)] = prevTX
    }
    return tx.Verify(prevTXs)
}
func (tx *Transaction) Verify(prevTXs map[string]Transaction) bool {
    if tx.IsCoinbase() {   //判断是否为大笔交易
        return true
    }
    for _, vin := range tx.Vin {
        if prevTXs[hex.EncodeToString(vin.Txid)].ID == nil {
            log.Panic("ERROR: Previous transaction is not correct")   //判断输入地址的有效性
        }
    }
    txCopy := tx.TrimmedCopy()    //创建一个裁剪版本的交易副本
    curve := elliptic.P256()    //我们需要相同区块用于生成密钥对
    for inID, vin := range tx.Vin {
        prevTx := prevTXs[hex.EncodeToString(vin.Txid)]
        txCopy.Vin[inID].Signature = nil
        txCopy.Vin[inID].PubKey = prevTx.Vout[vin.Vout].PubKeyHash
        r := big.Int{}
        s := big.Int{}
        sigLen := len(vin.Signature)
        r.SetBytes(vin.Signature[:(sigLen / 2)])
        s.SetBytes(vin.Signature[(sigLen / 2):])
        x := big.Int{}
        y := big.Int{}
        keyLen := len(vin.PubKey)
        x.SetBytes(vin.PubKey[:(keyLen / 2)])
        y.SetBytes(vin.PubKey[(keyLen / 2):])
//这里我们解包存储在 TXInput.Signature 和 TXInput.PubKey 中的值,因为一个签名就是一对数字,一个公钥就是一对坐标。我们之前为了存储将它们连接在一起,现在我们需要对它们进行解包在 crypto/ecdsa 函数中使用
        dataToVerify := fmt.Sprintf("%x\n", txCopy)
        rawPubKey := ecdsa.PublicKey{curve, &x, &y}
        if ecdsa.Verify(&rawPubKey, []byte(dataToVerify), &r, &s) == false {  //验证
            return false
        }
        txCopy.Vin[inID].PubKey = nil
    }

    return true
}
func NewBlock(transactions []*Transaction, prevBlockHash []byte, height int) *Block {//产生一个新的块
    block := &Block{time.Now().Unix(), transactions, prevBlockHash, []byte{}, 0, height}//定义数据结构
    pow := NewProofOfWork(block)    //定义工作量证明的数据结构
    nonce, hash := pow.Run()    //挖矿
    block.Hash = hash[:]
    block.Nonce = nonce
    return block
}
func (pow *ProofOfWork) Run() (int, []byte) {
    var hashInt big.Int
    var hash [32]byte
    nonce := 0
    fmt.Printf("Mining a new block")
    for nonce < maxNonce {
        data := pow.prepareData(nonce)
        hash = sha256.Sum256(data)
        fmt.Printf("\r%x", hash)
        hashInt.SetBytes(hash[:])
        if hashInt.Cmp(pow.target) == -1 {
            break
        } else {
            nonce++
        }
    }
    fmt.Print("\n\n")

    return nonce, hash[:]
}
func (pow *ProofOfWork) prepareData(nonce int) []byte {
    data := bytes.Join(
        [][]byte{
            pow.block.PrevBlockHash,
            pow.block.HashTransactions(),
            IntToHex(pow.block.Timestamp),
            IntToHex(int64(targetBits)),
            IntToHex(int64(nonce)),
        },
        []byte{},
    )

    return data
}
func (u UTXOSet) Update(block *Block) {
    db := u.Blockchain.db
    err := db.Update(func(tx *bolt.Tx) error {
        b := tx.Bucket([]byte(utxoBucket))
        for _, tx := range block.Transactions {
            if tx.IsCoinbase() == false {
                for _, vin := range tx.Vin {
                    updatedOuts := TXOutputs{}
                    outsBytes := b.Get(vin.Txid)
                    outs := DeserializeOutputs(outsBytes)

                    for outIdx, out := range outs.Outputs {
                        if outIdx != vin.Vout {
                            updatedOuts.Outputs = append(updatedOuts.Outputs, out)
                        }
                    }

                    if len(updatedOuts.Outputs) == 0 {
                        err := b.Delete(vin.Txid)
                        if err != nil {
                            log.Panic(err)
                        }
                    } else {
                        err := b.Put(vin.Txid, updatedOuts.Serialize())
                        if err != nil {
                            log.Panic(err)
                        }
                    }

                }
            }
            newOutputs := TXOutputs{}
            for _, out := range tx.Vout {
                newOutputs.Outputs = append(newOutputs.Outputs, out)
            }
            err := b.Put(tx.ID, newOutputs.Serialize())
            if err != nil {
                log.Panic(err)
            }
        }

        return nil
    })
    if err != nil {
        log.Panic(err)
    }
}

参考

https://jeiwan.cc/

到此这篇关于利用go语言实现比特币交易(Transaction)的文章就介绍到这了,更多相关go语言比特币交易内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • go语言实现简易比特币系统钱包的原理解析

    钱包基础概念 广义上,钱包是一个应用程序,为用户提供交互界面.钱包控制用户访问权限.管理比特比地址及秘钥.跟踪余额.创建交易和签名交易 狭义上,即从程序员角度来看,"钱包"是指用于存储和管理用户秘钥的数据结构 钱包是私钥的容器,一般是通过结构化文件或简单数据库来实现的 钱包中并不包含比特币.比特币是被记录在比特币网络的区块链中,用户通过钱包中的密钥签名交易,从而控制网络中的比特币,在某种意义上,比特币钱包就是密钥链 钱包结构体 type Wallet struct { //私钥 Pri

  • go语言实现简易比特币系统之交易签名及校验功能

    介绍 签名的输入: 待签名的交易数据,包括输入和输出 引用的UTXO信息 私钥 签名的输出: 数字数字签名 公钥 签名的目的 证明交易所引用的UTXO的确属于付款人 证明交易的所有数据的确是付款人提供的,且未被修改过 签名中需要的数据 UTXO中的PubKeyHash,这描述了付款人 新生成UTXO中的PubKeyHash,这描述了收款人 由于每一笔交易都可能引用多个UTXO,因为多个UTXO可能存在于多条交易中.所以我们需要遍历所以的引用交易,并对他们逐个签名 签名过程 用解锁脚本解锁对应的U

  • 教你用go语言实现比特币交易功能(Transaction)

    比特币交易 交易(transaction)是比特币的核心所在,而区块链唯一的目的,也正是为了能够安全可靠地存储交易.在区块链中,交易一旦被创建,就没有任何人能够再去修改或是删除它. 对于每一笔新的交易,它的输入会引用(reference)之前一笔交易的输出(这里有个例外,coinbase 交易),引用就是花费的意思.所谓引用之前的一个输出,也就是将之前的一个输出包含在另一笔交易的输入当中,就是花费之前的交易输出.交易的输出,就是币实际存储的地方.下面的图示阐释了交易之间的互相关联: 注意: 有一

  • 手把手教你导入Go语言第三方库

    目录 环境 环境变量 实践导包 最好的学习方式就是实践. 我们通过导入gin包来深入学习. 环境 go 1.13.5 goland 2019.3.1 manjaro-gnome3.34.2 环境变量 首先回想一下,我们安装goland的时候,设置了几个比较重要的环境变量,也就是GOROOT和GOPATH. GOROOT比较容易理解.也就是我们go语言的安装目录,标准库什么的就放在里面的.在linux中的话,默认安装到/usr/lib/go中,我们创建项目时选择的SDK也就是这个. GOPATH可

  • 超详细,教你用python语言实现QQ机器人制作教程

    目录 前期准备 1.机器人框架的下载和配置 2.python的配置和安装 具体实现 1.发送信息 (1)比如发送私聊信息(QQ用接收者的): (2)发送群聊信息(群号自己用一个): (3)当然不止发送文本信息 2.获取群成员列表 3.接收上报的事件 4.实现简单的自动回复 前期准备 1.机器人框架的下载和配置 首先需要一个qq机器人框架,我使用的是基于mirai 以及 MiraiGo 开发的go-cqhttp(里面有开发文档). 框架下载地址 Windows下32位文件为 go-cqhttp-v

  • go语言文件正则表达式搜索功能示例

    本文实例讲述了go语言文件正则表达式搜索功能.分享给大家供大家参考,具体如下: 复制代码 代码如下: package main import (     "fmt"     "os"     "path/filepath"     "regexp" ) func main() {     // 命令行参数     args := os.Args     // 检查参数     if len(args) == 1 {      

  • C语言实现简单计算器功能(1)

    本文为大家分享了C语言实现简单计算器功能的具体方法,供大家参考,具体内容如下 这几天一直上控制语句,在学完if.switch和循环语句后,为了巩固所学知识点,想给学生出一道简单的计算器程序.题目如下: 通过所学的知识(选择语句.循环语句)完成一个简单的计算器编程,要求不许抄袭,只能用自己所学的知识点,不可引用网上自己的不懂的程序. 要求的格式如下图: 要求以这样的一种格式进行输出,并能通过选择完成不同的功能. 如:选择1,完成整数相加功能,算完后又能回到该页面. 经过分析,我简单的写了一段代码,

  • C语言实现简单计算器功能(2)

    本文接着上一篇C语言实现简单计算器功能(1)继续学习. 之前用switch语句完成了一个简单计算器的功能,下面我给大家用if语句和while循环语句再实现一个简单计算器的编程. 要求: 实现简单的计算器,具体代码如下: #include <stdio.h> // 头文件 void main() { char a = 0; // 变量声明并初始化 int x, y; // 变量声明 int result = 0; char b = 37; while(1) // whiel循环实现反复在计算器系

  • C语言实现简单通讯录功能

    本文实例为大家分享了C语言实现简单通讯录功能的具体代码,供大家参考,具体内容如下 1.存放联系人信息 2.信息:名字+年龄+性别+电话+住址 3.增加联系人 4.删除联系人 5.查找联系人 6.修改联系人 7.排序 test.c源文件代码如下: #define _CRT_SECURE_NO_WARNINGS 1 #include "contact.h" void menu() { printf("#####################################\n&

  • C语言实现推箱子功能汇总

    本文实例为大家分享了C语言实现推箱子功能的具体代码,供大家参考,具体内容如下 前言: 先说说我写推箱子小游戏的过程. 第一版:没有图形化界面,不能选关.只有推箱子的最基础功能. 第二版:增加图形化界面,但是不能选择关卡. 最终版:增加选择关卡功能.播放/关闭音乐功能.退出游戏功能,捕获鼠标功能. 首先看一下最终版效果图 功能点分析: 打开/关闭音乐和退出游戏比较简单. 打开音乐所需调用的头文件 #include <graphics.h> //调用easyx图形库时所需增加的头文件 #inclu

  • C语言实现简易计算器功能

    本文实例为大家分享了C语言实现简易计算器功能的具体代码,供大家参考,具体内容如下 表达式求值项目(多功能计算器).该项目主要包含了10个模块,10项功能: 1:加法运算:主要进行两个数的加法运算,可进行计算整数和浮点数: 2:减法运算:主要进行两个数的减法运算,可进行计算整数和浮点数: 3:乘法运算:主要进行两个数的乘法运算,可进行计算整数和浮点数: 4:除法运算:主要进行两个数的除法运算,可进行计算整数和浮点数:增加了报错功能,由于除数不可以为0,当用户输入的除数为0的时候,该项目会出提示红色

  • 教你利用R语言测试电脑的性能

    利用R语言测试电脑的性能如何 同事新配了一个电脑,想用R语言编写一个程序,看一下电脑性能如何,让我写个代码测试一下. 我能怎么样,我也不懂如何测试电脑啊,那就计算一下矩阵的运算吧.因为我理解的电脑运行性能就是矩阵计算了. 编写代码 rm(list=ls()) set.seed(123) # 设置矩阵的行数 n = 10000 # 生成一个矩阵 value = rnorm(n*n, 10,3) mat = matrix(value,n,n) # 测试电脑性能 system.time({ # 矩阵求

随机推荐