博客主页 😀
文章

Count:

计 36 篇
1106
CPP学习DAY4
无标签
CPP学习DAY4
分类: C++
简介:函数(Function)函数就是我们编写的代码块,被设计用来执行某个特定的任务,当我们之后说到类(class)的时候,那些代码块被称为“方法(method)”,但在这里,是在明确地说某种不属于某个 class 的东西。对我们而言,分割代码以防止重复代码是很常见的。我们不希望多次写同一代码,因为这样做除了复制和粘贴大量的代码,并最终获得一团糟以外,也意味着,如果我们决定改变一些代码,我们就必须在所有粘贴原始代码的地方改变它,这对维护来说将会是一场灾难。所以我们可以做的就是写一个函数来做我们想做的,然后在我们需要的时候我们可以在代码中多次调用,。可以这么理解函数,有一个输入和一个输出(尽管这并非绝对必要的),我们可以提供特定的参数,然后这个函数可以为我们返回一个值。假设我们想把两个数相乘,我们想要写出一个函数来实现它,我要做的第一件事就是在这里写一个叫返回值(return value)的东西,这是这个函数会返回的类型。因为我们把两个整数相乘、当然将产生一个整数,所以我们返回值是int,我将给这个函数一个名字,在本例中,叫 Multiply,它会接受两个参数,它们是我们想要相乘的数字,我把它们叫做A和B。然后我将给函数一个函数体(body)它所要做的就是返回 a × b 。int Multiply(int a, int b) { return a * b; }所以你可以看到我们这里有一个函数,它接受两个参数,都是整数,然后仅仅返回这两个数字的乘积。其实我们并不一定要提供参数例如我可以不提供任何参数,然后返回 5 × 8。int Multiply() { return 5 * 8; }这仍然是一个返回一个整数的函数,但它只是不取任何参数。我们还可以告诉函数我们不想让它返回任何东西。我们通过写一个void来作为它的返回类型。void当然代表没东西,那么我们可以直接把结果打印到控制台。int Multiply() { std::cout << 5 * 8 << std::endl; }让我们回到原来的例子:我们有 int a和 int b,我们返回了这两个整数的乘积。int Multiply(int a, int b) { return a * b; }那么我们如何调用这个函数呢?调用一个函数很简单,让我们试着打印乘法的结果。首先,我要声明一个变量来存储这个结果,所以我输入int result = Multiply()。然后我们就用3和2,这样做的结果就是用这两个参数来调用Multiply函数,然后把返回值,也就是 a × b 的结果,存到这个result整型变量。然后我们可以通过控制台输出这个result让我们按F5来运行我们的程序,当它build好后,我们将会得到6int main() { int result = Multiply(3, 2); std::cout << result << std::endl; std::cin.get(); }这显然是 3×2 的结果。说的更详细些,假设我想要做一系列乘法,然后把它们都打印到控制台。如果我在没有函数的情况下做类似的事情那么它看起来会很乱。我需要重复这段代码,去复制粘贴几次,我把它们叫做 result2 和 result3。int main() { int result = Multiply(3, 2); std::cout << result << std::endl; int result2 = Multiply(8, 5); std::cout << result << std::endl; int result3 = Multiply(90, 45); std::cout << result << std::endl; std::cin.get(); }我们做 85,和 90 × 45 ,如果我运行上面这个程序,将得到一样的值。因为当我复制并粘贴这段代码时,我忘记更改输出语句中的变量了。这种情况其实经常发生,人们复制和粘贴代码块,然后忘记改变一个小细节,在某些情况下,你可能只是运行你的程序,甚至没有注意到不对,直到在之后某个地方造成错误。然而,如果你为它创建一个函数,这种问题就非常容易解决。让我们通过打印result2和result3来解决它:如果我运行下面的代码:int main() { int result = Multiply(3, 2); std::cout << result << std::endl; int result2 = Multiply(8, 5); std::cout << result2 << std::endl; int result3 = Multiply(90, 45); std::cout << result3 << std::endl; std::cin.get(); }我们会得到正确的结果。然而你可以看到,我实际上是多次复制了几乎相同的代码。如果我决定直接用数字相乘的写法来代替这个乘法函数,我必须在每个地方替换它: int result = 3 * 2; std::cout << result << std::endl; int result2 = 8 * 5; std::cout << result2 << std::endl; int result3 = 90 * 45; std::cout << result3 << std::endl;如果不想这么麻烦,我们就可以为它写一个函数。返回类型是void,因为它并不会返回什么给我们,它只会执行我们要求它做的事情。我们把它命名为 ”MultiplyAndLog“,然后思考我们想要的参数。所以这三段代码之间到底什么变化了?实际发生变换的只是两个相乘的数字,所以它们成为了我们函数的参数。到底这三段代码什么发生了变化,我们需要指定什么才能让这个函数执行我们想要的操作?让我们写下我们的参数,所以我们会用 int a 和b (你可以管它们叫任何东西)我们将把其中一段复制粘贴到这个函数中:void MultiplyAndLog(int a, int b) { int result = a * b; std::cout << result << std::endl; }当然,我将用我们的参数来替换3和2,这样我们就可以使用我们指定的参数来执行该函数里的乘法运算了,这会导致a*b发生,然后打印结果到控制台。所以现在,我不需要写很多次这些,我只需要简单地调用MultiplyAndLog,并传入参数所以比如说3和2,然后是8和5,然后是90和45,仅此而已。int main() { MultiplyAndLog(3,2); MultiplyAndLog(8,5); MultiplyAndLog(90,45); std::cin.get(); }这就是我们最后得到的样子,干净易读的程序。这是一个很简单的例子,但它很有效地证明了函数是非常重要的,我们的目标应该是把你的代码拆分成许多许多函数。但不要太过分,你不需要给每一行代码都准备一个函数,那样很难维护、你的代码看起来会很混乱很拥挤。实际上那样会让程序运行更慢。我们每次调用函数时,编译器会生成一个调用指令。这意味着,在一个运行的程序中,为了让我们调用一个函数我们需要为这个函数创建一整个 stack frame(栈框架),也就是说,我们得把参数之类的东西 push(推)到栈上。我们还需要一个叫做返回地址的东西,将其压到栈上,然后我们需要跳到我们程序的某个不同部分,以执行我们的函数里的指令。为了将我们 push进去的结果返回, 我们需要返回到最初调用函数之前的地方。所以这整个过程就是在内存中跳转来跳转去而为了执行函数指令。所有这些都需要时间,所以它减慢了我们的程序。上面所说的减慢原因的原因是,这都是建立在假设编译器决定保留我们的函数作为一个函数而不是将其内联(inline)。我们将在以后深入讨论 inlining之所以说这么多,是因为不想让你直接为每一行代码创建一个函数,这需要一点经验来意识到你什么时候需要一个函数。但基本上,如果我们正在多次做一个重复的任务,那么就可以为它创建一个函数。函数的主要目的是防止代码重复,我们不希望只是到处复制和粘贴代码。现在我们再回到我们的代码。你可能会注意到这个主函数有点奇怪,它说他的返回值是 int,然而却找不到return这个关键字。很明显我没有返回任何东西。所以如果我指定一个返回类型,我真的需要返回一些东西吗。试试看,在这个乘法函数中什么也返回,再按ctrl F7来编译该文件,我们将会得到一个错误,告诉我Multiply必须返回一个值。因此带有返回类型的函数实际上需要返回值吗?答案是肯定的!他们需要!主函数实际上是一个特殊的函数,主函数且只有主函数可以不用返回一个值,如果你没有指定返回值,它会自动假设你返回0。这只是现代c、c++版本的特点,为了让你的代码更简洁。有趣的是,所有这些必须返回一个值的规定实际上只适用于调试模式。如果我们在release模式编译,你会发现我们实际上并没有错误,这并不是说我们在这里做的是正确的,因为如果拿那个返回值做其他事,会得到未定义行为,只是编译器并没有对我们提示而已。然而,在调试模式下,当特定的调试编译标记(flags)被激活时,我们将会得错误,它将帮助我们调试代码,因为在任何时候你都不应该写一个函数,说了它会返回什么,但是并没有返回。这就是对函数的基本介绍。函数非常有用,每个程序都是由一系列函数组成的我们还通常将函数拆分为声明和定义。声明通常会存在头文件,而定义则会写在翻译单元里或者说cpp文件里,下一节会单独来讲头文件中的函数声明。
1226
CPP学习DAY3
无标签
CPP学习DAY3
分类: C++
简介:变量当我们创建一个变量时,它会被存储在内存中的两个地方之一:栈或堆。C++ 中的原始数据C++ 中的原始数据基本上构成了我们在程序中存储的任何类型的数据的基础。在 C++ 中,不同变量之间的唯一区别就是大小—这个变量占用内存大小的多少,这实际上是这些原始数据类型之间的唯一区别。声明一个新变量的语句如下:变量类型 变量名 [= 变量值]变量类型 变量名 [= 变量值]如int variable;或int variable = 8;int代表整数,它允许我们在给定的范围内存储一个整形数字。按照传统,整形数据的大小为4字节(byte),实际上,数据类型的实际大小取决于编译器。这是一个带符号的整数,所以其可以存储的数字范围是±21亿。可是为什么是21亿呢,实际上21亿并不准确,大概是21亿上下。我们来看看这20亿是怎么来的。一个字节是8比特(bit),也就是说,4个字节就是32bits。因为这个数据类型是带符号的,也就是说它有正负,包含了一个符号,也就是负号,而负号会占用1bit,所以我们留给实际数字只有31位。每个bit可存储两个值,也就是0和1,所以一共有2^31,也就是约21亿(实际精确结果为 2,147,483,648,但是要留一位给0,所以应是2,147,483,647,粗略记为21亿就可以了)。那么有没有办法只存储整数而不存储复数,也就是让32bits都用于存储数字呢?答案是当然有!我们可以这样定义一个整型变量:unsigned int variable;这里的 unsigned 意思是无符号数,也就是我们将用来存储符号的那 1 bit 也用于存储数字了。那么 2^32 是多少呢?当然是两倍,也就是约 42 亿了。那么我们还有哪些数据类型呢?下面列出其它的数据类型:数据类型内存大小char1 byteshort2 byteint4 bytelong4 bytelong long8 byte…………当然还有很多的数据类型及其变化形式,但最基本的是这5个。char 通常是用来存储字符的,我们可以给它赋值 65 ,也可以给它赋值 'A'(注意这里用的是单引号)。那我们可以给一个 int 类型的变量赋值字符吗?当然也可以。与字符 A 所绑定的数字就是 65。既然数字是字符,字符是数字,那么他们之间又有什么联系呢?我们先以 char 为例,看看下面两行代码输出的分别是什么:char a = 65; char b = 'A'; std::cout << a <<std:: endl; std::cout << b <<std:: endl;事实上,第一行输出的是 A,第二行输出的也是 A。因为我们在使用 char 作为 a 和 b 的数据类型,就是在告诉 cout 我们传入的是一个字符而不是数字。如果我们将 char 换成 short 又会出现什么情况呢?short a = 65; short b = 'A'; std::cout << a <<std:: endl; std::cout << b <<std:: endl;运行一下可以看到,输出了两个 65。这是因为我们告诉了 cout 我们传入的是一个字数字而不是字符。从上面的例子中可以看出,c++ 的数据类型并不局限于官方给出的建议用法,实际的使用方法还是取决于程序员。因此在这里想强调的是:数据类型之间唯一的真正区别是,当我们使用改数据类型创建一个变量的时候,将会分配多少内存。如果我们想定义小数呢?那么我们可以采用 float 和 double 这两种数据类型:float a = 5.5; double b = 5.5;但是你以为这样就结束了吗?还没有!尽管这里我们使用了 float 数据类型,但当我们在VS中将鼠标悬停在 a 变量上,我们可以发现它仍然被定义为了double类型。那么我们如何定义一个真正的 float 类型的变量呢?我们只需在所赋值后加一个 f(大小写均可)就可以了。float a = 5.5f;float 和 double 前也可以加 long 之类而组成变化形式,这里不多加讨论,只给出这两个数据类型所占的内存大小:数据类型内存大小float4 bytedouble8 bytebool 型是一个较为常见的类型,它的值只有两个:true或者flase。但是如果我们尝试这样定义: bool a = true; 那么我们将会得到一个 1 的结果。因为计算机中没有对或错的说法,只有 0 和 1。所以基本上 0 就代表 false(bool a = false; 的输出是0),而 0 以外的任何东西都被认为是 true。bool 型只占用 1 byte 大小。什么?为什么是 1 byte 而不是 1 bit?事实上,它确实只占用 1 bit,但当我们需要从内存中获取或存储我们的 bool 时,我们没有办法寻址到每一个 bit,我们只能寻址到 bytes。正因如此,我们无法创建只有 1 bit 的内存空间。但是,我们可以将 8 个 bool 类型的值存放在1 byte 里面。开头讲过,实际上数据类型所占空间大小取决于编译器,那么我们要怎样来检查编译器对某个数据类型的定义呢?我们可以用 sizeof() 进行检查://下面代码输出的是bool类型所占用的内存大小 std::cout << sizeof(bool) << std::endl; //也可以不要括号,写成: std::cout << sizeof bool << std::endl;现在,有了这些原始类型,我们可以将其转换为指针(pointer)或者引用(reference)。指针可以通过在数据类型旁边写一个星号来声明,而引用则是在数据类型旁边加上一个&符号。int* a = 1234; //这是指针 bool& b = true; //这是引用指针和引用是一个非常大也及其复杂的问题,所以会在后面的章节单独进行学习。
877
CPP学习DAY2
无标签
CPP学习DAY2
分类: C++
简介:链接器的工作方式当我们将源代码编译成 obj 文件后,需要通过链接器将这些文件连接起来,使之生成我们所需要的可执行文件。那么在这个过程中,链接器究竟做了哪些工作呢?链接的主要工作是找到每个符号和函数的位置,并将它们链接在一起。之前我们提到过,每一个 cpp 源代码文件实际上就是一个 translation unit,他们之间无法相互沟通,彼此之间还没有建立联系。当我们将程序写在多个 cpp 文件中时,就必须使用链接器了。链接器除了找到每个符号和函数的位置外,还有一个很重要的功能,就是找到程序的入口点(Enter point),也就时我们所说的main函数。当我们 build 一个项目时,实际上是执行了两个步骤:编译和链接。在VS中,假设我们有一份 cpp 代码如下:#include <iostream> void Log (const char* message){ std::cout << message <<std::endl; }当我们执行编译命令时(快捷键:CTRL + F7),我们会发现编译成功完成,没有报错;但当我们执行 build 或者运行(快捷键:F5)时,会产生一个 linking error,其大意时"没有找到程序入口点"。这是因为上面的代码中并没有包含 main 函数,在链接时链接器找不到程序的入口点,就无法知道程序应当从哪一步开始。但是入口点并不一定必须是 main 函数,实际上我们可以自行定义程序的入口点,这可以在链接器中修改,但绝大多数情况我们无需改动,使用 main 作为程序的入口点即可。下面我们用一个例子来小结一下:首先我要声明一个用于打印的函数,将其写在 Log.cpp 文件中:#include <iostream> void Log(const char* message) { std::cout << message << std::endl; }这里我们可以尝试去编译,会发现我们编译成功了!(即使里面没有main函数);然后我们想写一个简单的两数相乘的程序,命名为 Math.cpp :#include <iostream> void Log(const char* message); int Multiply(int a, int b) { Log("Multiply"); return a * b; } int main() { std::cout << Multiply(5, 8) << std::endl; std::cin.get(); }此处的 void Log(const char* message); 是告诉编译器我们有一个名为 Log 的函数存在(尽管我们并没有给出其详细定义),而编译器也会相信缺失有一个Log 函数存在,这样在下面的 Multiply 函数中才能使用 Log 函数。将两份文件编译完成后,我们会得到两个 obj 文件,这两个文件在生成时会被 linker 所连接。如果我们将上述 Log.cpp 文件中的 Log 改成 Logr 或别的什么名字,我们再编译时会发现这两个文件依旧会编译成功。这说明这两个文件之间没有联系的。但如果我们按下生成又会发生什么呢?我们会得到一个 Linking error,会告诉你无法解析的外部符号。这就体现出 Linker 的功能了。linker 会在每个 obj 文件之间建立联系,从而生成最终的程序。重新回到上面的例子。如果我们将写在 Multiply 函数中的 Log("Multiply"); 注释掉,那么链接器将不会报错,因为在编译过程中,编译器识别到我们并没有使用 Log 函数,那么在链接时将不会去寻找和 Log 有关的信息。然而如果我们不是注释掉Log("Multiply"); 而是将 main 函数中的输出语句注释掉,那么链接器依旧会报错。可是我们既然将输出语句注释了,说明我们并没有使用 Multiply 函数,自然也就没有用到 Log 函数,那么怎么会报与 Log 有关的链接错误呢?这是因为我们在此文件中声明的 Multiply 函数中使用到了 Log 函数,而编译器无法确定这个声明的函数是否会在其他地方被使用,所以一同进行了编译,那么链接器自然就会去找包含在 Multiply 函数中的信息。而在之前我们注释掉 Log 语句时,编译器发现并没有使用到 Log 函数,自然就不会将它放入这个 translation unit 的编译结果中。这里需要注意的两个关键词是“使用”与“定义”。那么如何解决后者的问题呢?我们可以在 Multiply 函数的定义前加上 static ,即static int Multiply(int a, int b) { Log("Multiply"); return a * b; }这样我们这个函数的使用范围(作用域)就是当前文件了,也就是说其他的文件无法与之建立连接。这样我们在编译此文件时,如果发现此函数没有在当前 Translation unit 中被调用,即便是声明了此函数,也不会对其进行编译。这样我们的链接也就能顺利通过了。在这里我们再强调一点:函数的返回类型,函数名、参数类型及参数个数都是十分重要的。如果我们将上例中的 Log.cpp 文件中的 void Log 改成 int Log 并添加代码 return 0;,一样会出现链接错误。因为在 Math.cpp 文件中,链接器试图去寻找一个返回类型为 void 的名为 Log 的函数,当然是找不到的,所以会出现错误。参数同理。还有一种链接错误就是当我们有相同符号,也就是有两个名称相同的函数具有相同的返回值和相同的参数,此时我们的linker不知道要链接到哪个函数,此时就会出现链接错误。而当这两个相同函数出现在同一个代码文件中,即使没有发生链接,编译器也能够通过报错来告诉我们此处出现了两个相同函数。也许你会说这么傻的错误怎么会发生,但是请考虑以下情况:我们先写一个头文件 Log.h#pragma one void Log(const char* message) { std::cout << message << std::endl; }再写文件1:#include <iostream> #include "Log.h" int main() { Log("hello"); }再写文件2:#include <iostream> #include "Log.h" void IntLog () { Log("world!"); }我们可以看到,这三个文件中 Log 只被定义了一次,但是如果我们将它进行生成,那么我们仍会得到链接错误!这就要重新说回 #include 的作用了:#include 就是将后面文件中所有的代码复制到此处。所以实际上我们的文件1和文件2是这样:文件1:#include <iostream> void Log(const char* message) { std::cout << message << std::endl; } int main() { Log("hello"); }文件2:#include <iostream> void Log(const char* message) { std::cout << message << std::endl; } void IntLog () { Log("world!"); }这样就很明显了,我们将 Log 函数定义了三次,链接器当然会报错了。那么我们要怎样避免这样的错误呢?方法一:在 Log.h 文件中对 Log 函数的定义前添加 static 即可,也就是说,被复制过去的对于 Log 函数的定义仅在当前文件中生效。这样就可以解决链接方面的问题了。方法二:在 Log.h 文件中对 Log 函数的定义前添加 inline 即可,也就是说,被复制过去的仅仅是 Log 函数的定义的内部的文件仅在当前文件中生效。拿文件1举例:方法1:#include <iostream> stastic Log(const char* message) { std::cout << message << std::endl; } int main() { Log("hello"); }方法2:#include <iostream> int main() { std::cout << "hello" << std::endl; }还有一种方法,就是将其写作一个单独的 Translation unit,然后在头文件中仅保留对此函数的声明(再次区分定义与声明的区别!)。最后再重复一次:链接器(Linking)的作用就是要将编译过程中生成的所有对象文件(.obj)链接起来。它还会导入我们所需要的其他的一些库文件,例如 C运行时库、C++标准库、平台API以及其它的一些东西,这是非常常见的。同时也有不同类型的链接:静态链接和动态链接 。这些内容在后续的学习中将会被涉及。
876
CPP学习DAY1
无标签
CPP学习DAY1
分类: C++
简介:简单的HelloWorld程序其实并不简单"#include"处为预处理语句,在#号后面的都是预处理语句,在编译之前会把里面文件所包含的内容copy到此文件中。include所包含的文件通常被称为“头文件 (header file) ”。例如:#include <iostream>这句的意思就是把iostream文件里的内容拷贝到当前文件中。每一个C++程序都有一个main函数。main函数是程序的入口。c++程序是逐行执行返回类型是int,但main函数比较特殊,当不需要返回值时,其默认会返回0。注意:仅对main函数起作用,此为一个特殊情况。<< 符号叫做重载运算符,实际上是一个函数,相当于print() ,所作的操作就是将“hello world” 字符串传入cout,而cout就是将其打印在控制台里。endl就是前进到另一行。.get() 含义是等待输入。当预处理结束后,编译器就会将我们的代码转换为机器码。VS中的 solution configuration (解决方案配置) 用于定义如何为这个平台编译的一系列规则; solution platfotm(解决方案平台)则是我们当前编译的目标平台,例如我们选择x86,就是生成 windows 32位系统下的可以运行的文件(x86和win32是一回事)。所有的 .cpp 文件都会被编译,而头文件则不会,头文件只有在编译的时候其内容会被放入 .cpp 文件中。所以我们会有一堆 .cpp 文件被编译,而且他们是被一个个单独编译的。每个 cpp 文件会被编译成一个 object 文件,在Windows下的拓展名为 .obj 。这些 obj 会由链接器 (linker) 链接成一个整体,也就是最后的 exe 文件。linker会将所有的obj拿来,并将它们联系起来组成一个 exe 文件。在vs中,仅编译 cpp 文件的快捷键为 ctrl+F7 。P.S. 不要依赖错误列表 (error list),使用输出窗口查看错误!如果我们不想在main文件中显示太多代码,则可以将代码写在多个文件中,但是需要在使用时进行声明。declaration(声明)definition(定义)linker 会将它们相互关联。在生成的时候,linker 会去找 log 的定义,然后跟 main 里调用的联系起来。如果找不到就会产生一个 linking error 的错误。linker的工作就是 resolve symbols,联通各个函数。本例中,linker会把log的定义拿到一个公共的binary里,也就是 hello world.exe编译器(compiler)的工作原理c++ 编译器唯一的功能就是要把代码文本变为从代码到程序这一过程中的中继格式——obj。之后 obj 们会被传入 linker,linker 会将其链接起来,从而生成一份可执行文件,也就是我们最后得到的程序。关于 linker 的部分之后再谈,这部分先讨论编译器的工作。事实上,compiler 在产出这些 obj 时,做了好几件事:首先它需要预处理(pre process)我们的代码,也就是所有的预处理语句会在那时被评估;预处理后,我们会进入标记解释(tokenizing)和解析(parsing)阶段,这个阶段用通俗的语言来讲就是把我们所写的c++代码处理成编译器能懂和处理的语言,这个阶段的结果就是创建某种叫做抽象语法树(abstract syntax tree),也就是将我们的代码以抽象语法树的形式表达出来。说到底,编译器的工作就是把代码转化成常数资料(constant data)和指令(instruction),当编译器创建了这颗抽象语法树后,就可以产生代码了,这个代码是真正的 CPU 会执行的代码。项目里的每个 cpp 文件都会被编译器编译成一个 obj,而这些 cpp 文件也被称为编译单元(translation unit)。我们要有一种意识,那就是 c++ 中没有“文件”这一说法,文件只是一种用来给编译器提供源码的方法。我们需要告诉编译器文件类型和编译器该如何处理它,当我们创建一个.cpp 文件时,编译器就知道它是 c++ 的代码而不是头文件代码;同样的,当我们创建一个 .h 文件,则编译器会将它识别为头文件代码而不是 c++ 代码。这是一种默认的方式,我们完全可以自行定义(可能要对编译器进行设置或者做一些改动),但如果我们不主动告诉编译器怎么处理,编译器就会按照自己的默认规则进行处理。我们完全可以定义一个后缀名为 .chi 的文件,然后告诉编译器这是 c++ 代码,请按照 c++ 代码进行处理。所以,文件不代表任何东西,只是一种用来给编译器提供源码的方法(再次强调)。当我们告诉编译器这个文件是c++代码时,编译器就会将其当作一个 translation unit,然后 translation unit 会得到一个 obj。在 cpp 文件 include 其它 cpp 其实时很常见的做法。实际上就是一个大的 cpp 文件里面有很多小的 cpp 文件。如果我们只编译那一个cpp文件,也就是只有一个 translation util,也就只有一个 obj。这就是为什么我们这里要用两个术语(translation unit 和 cpp文件),如果我们所编译的 cpp 文件相互没有 include ,则会生成多份 obj,然而多份 cpp 文件如果相互 include,那么我们生成的 obj 文件可能会少于 cpp 文件数。但是通常我们的解决方案(这里以vs为例)是 Debug 模式,所以每一份 cpp 文件无论是否包含,还是会生成一份 obj 文件。有了上述的 obj 文件后,我们就可以使用链接器生成我们的程序了。
1410
2023年度总结
无标签
2023年度总结
分类: 默认分类
简介:很快又到了年末,今年的年终总结如期而至了,除了要回顾一下今年什么做的好什么做的不好,也要寄希望于明年,做一些明年的打算。希望今年的总结能为即将结束的2023画上一个句号,也为2024开一个好头。学习/知识管理今年的主要目标就是考研了,不能说满满一整年,最起码也有大半年都围着这么一件事情转了。说实话,一年的备考下来,让我明白了,网上那些学长学姐经验什么的,听听就好。真正在备考的还是自己,所以指定一份适合自己的高效方案才是最重要的。但如何“适合”,如何“高效”却很难把持。可我要说的还是,不能一味听学长学姐的建议之类的,重点还是要回归到自己所面临的问题上,要根据自己情况有所侧重。现在考研已经考完了,可能这场战斗下来并没有丰硕的果实,但是这段过程确实让我受益匪浅。在孤军奋战备考的时间里,我学会了怎样和自己相处,怎样耐得住孤独和寂寞,怎样去独自解决一些问题。更重要的是,我感觉考研磨练了我的韧劲,就是一种忍受力,让我的下限降低很多。同时我也发现了很多原本觉得很有意思的事情其实是浪费时间的,比如玩游戏和看视频。我考完研后本来想好好玩玩游戏,但真到打开游戏的时候却又觉得有些索然无味了。当然一定程度上的娱乐和消遣是必要的,但不能让它们充满自己的生活。今年备考过程中,积攒了很多想看的书和想看的电影和动漫,但碍于考研就一直没敢下手,现在也可以慢慢拿出来品一品了。接下来的一年里,我的目标是最少看完15本书(不是网络小说),然后坚持练字,培养整理收集到的知识和做笔记的能力,争取学会能够搭建属于自己的清晰的知识框架,并且能够高效利用起来,在此期间要搭建属于自己的稳定的多端的 knowledge kit。健康/锻炼今年参加了四场龙舟赛,两场在福州本地,一场在厦门,还有一场在云南。也做了两场龙舟赛事的裁判。总的来说在竞赛方面还算不错,也有拿到一点小荣誉,也去了别的城市观光游览体验了一番,同时还从赛场上走到赛场下,扮演了一下裁判的角色。有点可惜的就是今年因为考研的原因,错过了去内蒙古的冰上龙舟的活动,希望以后还能够有机会参加这种活动吧。今年有幸还作为龙舟队队长接受了东南卫视的采访,虽然我平时挺善于交际,但是面对镜头还是好尴尬哈哈哈。今年年中开始因为决定专心备考,中断了坚持了两年多的健身习惯。好像也就是从终端健身后不久,我就开始犯很严重的鼻炎,到现在还没有好。而且11月底12月初的时候还有很严重的过敏,从先开始的几个小点到后面大半个身子都红的发紫而且又痒又痛。做检查抽了7管血也没查出来到底是什么原因,最后得出的结论是因为内分泌失调而引起的过敏性荨麻疹。不过那段时间确实是天天熬夜,不锻炼,压力也大。这可能是我目前得过的最严重的病了,好在现在身上的不适的症状已经没有了,身上也是只有的皮肤颜色有点黯淡,之后多吃些维生素帮助色素沉降就好了。接下来的一年要注意坚持锻炼,恢复身体机能。昨天去了趟健身房发现自己连半年前三分之一的重量都推不起来了,而且也有了小肚子。今年务必加强锻炼提高身体素质,坚持健身。今年的半年目标是回复到原来水平,年度目标是增肌到70kg,坚决避免虚胖。情绪/精神状况今年我自我感觉我的精神状况不是很好,情绪方面也偶尔有失控的状况。但我能确定百分百是由于考研所引起的,所以之后也是可以避免的。今年情绪偶尔失控了两三次,主要是焦虑和内耗,这些我都有明确的意识到,但是没有合适的解决方法,纯靠熬过去。精神状况的话也没有以往那么积极乐观,总体来说趋于平稳,但有时候会有些消极。后面我会尽可能让自己的情绪更加稳定,也尽量保持积极乐观的心理状态。我也得去学习一些如何化解情绪波动、缓解内耗和应对情绪临界的方法,尽可能保证自己的心理健康,同时要加强对自己心理状态的监控和记录,并不断强化自己的情绪耐受力,提高情绪阈值。兴趣/创造我个人是对一些嵌入式开发和计算机程序设计以及一些平面设计感兴趣的,但今年实在是没有时间投入进去,我的个人博客更新频率也很低,后半年基本没有更新。之后我会尝试分配出一些时间去培养这种兴趣爱好,并创造性地做一些小玩意或者进行一些改装。目前的初步设想是在租的房子里尝试一下客制化智能家居,3月开始慢慢进行改造,个人预计8月初能有一个初步的成效,主要侧重与兼容性和成本节约,还有实用性。还有就是博客更新方面,我也会尽可能去提高博客更新频率,内容的话还没有一个明确的定向,到时候再看。个人生活/家庭说实话今年家里给予的支持是至关重要的,不管是经济方面还是精神方面。我的母亲担负起了主要角色,我与她的交流也较为频繁。她始终支持我做出的决定,总会无条件地赞同,支持。在我产生开摆的想法时候会鼓励我,但在知道我劳累的时候也会劝我休息。我跟她提起我可能考不上的额时候她会安慰我说考不考得上无所谓等等……可能正是因为她对我的无限的宽容、支持和理解,才能让我坚持到走上考场完成考试,不然可能我也是半途而废的人中的一员。至于我的父亲,我与他说实话没有太多的共同语言,而且每次打电话都会吵架。我认为他对我的教育理念没有我母亲那么更容易让我接受。可能大部分是由于他的不善言辞和坏脾气。所以我也没有太多想去写他的。我知道他的出发点确实是为了我好,但是吧,有时候还是先别出发会比较好。而我自己的话,今年由于已经毕业,而且想要换一个更好的环境去备考考研,所以选择了在外租房,也算是提前体验了社会生活的一部分。租房生活确实自由了很多,但也对自我的自律性提了更高的要求,好在我的自律性还凑合,所以也是维持了正常的备考生活。今年这一年的自己在外面住的经历,让我的自立自理能力更上一层,有些本来需要舍友合作帮忙的事情也能够自己应付了,所以这对我个人生活而言是一种比较大的进步,也积累了挺多的经验。友情/社交其实说实话,这一年下来我的社交频率急剧下降,尤其是下半年,几乎没咋和人打交道。但是我确实是发现,我自己的独处能力变强了很多,而且也发现有些社交其实并没那么重要。可能我以前的社交方式原先也是有待于优化的。今年我会去试着调整一下,让自己的社交活动变得更高效更有质量一些。今年也有一些很不错的朋友,在我各个阶段能够给我一些激励,包括我的队友,我的发小,还有我的老师。我的发小不用多说,相处了十几年了,彼此之间都心里有些默契的,这种关系是不用刻意花心思去打理也能维持在一个很要好的状态。今年也和一些新队员建立起了一些不错的关系,从他们身上看到了我刚开始划船时候的那种热情。和老队员之间的关系也有所加深,彼此之间也有了更深的了解。我的教练在我考研期间不断激励我,他自己也顺利拿到了博士学位,用亲身经历教导我们要努力读书。我的高中老师和本科老师也在我考研期间给了我很大的帮助以及鼓励,对此我也很感谢他们。感谢所有在我背后默默支持我,给我鼓励的所有人。、接下来我会更认真地经营这些已拥有的情谊,也会去尝试拓宽我的交际圈,尽可能接触一些不同的人,同时也优化自己的社交方式,改正以往的不足之处。工作/职业说实话我觉得我这回大概率是考不上了,但我也不能够再花费一整年脱产备考。所以我应该将中心放在工作上了。并不是说读书不重要,但我现在需要将工作排在第一位,然后再考虑考学的事情了。好在我认识的老师都是很难得的贵人,我的大学本科老师给我提供了一份还不错的岗位,我的高中老师也愿意在上海帮我介绍工作。我并没有主动找他们,反倒是他们先联系上了我。在找到一条出路的同时,我是真的很感谢这两位老师在我一筹莫展的时候为我解决了燃眉之急,并且给我了相对较为长远的发展机会。接下来我要认真工作,努力学习技术知识和岗位相关的知识,尽可能快速成长起来,提升自己的个人价值。财务状况今年开支巨大,没收入不说,还要付一些房租水电,购买学习资料,吃饭也是个大头。年初时候手机还坏了一次,真的是曹乐。今年没有什么积蓄,反倒是增加了不少开支。现在的目标是:前两个月稳定一下现在的收支情况,从现在的净支出切换到能够有所储蓄。年中前根据实际情况看一下是不是需要新添置一台电脑,然后每个月或多或少给家里能打一些钱。今年的目标是给家里升级一下电脑手机之类的电子产品,我自己的话手机是刚换的,主力笔记本也还行,但是考虑到工作出差,可能需要添置一台续航久一点的电脑,这都是后话了,等到后期再看。主要的几个目标先实现。今年最大的挑战考研考研还是考研(真的好难啊)今年最骄傲的成就顺利完成考研确定了一份目前看还不错的工作明年最主要的愿望稳定工作(或者考研上岸)小有积蓄能换一整套衣柜,给家里换换电子产品熟练掌握工作内容健身健身健身!!过去一年有好有坏,有成功也有失败,但不管怎样,2024又是一个新的起点。尽管走下去,不必逗留着。愿我们的2024会更加精彩!
博客主页 CHI's blog 今春不见桃花
闽ICP备2022003806号 闽公网安备35012102500456号 本站由又拍云提供CDN加速/云存储服务 本站已运行 2 年 318 天 10 小时 4 分 自豪地使用 Typecho 建站,并搭配 MyDiary 主题 Copyright © 2022 ~ 2025. CHI's blog All rights reserved.
打赏图
打赏博主
欢迎
欢迎
欢迎访问CHI's blog
欢迎您来评论,但首次评论需经过审核才能显示,之后就不用啦^_^
搜 索
足 迹
分 类
  • 默认分类
  • 相册
  • 随想录
  • 技术向
  • 读书笔记
  • 生活小记