糕工的C语言开发日志 半月刊(日更版) 第五期2024/8下

一、专有名词与英语

trace     追踪
generic   泛型
branch    分支
remote    远程
origin    起源

二、命令行基础指令

1.安装软件

sudo apt-get install gcc
sudo 获得ROOT权限
apt-get install 安装程序
gcc 程序名字

2.文件操作

cd xxx 进入xxx目录
cd .. 回退到上一级目录

rm xxx.cpp 删除xxx.cpp文件
rm -r xxx 递归删除xxx目录

ls 查看当前目录内容
ls -a 查看当前目录所有内容包括隐藏部分
ls -al 列表查看

nano xxx 使用nano打开xxx,如果没有则会先创建该文件
vi xxx 同nano 使用vi

3.git操作 

<1>git首次安装初始化

以下是初次安装需要配置的一些信息
git config --global user.name "u name"   若没有空格,可以不使用""
git config --global user.email uemail
git config --global credential.helper store 这个代表不需要每次上传输入密码
配置完成后可以开始使用git

.gitignore 该文件存储的文件名(可以是具体文件名,也可以是通配符)将不会被git识别
和上传到仓库,(已上传的不受限制)*.x代表所有.x文件,这就是*通配符的作用

<2>git本地仓库初始化

git init 在本地初始化一个空的仓库
git add a.x 上传a.x 也可以 git add . 上传当前目录所有内容  /*将文件传到暂存区*/
git status 查看当前暂存区状态
git commit -m "输入你的提交内容注释"  /*将暂存区文件传到仓库*/

git log 查看上传记录 git log --oneline 查看简单的记录,log指令下使用q退出。
git diff 可以比较本地和仓库的差别,也可以比较不同版本的差异
git diff HEAD~ HEAD 意思是比较HEAD也就是最新版本和上一个版本,~后可以加数字表示从HEAD
回退几个版本
git diff x y  在log可以看到每次提交前面有个版本号 ,这里可以使用版本号查询差异
git rm xxx.x  从本地和仓库删除xxx.x文件,若是只从仓库删除 使用 git rm --cached xxx.x 
加上--cached修饰

<3>git远程仓库首次初始化

ssh配置:/* ssh-keygen意思是生成秘钥,-t rsa指定rsa格式,-b 4096 秘钥大小4096 */
首先退回到根目录下,然后使用cd .ssh进入.shh文件夹,使用ssh-keygen -t rsa -b 4096 生成
ssh公钥,回车后输入文件名,然后输入私钥密码(空白就直接回车)

打开新的 秘钥.hub文件,这里是公钥,复制全部内容并将其添加到github网站的setting里的SSH秘
钥里。
修改秘钥.hub文件,这里我的文件名为githubsshkey.hub,
将下列内容添加到config文件:没有就新建一个(就叫config,是一个无后缀的文件)/*这里很重要,
错了会没有权限*/

# github
Host github.com
HostName github.com
PreferredAuthentications publickey
IdentityFile ~/.ssh/githubssh 
这些配置的意思是在访问github时,使用这个秘钥(私钥)文件。

<4>远程仓库链接本地仓库

    无本地仓库直接关联远程仓库:
最简单的办法是 git clone 仓库,(不需要提前git init),然后git commit之后再git push。

    先有本地仓库再关联远程仓库:
首先git init,然后git remote add origin SSH链接,remote是远程的意思,origin是起源.

<5>远程仓库pull和push,分支操作

使用git branch -M main 修改本地branch(分支的意思)名称为main,这里是为了和远程仓库
主分支同名如果不加上origin 分支名,默认是对main做处理,如果push的分支没有的话会在
远程仓库新建。

git pull origin main (远程非空的情况下先使用pull),这里的main是分支名 
git push origin x1   (远程非空的情况下先使用pull),这里的x1是分支名

git branch xx 新建一个xx分支,
git branch 查询分支数量和名称,
git switch cc切换本地分支到cc,

<6>分支合并

git merge xxx 将xxx合并到当前分支中,随后调用-d删除刚才的分支。
git branch -d xxx 删除一个已经合并的分支xxx,若要删除未合并的分支,则使用-D

    在修改了文件导致分支有差异之后,可以使用git switch来切换查看每个分支,然后使用ls和
git  status的内容和状态,每当切换了分支,他都会显示该分支的文件状态。

    譬如分支main 删除了1.cpp,但是分支dev没有删除1.cpp,那么当你git switch到分支dev的
时候,是可以看到1.cpp的

三、cpp语法

1.初始化变量

除了常规的=初始化,对于变量,也可以用(){}初始化,如下
int a = 1;
int a(1);
int a{1};
三者是等价的。

