C语言 关于变量和函数的笔记
概述C语言中的变量可以大致分为4种: (非静态)局部变量 静态局部变量 (非静态)全局变量 静态全局变量 变量的类别由定义的位置和修饰词决定,例如 12345678int s_global = 10; // 全局变量static int s_global_static = 20; // 静态全局变量void fun(){ int s = 100; // 局部变量 static int s_local_static = 200; // 静态局部变量 // ...} 变量的类别决定了它们具有完全不同的性质。 与变量不同,C语言中的函数大致只需要分成两种: 普通函数 静态函数 函数的类别取决于是否使用了static修饰,不同类别的函数也具有不同的性质。 下面会从不同的角度对变量和函数的类别进行理解。 进程的内存分区在启动一个可执行程序时,系统会创建一个进程并为其分配一个内存空间,其中包括进程执行所涉及的所有指令和数据,大致分为如下几个区域: 文本区(Text Segment):存放程序的具体代码指令(包括函数),这块区域在程序运行期间是...
Cpp 异常处理
C++提供了一套复杂的异常处理机制,虽然很多第三方库都禁止使用异常,但是至少在标准库中广泛采用了异常机制,因此学习一下相关的语法是有必要的,至于在编程实践中是否采用异常,仍然是值得讨论的。 简单示例从一个简单的例子开始,这里调用的函数processInput对于非法的输入参数会抛出异常,在main函数中调用时使用try-catch语句块捕获异常进行处理。 123456789101112131415161718192021222324252627#include <iostream>#include <stdexcept>void processInput(int value) { if (value == 0) { throw std::invalid_argument("Input cannot be zero!"); } if (value < 0) { throw std::out_of_range("Input cannot be negative!"...
不同编程语言的相互调用
记录一下各种语言之间简单地进行相互调用的做法。 Cpp 调用 Python在C++程序中调用Python解释器,包括执行简单Python语句(字符串形式),以及执行整个py脚本(文件形式)。 执行简单命令最简单的例子:调用Python解释器,输出HelloWorld,C++源文件如下 main.cpp12345678910111213141516#ifdef _DEBUG#undef _DEBUG#include <Python.h>#define _DEBUG 1#else#include <Python.h>#endifint main() { Py_Initialize(); PyRun_SimpleString("print('Hello, world! (from Python)')"); Py_Finalize(); return 0;} 注意在导入Python.h时进行了一些处理,因为我们的电脑中通常只有Release版本的Python库,并没有Debug版...
C语言 结构体笔记
关于C语言中的结构体的语法,这是C++自定义类型的基础。C++的类提供了非常丰富的功能,但是也会尽量兼容C语言的结构体。 简单示例下面是针对学生信息的结构体的使用示例,包括了结构体对象的定义和读写,以及通过指针传递给函数。 123456789101112131415161718192021222324252627282930313233343536373839#include <stdio.h>#include <string.h>struct Person { char name[50]; int age; double height;};void show(struct Person *person) { printf("Name: %s\n", person->name); printf("Age: %d\n", person->age); printf("Height: %.2f\n", person->h...
Cpp new/delete 语句
new和delete是C++中最基础且重要的申请和释放堆内存的语法,以它为线索可以引出C++堆内存管理的相关内容,它也是智能指针所需要的基础知识,值得整理一下。 基本使用内置类型new和delete最基础的用法是被设计用来替代C语言中的malloc和free的,分配堆内存来构造指定类型的对象,返回对应类型的指针,例如 12int *p = new int; // uninitializeddelete p; 这两个语句都是非常危险的: 最简单的new语句对内置数据类型不会对内存进行初始化,我们得到值是随机的;(Debug模式下可能内存被清空,改成Release模式可以看到随机值) delete语句不会在释放内存之后将指针置空,指针会变成空悬指针。 我们可以用下面的new语句进行初始化 12345double *p1 = new double; // uninitializeddouble *p2 = new double(); // 0double *p3 = new double{}; // 0double *p4 = ne...
Cpp 显式加载动态库
在 C++ 中,加载动态库通常有两种方式:显式加载和隐式加载。隐式加载比较简单,只需要设置链接选项即可自动进行,本文主要学习一下显式加载。 概述在 C++ 中,显式加载和隐式加载动态库的基本介绍如下: 隐式加载是最常用的方式,通常的C++标准库等系统中的库都是采用隐式加载的,程序在编译时需要添加-l选项链接到动态库。在程序启用时,系统会自动查找并加载对应的动态库。隐式加载的优势是代码简单,不需要在代码中处理加载动态库的各种细节。但缺点是要求在编译时动态库也要参与链接,在编译时和运行时都需要保证动态库是可以找到并且使用的,编译时无法找到则编译失败,运行时无法找到和使用则程序无法启动。 显式加载则是一种更灵活的方式,我们可以在代码中精准地控制加载和卸载动态库的所有细节。优势是程序在编译链接时完全不需要动态库的参与,程序在运行时可以根据需要有选择性地进行加载或卸载动态库,即使在运行时对某个动态库的加载失败也不会导致程序中止。 基于显式加载我们可以实现动态库的热更新:在使用动态库的程序保持运行的情况下,更新对应的动态库。这对于一些大型的商业服务是非常普遍的需求,他们不希望因为某些...
Cpp 常见编程范式
单例模式单例模式作为最简单但最常用的设计模式,值得仔细整理一下 C++ 的相关语法。 单例模式简介单例模式(Singleton Pattern),使用最广泛的设计模式之一。其意图是保证一个类仅有一个实例被构造,并提供一个访问它的全局访问接口,该实例被程序的所有模块共享。 大致的做法为: 定义一个单例类; 私有化构造函数,防止外界直接创建单例类的对象; 禁用拷贝构造,移动赋值等函数,可以私有化,也可以直接使用=delete; 使用一个公有的静态方法获取该实例; 确保在第一次调用之前该实例被构造。 在 C++中我们需要考虑: 在什么时机构造?程序一开始,第一次调用前? 如何构造这个实例?在堆上构造,还是通过静态变量?如果单例在堆上构造,是否存在内存泄漏? 实例构造失败会如何?抛异常?返回空指针? 通过接口返回指针还是引用? 线程安全?多线程下会不会重复构造?加锁? 我们暂时不考虑类的继承问题,以及需要给单例的构造传参数的问题。关于构造时机的不同,有以下两种习惯的称呼: 饿汉模式(Eager Singleton):在程序启动后立刻构造单例; 懒汉模式(Lazy Singlet...
Cpp 值封装类型学习笔记
现代C++提供了几个非常实用的值封装类型工具,分别为 std::optional<T> 可能含有T类型的值或者无值;(C++17引入) std::variant<T1,T2,...> (类型安全的union)可能含有类型列表中某个类型的值;(正常情况下不会无值)(C++17引入) std::any 可能含有任意类型的值,也可能无值,使用者必须提供自行指定合理的类型;(如果内部的值转换失败会抛异常)(C++17引入) std::expected<T,E> 可能含有正常的T类型的结果,也可能含有异常的E类型的值;(C++23引入) 它们的定位和用法比较类似,因此一起整理一下,因为这几个工具的语法仍然在迅速发展中,本文只考虑C++20和C++23已经支持的语法。 需要注意的是,这些类型工具都只能接收简单的类型,不允许使用数组类型和引用类型,并且最好不要含有cv修饰符。 std::optionalstd::optional<T>对象的值可能是T对象,也可能是std::nullopt(代表没有值),注意这里的T不允许是数组类型和引用类型。...
Cpp static+extern+inline 学习笔记
整理一下关于C++中的几个关键词(static,extern和inline)的笔记。 staticstatic这个关键词的语义非常复杂,在C语言中本来就有多重语义,C++又添加了额外的语义。 静态函数默认情况下,函数可以被其它编译单元使用,具有外部链接性,可以使用static将其改为具有内部链接性的静态函数,只有在当前文件中才可以使用此函数。例如 123static void func();void func(){} 静态全局变量与静态函数类似,全局变量在默认情况下可以被其它编译单元所使用,具有外部链接性,可以使用static将其改为静态全局变量,只有在当前文件中才可以使用。例如 1static int s = 100; 静态局部变量在函数体内部使用static修饰的局部变量会变成静态局部变量,此时静态的语义不再是对外部不可见,而是延长生命周期,此时变量的存储位置从栈区转移到了数据区,不会因为函数调用的结束而销毁,下次进入函数体时会自动忽略定义和初始化语句,例如 12345int func(){ static int s = 0; ...
Cpp 面向对象——访问和继承权限
在 C++ 的面向对象编程中,有三个访问权限限定词:public、private 和 protected,用于定义对类成员(成员包括变量和函数)的可访问性,也包括在继承后的访问权限变化。 我们主要对class进行讨论,最后会讨论struct和class的区别。 访问权限C++ 通过访问权限限定词来提供数据封装和信息隐藏机制,这有助于提高代码的可读性和可维护性,三种访问权限限定词的基本语义如下: public成员:公开的类成员,可以通过类的成员函数和类的对象访问。 protected成员:受保护的类成员,只能通过类的成员函数访问,无法通过类的对象访问。 private成员:私有的类成员,只能通过类的成员函数访问,无法通过类的对象访问。 注意这里访问的含义:对数据成员的访问是读写,对成员函数的访问则是函数调用。对于数据的访问权限并没有被进一步拆分为只读和可读写,只读的访问效果需要基于const实现,实现只写的访问效果则需要考虑左值和右值特性,不在本文的讨论范围。 通过类的对象访问是指a.x和a.show()这类语法。通过类的成员函数访问是指在成员函数体内部对类的其它成员访问,因为...
