【C语言实战(78)】C语言进阶:筑牢数据安全防线,密码学实战探秘

目录

  • 一、密码学基础概念
    • [1.1 加密算法分类](#1.1 加密算法分类)
    • [1.2 安全编码场景](#1.2 安全编码场景)
  • 二、对称加密实战(AES-128)
    • [2.1 AES 基础](#2.1 AES 基础)
    • [2.2 开源库使用](#2.2 开源库使用)
      • [2.2.1 加密流程](#2.2.1 加密流程)
      • [2.2.2 解密流程](#2.2.2 解密流程)
      • [2.2.3 实战](#2.2.3 实战)
  • 三、哈希算法实战(SHA-256)
    • [3.1 SHA-256 基础](#3.1 SHA-256 基础)
    • [3.2 开源库使用](#3.2 开源库使用)
      • [3.2.1 盐值作用](#3.2.1 盐值作用)
      • [3.2.2 实战](#3.2.2 实战)
    • [3.3 安全编码最佳实践](#3.3 安全编码最佳实践)
  • 四、总结与展望

一、密码学基础概念

1.1 加密算法分类

  • 对称加密:对称加密算法是指在加密和解密过程中使用同一个密钥的加密方式。这种加密方式的特点是加密和解密速度快,效率高,适合对大量数据进行加密处理。然而,由于加密和解密使用相同的密钥,密钥的安全管理变得至关重要,一旦密钥泄露,加密的数据就不再安全。典型的对称加密算法如 AES(高级加密标准),它是一种分组加密算法,分组大小为 128 位,密钥大小可以是 128 位、192 位或 256 位。AES 有多种工作模式,如 ECB(电子密码本模式)和 CBC(密码块链接模式) ,其中 CBC 模式需要初始化向量 IV,其安全性高于 ECB 模式,因为 ECB 模式对于相同的明文块会加密出相同的密文块,容易被攻击者利用。
  • 非对称加密:非对称加密算法使用一对密钥,即公钥和私钥,公钥用于加密数据,私钥用于解密数据。这种加密方式的优势在于解决了密钥分发的问题,因为公钥可以公开传播,而私钥由接收方妥善保管。但是,非对称加密算法的加密和解密速度相对较慢,计算开销较大,通常适用于加密少量数据或用于数字签名、身份认证等场景。RSA 算法是目前应用最广泛的非对称加密算法之一,其安全性基于大数分解的困难性。例如,在网络通信中,服务器可以将自己的公钥发送给客户端,客户端使用公钥对敏感数据进行加密后传输给服务器,只有服务器拥有对应的私钥才能解密数据,从而保证了数据传输的安全性。
  • 哈希算法:哈希算法是一种将任意长度的数据转换为固定长度哈希值的算法,也被称为散列算法。哈希算法具有单向性,即从哈希值无法反向推导出原始数据,并且对于不同的输入数据,哈希值具有唯一性(抗碰撞性),即使原始数据只有微小的变化,也会导致生成的哈希值截然不同。哈希算法主要用于数据完整性校验,确保数据在传输或存储过程中未被篡改。例如,SHA-256(安全哈希算法 256 位),它会将任意长度的数据转换为 256 位的哈希值,常用于文件传输后的完整性验证,以及在存储用户密码时,存储密码的哈希值而非明文,增强密码的安全性。

1.2 安全编码场景

  • 用户密码存储:在应用程序中,用户密码的安全存储至关重要。如果直接存储用户密码的明文,一旦数据库泄露,用户密码将直接暴露,造成严重的安全风险。因此,通常采用哈希算法对用户密码进行处理,存储密码的哈希值。例如,在用户注册时,系统将用户输入的密码通过 SHA - 256 等哈希算法计算出哈希值,然后将哈希值存储在数据库中。当用户登录时,系统对用户输入的密码再次计算哈希值,并与数据库中存储的哈希值进行比对,如果两者相同,则验证用户密码正确。为了进一步增强安全性,还会为每个密码添加随机盐值(Salt),盐值是一个随机字符串,与密码拼接后再进行哈希计算,这样即使两个用户设置了相同的密码,由于盐值不同,生成的哈希值也会不同,有效防止了彩虹表攻击。
  • 敏感数据传输:在网络传输过程中,敏感数据如用户的个人信息、财务数据等需要进行加密,以防止数据被窃取或篡改。对称加密算法由于其高效性,常被用于敏感数据的加密传输。以 AES 算法为例,在数据传输前,发送方和接收方先协商好一个密钥(可以通过安全的方式交换,或者使用非对称加密来交换对称加密的密钥),发送方使用该密钥对敏感数据进行 AES 加密,然后将密文发送给接收方,接收方收到密文后,使用相同的密钥进行解密,从而获取原始的敏感数据。这样,即使数据在传输过程中被截获,攻击者在没有密钥的情况下也无法解密数据,保障了数据传输的安全性。

二、对称加密实战(AES-128)

2.1 AES 基础

AES(Advanced Encryption Standard)作为一种分组加密算法,在现代密码学领域应用广泛。其分组大小固定为 128 位,这意味着在加密过程中,会将明文按 128 位(16 字节)为一组进行处理 。密钥大小则有 128 位、192 位和 256 位三种选择,分别对应 AES-128、AES-192 和 AES-256 算法。密钥长度越长,破解的难度就越大,安全性也就越高,但同时加密和解密的计算开销也会相应增加。在实际应用中,AES-128 因其在安全性和计算效率之间取得了较好的平衡,被大量使用。

AES 拥有多种工作模式,其中 ECB(电子密码本模式)和 CBC(密码块链接模式)较为常见。ECB 模式是将明文分组后,每个分组独立进行加密,相同的明文分组会产生相同的密文分组。这种模式的优点是简单直观,易于实现,并且有利于并行计算。然而,它的缺点也很明显,由于相同明文块加密结果相同,容易被攻击者利用统计分析的方法破解,无法隐藏明文的模式,因此在安全性要求较高的场景中不推荐使用。

CBC 模式则引入了初始化向量 IV(Initialization Vector),在加密第一个明文分组时,先将其与 IV 进行异或操作,然后再进行加密;后续的明文分组则与前一个密文分组进行异或后再加密。这样,即使是相同的明文分组,由于其前面的分组密文不同(或者第一个分组与不同的 IV 异或),加密后的密文也会不同,大大增强了加密的安全性,适合传输长度较长的报文,是 SSL、IPSec 等协议的标准加密模式 。不过,CBC 模式由于需要依赖前一个分组的处理结果,不利于并行计算,并且如果在传输过程中某个密文分组出现错误,会影响到后续分组的解密,存在误差传递的问题。

2.2 开源库使用

在 C 语言开发中,为了实现 AES-128 加密和解密功能,可以集成 tiny-AES-c 库。这是一个轻量级、跨平台的 AES 加密库,非常适合在资源受限的环境中使用,其代码量小,并且不依赖于其他复杂的第三方库,易于集成到项目中。

2.2.1 加密流程

  • 初始化密钥与 IV:首先,需要定义并初始化 AES 加密所需的密钥和 IV。密钥的长度为 128 位(16 字节),IV 的长度也为 16 字节,且 IV 必须是随机生成的,以确保每次加密的安全性。例如:
c 复制代码
#include "aes.h"

// 定义128位密钥
uint8_t key[AES_BLOCKLEN] = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c}; 
// 定义初始化向量IV
uint8_t iv[AES_BLOCKLEN] = {0}; 
  • 对数据进行 PKCS#7 填充:由于 AES 是分组加密算法,要求明文数据的长度必须是 16 字节的倍数。如果数据长度不足,就需要进行填充。PKCS#7 填充是一种常用的填充方式,它会在数据末尾填充若干个字节,每个字节的值等于需要填充的字节数。例如,如果数据长度为 13 字节,那么需要填充 3 个字节,每个字节的值都是 0x03。下面是一个简单的 PKCS#7 填充函数示例:
c 复制代码
// PKCS#7填充
void pkcs7_padding(uint8_t *data, size_t data_len, size_t block_size) {
    size_t padding = block_size - (data_len % block_size);
    for (size_t i = 0; i < padding; i++) {
        data[data_len + i] = (uint8_t)padding;
    }
}
  • 调用 AES-CBC 加密函数:完成密钥、IV 初始化和数据填充后,即可调用 tiny-AES-c 库中的 AES-CBC 加密函数对数据进行加密。在调用函数时,需要传入待加密的数据、加密后的密文存储缓冲区、数据长度、密钥以及 IV 等参数。示例代码如下:
c 复制代码
aes_context ctx;
// 设置密钥并初始化上下文
aes_set_key(&ctx, key, sizeof(key));
// 执行加密操作
aes_cbc_encrypt(data, encrypted_data, data_len, &ctx, iv);

2.2.2 解密流程

  • 使用相同密钥与 IV:解密过程需要使用与加密时相同的密钥和 IV,以确保能够正确还原出原始明文。在实际应用中,密钥和 IV 的安全存储和传递至关重要,一旦泄露,加密的数据就会失去安全性。
c 复制代码
// 使用与加密相同的密钥和IV
uint8_t key[AES_BLOCKLEN] = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c}; 
uint8_t iv[AES_BLOCKLEN] = {0}; 
  • 调用 AES-CBC 解密函数:调用 tiny-AES-c 库中的 AES-CBC 解密函数,传入密文、解密后的明文存储缓冲区、密文长度、密钥以及 IV 等参数,即可进行解密操作。示例代码如下:
c 复制代码
aes_context ctx;
// 设置密钥并初始化上下文
aes_set_key(&ctx, key, sizeof(key));
// 执行解密操作
aes_cbc_decrypt(encrypted_data, decrypted_data, data_len, &ctx, iv);
  • 去除填充:解密后的明文可能包含填充数据,需要根据填充方式去除这些填充字节,以得到原始的明文数据。对于 PKCS#7 填充方式,去除填充的方法是读取最后一个字节的值,该值表示填充的字节数,然后将这些填充字节从明文末尾移除。示例代码如下:
c 复制代码
// 去除PKCS#7填充
void pkcs7_unpadding(uint8_t *data, size_t data_len) {
    size_t padding = (size_t)data[data_len - 1];
    if (padding > data_len) {
        return;
    }
    data_len -= padding;
    data[data_len] = '\0';
}

2.2.3 实战

以 "密码管理器" 为例,假设我们需要对其中存储的密码数据进行加密存储,以提高密码的安全性。在用户输入密码后,应用程序首先获取密码数据,然后按照上述 AES-128 加密流程对密码进行加密,将加密后的密文存储到数据库或其他存储介质中。示例代码如下:

c 复制代码
#include <stdio.h>
#include <string.h>
#include "aes.h"

// PKCS#7填充
void pkcs7_padding(uint8_t *data, size_t data_len, size_t block_size) {
    size_t padding = block_size - (data_len % block_size);
    for (size_t i = 0; i < padding; i++) {
        data[data_len + i] = (uint8_t)padding;
    }
}

// 去除PKCS#7填充
void pkcs7_unpadding(uint8_t *data, size_t data_len) {
    size_t padding = (size_t)data[data_len - 1];
    if (padding > data_len) {
        return;
    }
    data_len -= padding;
    data[data_len] = '\0';
}

int main() {
    // 定义128位密钥
    uint8_t key[AES_BLOCKLEN] = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c}; 
    // 定义初始化向量IV
    uint8_t iv[AES_BLOCKLEN] = {0}; 
    // 假设用户输入的密码
    char password[] = "user_password";
    size_t password_len = strlen(password);

    // 加密后的密文存储缓冲区
    uint8_t encrypted_password[1024];
    size_t encrypted_password_len;

    // 解密后的明文存储缓冲区
    uint8_t decrypted_password[1024];
    size_t decrypted_password_len;

    // 对密码进行PKCS#7填充
    pkcs7_padding((uint8_t *)password, password_len, AES_BLOCKLEN);
    password_len += AES_BLOCKLEN - (password_len % AES_BLOCKLEN);

    aes_context ctx;
    // 设置密钥并初始化上下文
    aes_set_key(&ctx, key, sizeof(key));
    // 执行加密操作
    aes_cbc_encrypt((uint8_t *)password, encrypted_password, password_len, &ctx, iv);
    encrypted_password_len = password_len;

    // 模拟存储加密后的密文,这里只是简单打印密文
    printf("加密后的密文: ");
    for (size_t i = 0; i < encrypted_password_len; i++) {
        printf("%02x ", encrypted_password[i]);
    }
    printf("\n");

    // 执行解密操作
    aes_cbc_decrypt(encrypted_password, decrypted_password, encrypted_password_len, &ctx, iv);
    decrypted_password_len = encrypted_password_len;

    // 去除填充
    pkcs7_unpadding(decrypted_password, decrypted_password_len);

    // 打印解密后的明文
    printf("解密后的明文: %s\n", decrypted_password);

    return 0;
}

当用户需要查询密码时,应用程序从存储介质中读取加密的密文,然后按照解密流程进行解密,将解密后的明文展示给用户(实际应用中可能不会直接展示明文,而是用于其他验证操作)。通过这种方式,有效保护了密码数据的安全,即使存储介质中的密文被泄露,没有正确的密钥和 IV,攻击者也无法获取用户的真实密码。

三、哈希算法实战(SHA-256)

3.1 SHA-256 基础

SHA-256(Secure Hash Algorithm 256-bit)是 SHA-2 系列哈希算法中的一种,在信息安全领域扮演着关键角色 。它能够将任意长度的数据通过复杂的数学运算,转换为固定长度的 256 位哈希值,这个哈希值通常以 32 字节的十六进制字符串形式呈现。

从原理上来说,SHA-256 首先会对输入数据进行填充,使其长度满足特定条件,即附加一个 "1" 位,再添加若干个 "0" 位,使得填充后的消息长度比 512 的倍数少 64 位,最后将用 64 位二进制表示的消息长度附加到消息末尾 。接着,算法会初始化八个 32 字节(256 位)的初始哈希值,然后将消息分割成 512 位(64 字节)的消息块,每个消息块进一步分为 16 个 32 位的字。在消息调度阶段,将 16 个 32 位的字扩展为 64 个 32 位的字,随后进入主循环,进行 64 轮压缩操作,每轮使用一个常量 K [i] 和一个消息字 w [i]。完成所有消息块的处理后,将计算结果加到当前哈希值,最终将 h0 到 h7 连接起来,形成 256 位(32 字节)的最终哈希值 。

在数据完整性校验方面,SHA-256 发挥着不可替代的作用。例如,在文件传输过程中,发送方可以计算文件的 SHA-256 哈希值,并将其与文件一同发送给接收方。接收方在收到文件后,重新计算文件的 SHA-256 哈希值,然后将计算得到的哈希值与发送方提供的哈希值进行比对。如果两个哈希值相同,就可以确认文件在传输过程中没有被篡改;反之,如果哈希值不同,那么文件很可能在传输过程中遭到了修改或破坏。

在用户密码存储场景中,SHA-256 同样是保障密码安全的重要手段。传统的做法是直接存储用户密码的明文,这种方式存在极大的安全风险,一旦数据库泄露,用户密码将全部暴露。而使用 SHA-256 对用户密码进行哈希处理后,存储的是密码的哈希值而非明文。当用户登录时,系统对用户输入的密码计算 SHA-256 哈希值,并与数据库中存储的哈希值进行比对,从而验证用户密码的正确性 。不过,为了进一步增强密码的安全性,通常还会引入盐值(Salt)的概念,后面会详细介绍。

3.2 开源库使用

在 C 语言中,为了实现 SHA-256 哈希值的计算,可以集成 cSHAKE 库。cSHAKE 库是一个功能强大且易于使用的开源库,它提供了丰富的接口和函数,方便开发者在项目中快速实现 SHA-256 相关的功能。

3.2.1 盐值作用

盐值(Salt)是一段随机生成的数据,通常为 16 字节或更长 ,在密码存储过程中起着至关重要的作用,主要用于增强密码的安全性,防止彩虹表攻击。

彩虹表是一种预先计算好的包含大量 "明文 - 哈希值" 映射关系的数据库。如果没有盐值,当多个用户设置相同的密码时,这些相同密码经过哈希算法计算后会得到相同的哈希值。攻击者一旦获取了数据库中的哈希值,就可以通过查询彩虹表,快速找到对应的明文密码,从而破解用户账户 。

而引入盐值后,每个用户的密码在进行哈希计算前,都会与一个唯一的盐值进行拼接,然后再计算哈希值。这样,即使两个用户设置了相同的密码,由于盐值不同,最终生成的哈希值也会截然不同。例如,用户 A 的密码是 "password",生成的盐值是 "salt1",拼接后的字符串为 "passwordsalt1",计算其 SHA-256 哈希值为 "hash1";用户 B 的密码同样是 "password",但盐值是 "salt2",拼接后的字符串为 "passwordsalt2",计算得到的哈希值为 "hash2","hash1" 和 "hash2" 是完全不同的 。这就使得攻击者无法通过预先计算好的彩虹表来破解密码,大大增加了密码破解的难度和成本,有效提升了密码存储的安全性。

3.2.2 实战

以用户注册和登录功能为例,详细介绍如何利用 cSHAKE 库结合盐值来实现安全的密码验证机制。

在用户注册时:

c 复制代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include "cSHAKE.h"

// 生成随机盐值,这里简单示例为16字节
void generate_salt(char *salt, size_t salt_len) {
    const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    for (size_t i = 0; i < salt_len; i++) {
        salt[i] = charset[rand() % (sizeof(charset) - 1)];
    }
    salt[salt_len] = '\0';
}

// 计算"密码 + 盐值"的SHA-256哈希值
void compute_sha256(const char *password, const char *salt, char *hash) {
    char combined[1024];
    snprintf(combined, sizeof(combined), "%s%s", password, salt);
    cSHAKE(combined, strlen(combined), hash, 32);
}

int main() {
    srand(time(NULL));

    char password[100];
    printf("请输入注册密码: ");
    scanf("%s", password);

    char salt[17];
    generate_salt(salt, 16);

    char hash[65]; // 32字节的哈希值转换为十六进制字符串需要64位,加上字符串结束符'\0'
    compute_sha256(password, salt, hash);

    // 模拟存储哈希值和盐值,这里只是简单打印
    printf("存储的哈希值: %s\n", hash);
    printf("存储的盐值: %s\n", salt);

    return 0;
}

上述代码中,generate_salt函数用于生成 16 字节的随机盐值,compute_sha256函数则将用户输入的密码和生成的盐值拼接后,利用 cSHAKE 库计算其 SHA-256 哈希值 。在实际应用中,会将计算得到的哈希值和盐值存储到数据库中。

在用户登录时:

c 复制代码
#include <stdio.h>
#include <string.h>
#include "cSHAKE.h"

// 计算"密码 + 盐值"的SHA-256哈希值
void compute_sha256(const char *password, const char *salt, char *hash) {
    char combined[1024];
    snprintf(combined, sizeof(combined), "%s%s", password, salt);
    cSHAKE(combined, strlen(combined), hash, 32);
}

int main() {
    char password[100];
    printf("请输入登录密码: ");
    scanf("%s", password);

    // 假设从数据库中获取到的盐值和哈希值
    char stored_salt[17] = "your_stored_salt";
    char stored_hash[65] = "your_stored_hash";

    char input_hash[65];
    compute_sha256(password, stored_salt, input_hash);

    if (strcmp(input_hash, stored_hash) == 0) {
        printf("登录成功\n");
    } else {
        printf("密码错误\n");
    }

    return 0;
}

在登录过程中,首先获取用户输入的密码,然后使用从数据库中读取的盐值与输入密码拼接,再次利用 cSHAKE 库计算其 SHA-256 哈希值 。最后将计算得到的哈希值与数据库中存储的哈希值进行比对,如果两者相同,则表示用户输入的密码正确,登录成功;否则,提示密码错误。通过这种方式,有效保障了用户密码的安全性,即使数据库中的哈希值和盐值泄露,攻击者在没有获取到原始密码的情况下,也难以通过哈希值破解用户密码。

3.3 安全编码最佳实践

在使用哈希算法和进行密码学相关的安全编码时,遵循一些最佳实践原则至关重要,可以有效提升系统的安全性,降低安全风险。

首先,应坚决避免使用过时的算法,如 DES(Data Encryption Standard)和 MD5(Message - Digest Algorithm 5) 。DES 算法由于其密钥长度较短(56 位),在现代计算能力下,已经能够被相对容易地破解,无法满足当前对数据安全性的高要求。MD5 算法虽然曾经被广泛应用,但后来被发现存在严重的安全漏洞,如容易产生碰撞,即不同的输入数据可能会生成相同的哈希值,这使得它在需要高度安全性的场景中不再适用,例如在用户密码存储和数据完整性校验等关键领域,使用 MD5 算法可能会导致严重的安全问题,因此应选择更安全、更强大的算法,如 AES 用于对称加密,SHA-256 及其更高级的变体用于哈希计算。

其次,密钥的存储位置和方式关乎整个加密体系的安全性。在嵌入式设备等应用场景中,密钥不应硬编码在代码中,因为这样一旦代码泄露,密钥也会随之暴露,导致加密数据完全失去保护 。理想的做法是将密钥存储在安全的位置,如嵌入式设备的加密 Flash 中。加密 Flash 提供了一定的硬件级别的加密保护,能够防止密钥被轻易读取或篡改 。同时,对于密钥的管理,应采用严格的访问控制策略,只有经过授权的程序模块才能访问密钥,并且在密钥的使用过程中,要尽量减少其在内存中暴露的时间,使用完毕后及时清理内存,避免密钥被窃取。

此外,对于密码的处理,除了使用加盐的哈希算法外,还可以考虑使用更高级的密码哈希函数,如 bcrypt、scrypt 或 Argon2 等 。这些函数不仅内置了盐值管理功能,还能够通过动态调整计算成本(如迭代次数、内存占用等),来抵御硬件加速破解,进一步增强密码的安全性。同时,在系统设计中,应定期更新密码存储方案和加密算法,以适应不断变化的安全威胁,例如当出现新的密码破解技术或加密算法漏洞时,能够及时采取措施进行升级和修复,确保系统的安全性始终处于较高水平。

四、总结与展望

在 C 语言的安全编码领域,密码学技术为数据的保密性、完整性和认证性提供了关键支持。通过深入理解对称加密、非对称加密以及哈希算法的基础概念,我们掌握了不同加密方式的适用场景和原理。在对称加密实战中,AES-128 算法凭借其高效性和安全性,成为保护敏感数据的有力工具,借助 tiny-AES-c 库,我们能够轻松实现数据的加密和解密,确保数据在存储和传输过程中的安全。哈希算法方面,SHA-256 以其不可逆性和强大的数据完整性校验能力,在用户密码存储和文件完整性验证等场景中发挥着不可或缺的作用,结合盐值的使用,进一步增强了密码存储的安全性,有效抵御了常见的密码破解攻击。

展望未来,随着信息技术的飞速发展,安全编码领域将面临更多的挑战和机遇。量子计算技术的崛起可能对传统的密码算法构成威胁,后量子密码学有望成为研究热点,C 语言开发者需要关注这一领域的进展,探索如何在 C 语言项目中应用抗量子密码算法,以确保数据的长期安全性。在物联网、人工智能等新兴领域,C 语言作为一种高效、灵活的编程语言,将继续发挥重要作用,我们需要根据这些领域的特点和需求,进一步优化密码学算法和安全编码实践,例如在资源受限的物联网设备中,开发更加轻量级、高效的加密算法和密钥管理方案。随着安全威胁的不断演变,安全编码的理念和技术也将持续更新,开发者需要不断学习和掌握新的安全编码技术和最佳实践,加强对代码安全性的审核和测试,以构建更加安全可靠的软件系统,为信息安全保驾护航。

相关推荐
Herbert_hwt2 小时前
C语言循环结构完全指南:掌握for、while、do-while循环及实战应用
c语言
奔跑吧邓邓子2 小时前
【C语言实战(79)】深入C语言单元测试:基于CUnit框架的实战指南
c语言·单元测试·实战·cunit
Shylock_Mister3 小时前
弱函数:嵌入式回调的最佳实践
c语言·单片机·嵌入式硬件·物联网
攒钱植发3 小时前
嵌入式Linux——“大扳手”与“小螺丝”:为什么不该用信号量(Semaphore)去模拟“完成量”(Completion)
linux·服务器·c语言
三品吉他手会点灯4 小时前
STM32F103学习笔记-16-RCC(第3节)-使用HSE配置系统时钟并使用MCO输出监控系统时钟
c语言·笔记·stm32·单片机·嵌入式硬件·学习
jzhwolp4 小时前
从nginx角度看数据读写,阻塞和非阻塞
c语言·nginx·性能优化
passxgx6 小时前
10.7 密码学中的线性代数
线性代数·密码学
oioihoii8 小时前
《C语言点滴》——笑着入门,扎实成长
c语言·开发语言
say_fall10 小时前
C语言容易忽略的小知识点(1)
c语言·开发语言