在cpp中,struct和class是不需要带前缀的,可以直接声明,

譬如C中
struct ggg temp1;

cpp中
ggg temp1;


带默认值的函数形参,需要注意带默认值的形参必须都保持在形参列表的尾部,
默认值应当定义在声明里,函数定义处不应包含默认值。

避免使用未初始化的空指针,以免出现未定义行为。
同时&引用也应当在创建时刻初始化。
尽量避免四层以上的函数嵌套,这里的四层是从你的底层计算而非项目的底层。

2.std命名空间

#include <iostream>
using namespace std;

这里的意思是后续的内容都使用std这个命名空间,并且必须先包含iostream,因为命名空间是由该文件
定义的。

当然如果你只使用一些功能,譬如cout,cin也可以单独声明,像这样。
using std::cout;

或者在调用的时候加上命名空间

std::cout<< "hello world"

3.vscode 编译和运行

 vscode的代码运行依赖cjson配置文件,如下是针对多文件同时编译时候的配置方法。

在task.json 的arg中-g后修改“*.c”代表编译该目录下所有.c文件,(*是通配符)
同理也可以加入.cpp文件

同时在.c和.h文件中包含其他文件的时候,尽量采用./xxx/x.h的形式,指明相对工作路径以后
的绝对路径。

4.cpp常用库 

在CPP调用C库库一般会采取

#include <cstdint> /* 这样的形式,在前缀加上C,并且无.h后缀 */

若要自建一个.c文件并且在cpp中调用其函数,应当在.h文件声明处使用extern "C"
如下:

#ifdef __cplusplus
extern "C"
{
#endif

此处正常填充.h文件内容

#ifdef __cplusplus
}
#endif

climits.h的库包含了各个基础数据类型的最大值和最小值的宏。

 5.string字符串

cpp中也支持c中的char* 和char str[]形式的字符串,但是更推荐stringstring形式,

使用string 要#include <string>

string 对比C原生的指针和数组形式有如下好处:
<1>可以直接拼接,实现 str3 = str1 +str2;的形式
<2>他的长度是变长的,可以自动调整大小,而不必担心指向的内存空间不足。

string 变量可以直接用 != 判定是否和目标字符串相同,但是传统C数组字符串不能。

 6.动态内存

    cpp的new delete 和C中的malloc free类似,首先你只能delete new来的内存,不能delete
自己指向的静态内存,
    其次也不要重复del同一块内存。

在cpp中对空指针使用del是安全的。

    当你要创建一个动态数组可以使用 int* arr = new int [10]; 同样的在释放的时候也要加[]
保持前后一致,代表释放整个数组,而非首个元素。delete [] arr;这里的[]是放在数组名前面的。

    数组是常指针,不可移动,但是new来的内存被放在指针而不是数组里,其实这里的arr是可以移动
的,但是为了避免移动后无法正确释放,所以不要移动new来的内存指针原件,如有需要可以去创建一个
副本来操作。

7.容器

#include <vector> 容器
vector<type> name(size);
#include <array> 数组 C++11特性
array<type,size> name = {};

    array和vector和原生的[]数组都可以通过[]访问,但是只有vector可以直接整个容器
复制到另一个容器。对于array和vector使用.at(index)可以检查非法索引的同时进行数据访问。

 8.引用

    引用也是一种直接操作该变量的方式,并且函数A形参是引用的话,可以直接传入原变量
(此时会隐性的生成一个临时引用),也可以引用传入,但是如果打算把B函数返回值放
进来的话,B函数的返回值必须也是引用类型才可以。

    另外就是A函数的形参如果是const & 常量引用的话,也是可以直接传入数值而不是变量
的,没有的话就只能传入变量和引用。

    形参string 类可以接收char* 传统字符串,尤其是在&引用场景。

9.引用、指针、实体

值传递,指针传递,引用传递的选择原则:

注:实际上引用是指针的另一个接口

<1>对于小型数据结构可以使用值传递
<2>对于数组结构使用指针传递(唯一方式)
<3>对于较大的数据结构,使用const *或者const &的方式传递
<4>对于类使用const &引用传递
<5>当然除此之外需要因地制宜,对于一些数据结构为了便于操作,选择引用&,
    譬如cout << value 这里实际上就是引用。

10.模板函数 

模板操作 template 
    使用 template <typename name>的方式声明一个模板,
(不需要加;另外就是typename是关键字,也可以被替换为class(class是早期C89做法),
name是模板名),优先使用typename而不是class。

template <typename T> 
void pfunc(T& a,T& b);
/* template <typename T1, typename T2> 一次声明两个模板 */
    如此就实现了利用模板实现了函数重载,当调用不同类型的实参传入,系统会根据模板
