C语言 类型转换
关于C语言类型转换的整理笔记。 概述在C语言中,对于A->B的类型转换过程中,无论是C语言风格的显式转换 (type)value(强制类型转换),还是隐式转换(自动类型转换,编译器自动推断目标类型),只要转换前后的类型一致,它们都遵循相同的转换规则,因此无损转换、截断、取模、UB 等可能的表现都完全一致。 如果A->B的转换过程是非法的,那么无论是显式还是隐式转换,都无法成功编译。 如果A->B的转换过程是合法的,那么都可以顺利编译,但是存在如下细微区别: 如果转换过程较为自然且安全,那么使用隐式转换和显式转换没有任何意义的区别; 如果转换过程较为危险,转换前后可能丢失数据,那么: 使用隐式转换时,编译器或静态代码检查可能会发出警告; 使用显式转换可以抑制这些警告,这表明我们手动确认这个转换是安全的。 注: 隐式转换通常发生在赋值(包括初始化和函数传参)和算术运算中; gcc对于涉及隐式转换的警告比较保守,很多情况下的警告需要使用-Wconversion手动开启。 由于本文就是探究类型转换的细节,因此在示例中更倾向于使用显式转换,以增强代码的可读性,...
Cpp 面向对象——三大特性和 C 语言实现
面向对象编程(OOP)并不是一种特定的语言或者工具,它只是一种设计方法、设计思想。 OOP表现出来的三个最基本的特性就是封装、继承与多态。 在本文中我们将讨论C语言如何简单地实现OOP的基础功能,并且关注C++是如何实现OOP的,将两者进行对比。 对于C++的讨论不涉及过多的语法细节,我们不关注public,private,protected修饰的区别,无论是对类的方法还是继承关系的修饰,统一使用public。 我们不关注多继承、菱形继承以及虚继承等复杂情景,只考虑简单的单继承情景,不考虑模板类的处理。 封装封装就是在形式上将数据和操作数据的方法打包在一起,然后提供部分接口给外部访问,隐藏内部的实现细节。 但是C语言没有直接提供将内部信息隐藏的机制,我们只能使用其它的间接方案: 君子协定,约定只通过固定的接口进行访问; 利用动态库的符号部分导出的特点将动态库的部分信息隐藏; 利用static变量和函数对源文件外部不可见的性质,将部分信息隐藏 下面我们只关注数据和操作方法的打包,因为C语言的结构体只含有数据成员,我们需要通过函数指针在结构体中加上操作数据的函数,例如 12345...
C语言 数组和指针
虽然在C++中完全不需要处理C语言中原始数组和原始指针等,C++提供了很多更好的替代实现,但是还是顺手整理一下吧。 在本文中的代码会涉及到几个运算符优先级的细节,这里备注一下: 数组取下标([])的优先级高于解引用(*)和取地址(&); 解引用(*)和取地址(&)的优先级相同,连续出现时,按照从右到左结合; 解引用(*)和取地址(&)是互逆的,因此连续出现时(&*和*&)直接抵消。 数组基础一维数组定义数组的语法很简单 1int a[5]; 此时变量a的类型是int [5]。 在定义时可以赋初值,此时可以省略数组长度,长度会通过初值个数自动获取,例如 1int a[] = {1,2,3,4,5}; 如果同时提供了数组长度和初值,那么数组长度必须大于等于提供的初值个数(否则报错),此时会对数组元素从前到后赋初值,剩余的元素会被初始化为0,例如 1int a[3] = {1}; // a[0] = 1, a[1] = 0, a[2] = 0 sizeof作用在数组名,返回数组整体占用的字节数,...
密码学笔记——AES算法
现在仔细学习整理一下对称加密算法的典型代表——AES算法,并进行C++实现。 算法结构 这里我们只考虑AES-128,即密钥是128bit 的具体算法。 对于输入的明文,首先经过某种扩充处理,保证长度是128bit 的整数倍,然后对明文进行分组,每一组128bit 即16个字节的数据,在具体算法描述中通常会按列拼接为一个$4\times 4$的字节矩阵,并称为状态矩阵。 对于每一个分组执行相同的加密处理得到密文,将每一组的密文拼接即可得到最终的密文。 我们主要考虑单个分组的处理,下面的明文以及密钥均为16个字节的数据, 记明文为$P$,密钥为$K$,密文为$C$。 首先我们要进行密钥的扩展,从16字节的原始密钥$K$经过某种扩展算法得到10个辅助密钥$K_1,\dots,K_{10}$,每一个$K_i$均是16字节的,对应一个状态矩阵,记$K_0 = K$。 加密算法大致分成10轮(9轮是完整的,还有1轮不完整,以及额外的初始步),输入明文$M=P$、密钥$K$以及扩展密钥${K_1,\dots,K_{10}}$,具体计算为: 初始:(只含轮密相加) 轮密...
密码学入门笔记
简单学习一点密码学基础知识。 概述密码学是研究如何保护信息免受未经授权访问和篡改的科学。它是信息安全的一个重要组成部分,涉及加密、解密、身份验证、数据完整性等多个方面。 主要应用情景: 数据加密:保护敏感数据,如金融信息、个人数据等。 身份验证:保护系统免受未经授权的访问。 数据完整性:确保数据未被篡改。 主要算法分类: 对称加密:对称加密是一种加密方法,其中加密和解密使用相同的密钥。 非对称加密:非对称加密使用一对不同的密钥:一个用于加密(公钥),一个用于解密(私钥)。 哈希算法(摘要算法):哈希算法将输入数据转换为固定长度的输出,通常用于传输数据的完整性验证和密码的服务端存储。 注意:现代密码算法的具体实现都是公开的,使用者仅仅需要对密钥进行保密和安全传输,破解通常只能基于暴力穷举,密钥的长度往往决定了算法的安全性。 我们只关注数据的加密解密部分,包括常见的对称加密和非对称加密算法,只是宏观地介绍,并不涉及算法具体的细节。 对称加密对称加密的特点是加密和解密过程是对等的,在加密解密过程中使用相同的密钥。 对称密码的安全性完全依赖于密钥的严格保密,加密以后的密文可以...
Cpp std::tuple 学习笔记
关于C++元组 std::tuple 的整理笔记。 概述std::tuple是一个非常重要的C++标准库模块,它是一个固定大小且类型安全的数据容器, 提供了一种将任意数量的不同类型元素有序地打包组合在一起的方式, 在某些原本需要临时定义结构体的场合可以用std::tuple替代,使得代码更加简洁。 std::tuple的最大特点是编译期运算,与元组相关的标准库函数的实现属于模板元编程的范畴。在模板元编程中,元组也是一个重要角色,因为元组在编译期既含有整数信息也含有类型信息。 事实上从C++11开始,标准库的<tuple>就提供了std::tuple,但是语法既繁琐又简陋,一些直观的语法是不支持的。 在后续的标准中对std::tuple的支持在不断完善,用法变得更加简洁。 在本文中主要涉及的是C++20标准支持的用法,部分语法在C++11标准下是编译不过的。 简单示例下面提供一个基于std::tuple实现具有多个返回值的函数示例 12345678910111213141516171819202122#include <format>#include <...
Cpp std::format 学习笔记
已经2024年了,C++20标准正式收编fmtlib得到的格式化方案std::format已经被三大编译器支持得很好了, 虽然部分特性在后续的标准中仍然在改进,但是值得好好学习整理一下了。 std::format在形式上和Python的字符串格式化非常类似,对用户很友好。当然由于Python自身是动态的,f-string可以玩得花样更多,写起来更方便,这是C++无论如何也比不了的。 简单示例从HelloWorld开始 12345678#include <format>#include <iostream>int main() { std::string str = std::format("Hello, {}!", "World"); std::cout << str << '\n'; return 0;} 主要是用来测试编译器支持的,编译器版本不能太低,并且还需要加入-std=c++20之类的选项,确保编译器...
VSCode Cpp 配置
记录一下目前在VSCode配置C++的内容。 需要说明的是,本文中的配置方案不适合小白,配置比较复杂: 使用 clangd 替换了微软官方的 Cpp 插件 深度结合 CMake 使用 不依赖 VSCode 的 tasks.json 支持多种编译环境 一种更简单的配置方案是基于 Code Runner 插件和 tasks.json 实现的,但是缺点是无法应用于复杂项目中,只适合单个源文件的简单情况,比如 C++ 的简单练习。 必要软件为了正常使用所有功能,除了针对VSCode的配置,需要单独安装下列软件,并把路径添加到PATH Git CMake ninja 插件列表首先列一下目前在VSCode上使用的,涉及到C++的核心插件: C/C++ (Microsoft):微软官方默认的C/C++插件,禁用了代码提示,但是调试这些还需要用它,不能完全禁用 clangd (LLVM):LLVM官方提供的,当前代码提示的主力插件 CMake Tools (Microsoft):微软官方提供的CMake支持,提供CMake命令工具条和快捷指令,以及CMake语法高亮...
Cpp 编译环境笔记
整理一下关于C++编译器的笔记。 在本文中只关注x64平台,Windows(10/11)或 Linux(限于Ubuntu)系统的C/C++编译环境,并且不考虑交叉编译等问题。 除了系统原生的MSVC和GCC,重点关注两个概念: MinGW,MinGW-W64,MSYS… LLVM,clang 将这两组概念并列是不合适的,因为它们根本不是一个层面上的东西,但是这两个确实是我在理解C++编译环境配置时遇到的最大困难。 基本概念C 语言标准c 语言的标准分成两部分: 语法,指导我们应该怎么编程,在源文件中必须满足某些规则 标准库(头文件),相对于系统层面向 c 语言程序提供的一些基础接口,例如printf等,但语法标准只是规定了接口的形式,并没有具体的实现 对 c 语言的支持,也分成两个部分: c 编译器,从源代码得到可执行文件和库文件 c 标准库的具体实现,以库文件的形式提供最基础的功能(以及对应的头文件),供其他程序使用 glibc由于Liunx系统和Windows系统都是基于c/c++编写的,在系统中实际提供的不仅仅是c标准库的实现,还包...
Cpp 日志库 spdlog 配置与使用
整理一下关于C++项目中日志库的学习,在之前曾经陆续实现过几次较为简单的日志工具, 但是在编程逐步正式化之后,自己造这种基础的轮子还是没必要的,采用成熟的spdlog日志库比较合适。 概述spdlog是一个快速的、跨平台的C++日志库,它提供了简单易用的API,支持多种日志级别、多线程安全、异步日志写入等功能。 它具有灵活的配置选项,允许开发者根据需要自定义日志记录行为,支持日志文件的轮换和分割等常见功能。 因为spdlog的使用简单直观,文档齐全,社区活跃,它已经成为当前最常用的C++日志记录工具之一。 spdlog对使用者非常友好: 可以作为纯头文件库使用,直接复制到项目中 可以在编译安装后通过CMake导入 可以将源码直接放在项目文件夹下,作为CMake子项目添加并使用(作为头文件库或二进制库均可) spdlog更推荐通过编译使用,因为可以节约整体的编译时间,否则每次编译时spdlog也要占用相当的时间,可能是用了大量模板的原因。在导入spdlog的头文件时,采用按需取用的策略,而不是某些库采用的只需要include一个万能头文件即可。 spdlog依赖fmtlib...