自动匹配并创建对应的函数。
    
    模板在函数实现(.cpp文件)和声明(.hpp文件)都需要写上,可以把它理解成一种类似
volatile或者const的关键字,每次用都要写一遍。
    就像这样:/* 当然函数的返回值也可以使模板,并且当非模板函数和模板函数具有相同
格式的时候,模板会覆盖掉非模板函数 */

template <typename T>
void pfunc1(T& a,T& b);

template <typename T>
void pfunc2(T& a,T& b);

    当然模板函数也有其狭隘性,譬如结构体、数组就不能比较大小,或者使用乘除法,并且
模板本身也是可以接收指针的,譬如T&,这里的T可以被识别为指针,但是又不能是直接对变量
取地址,你可以先创建一个指针变量,然后传入。

    在函数头部加入template <>,这个叫显式具象化,该形参的函数会优先调用显式具象化
的函数,而非模板创建的函数。

    template <> void pfun1(job&, job&); /* 这个叫显式具象化 */ 可以对这一类变量
的函数单独设置自己的函数。
    template  void pfun1<job>(job&, job&); /* 这个叫显式实例化 */ 将会按照这样
的格式去调用。

    pfunc1<job>(x,y);这就是按照job实例化而不是按照模板自动实例。当模板函数原型是 T 
而不是T&的时候,这种方式还会将不同类型的变量强制变为job识别,但是如果是&那就会报错。

11.重载函数

对于重载函数的最优匹配原则如下:
<1>完全匹配(包括const修饰符,但是这里只针对&和*生效,因为const type和type具有
    二义性不能重载),常规函数优于模板
<2>提升转换,char/short提升为int,float提升为double
<3>标准转换,int 转 char,long转double。
<4>用户声明的类型转换
如果最优匹配是模板函数,那么具体模板函数优于通用模板函数。
在形参中,&引用和本体被视为同一特征,尤其是在重载的时候,他们属于完全匹配的序列。

当重载函数存在模板和非模板的匹配函数时,可以使用 void pfunc1<>(形参)的方式来使用
模板函数,也可以在<>中写明具体哪个模板类

12.类 

    类的私有成员只能被他的成员函数访问,不论该成员函数的访问权限是public还是private又
或者protected

    私有成员不能被子类直接访问和修改(可以通过调用父类公有方法来改变),但是保护成员可以
被子类直接访问和修改。

    构造函数是一种特殊的成员函数,他没有返回值,但是也不需要声明为void返回,其他声明
方式和普通成员函数一样,
    另外就是形参不可以和成员变量同名,最好在成员变量前用m_前缀或者_后缀,用以区分。

    构造函数的语法有些特殊,可以使用如下方式,注:一般构造函数和类名是相同的

    type val = pfunc(...);传统方式
    type val(...);省略构造函数名,直接构造,二者是等价的,第二种用法更紧凑,也更常见


    譬如 type* val = new pfunc();

    并且构造函数不能当做成员函数访问,他是一个初始化函数,只能创建类对象,不能在对象中
当做方法调用。

    析构函数:就是删除对象的函数 ~name();析构函数和构造函数对应,在前面加~表示析构,
一般在对象销毁时被自动调用。

    const后置修饰成员函数,表明该函数不可修改类的成员变量。

13.decltype

    类似typeof的一个C++11特性关键字:decltype,使用方法如下:
    int a; decltype(a) b;定义了一个类型同a的b变量。
    decltype()也可以将函数作为参数,他将会定义一个和返回值同类型的变量,但是他只是
查找原型,不会调用函数本体。

    假如在decltype()内又()会导致其变成&,譬如decltype((a)) b = c;此时b是int&;
    decltype 还可以配合typedef使用,例如 typedef decltype(x+y) typec;此时你就得到
了类型typec。

    另外就是 引用的加减乘除以后的值是原类型,而不是引用类型。

    对于多模板的模板函数,decltype在其内部并不好用,所以有了auto 函数原型 -> type {}
或者是 auto 函数原型 -> decltype() {} 的形式,表明你可以在多模板的情况之下,提前获取
变量类型。

14.const与mutable

    const 修饰的变量可以放在.h文件被直接包含,他具有静态特性。

    mutable 是const的一个特殊修饰符,可以在const的结构体的内部成员变量前使用,
这样即使其他地方不能改,被mutable修饰的变量还是可以修改的。

 

附录:更新记录

        2024/8/16 

                开始了本期,更新了git和vs的一些操作,和一些基础cpp语法。

        2024/8/24

                一些基础cpp语法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值