最近在学习C++_@ChenPi的博客-CSDN博客,但是里面有很多步骤只写了结果没有写原因,所以记个笔记补充一下。
我用的操作系统是 虚拟机ubuntu18.04。
1.1 环境搭建
首先就是在虚拟机上安装vim
(我个人比较喜欢使用vim编辑器是因为vin编辑器可以防止误触,他必须先按下i才能输入或者删除)
sudo apt install vim
1.2 vim配置
sudo vim /etc/vim/vimrc
进入vim配置文件,在文件最后一行键入自己的配置
##个人常用配置(其余配置可自行查找)
set ai 自动缩进,与上一行保持一致的自动空格
set ic 在查询模型与匹配模式下忽略大小写
set number 左侧显示行号
set showmatch 显示括号配对,并高亮显示相匹配的括号
set showmode 文本输入模型下,加亮显示模式指示器
set showcmd 在状态栏显示所执行的指令,未完成的指令片段
set warn 长行显示自动折行
set cindent 以C/C++模式缩进
set ruler 打开状态栏标尺
set scrolloff=6 设置光标离窗口上下6行时窗口自动滚动
set tabstop=4 设置Tab长度为4
set wrap 自动换行显示
syntax on 自动语法高亮
1.3 环境安装
sudo apt update
sudo apt install gcc g++
sudo apt install gdb
sudo apt install make cmake
1.4 写一个c程序调试一下
建立一个文件夹,输入指令
vi c_demo.c
在打开的vim编辑器里面输入
#include <stdio.h>
int main()
{
printf("hello world\n");
return 0;
}
"esc"后":wq!"保存退出,输入指令
gcc c_demo.c -o c_demo
这行指令 gcc xxx -o xxx 的意思是将c_demo.c输出可执行文件c_demo
然后运行一下
./c_demo
然后就会出现hello world,表示运行成功。
1.5 写一个c++程序测试一下
#include <iostream>
int main()
{
std::cout<<"hello world" <<std::endl;
return 0;
}
标准库在名字空间中被指定为std,所以在使用标准库中的函数或者对象的时候要加上std::,这样编译器就会明白我们调用的函数或者对象是名字空间std中的 。cout<<xxx<<endl表示输出xxx。
g++ cpp_demo.c -o cpp_demo
./cpp_demo
1.6 安装vscode
很多新手可能不太习惯用代码来写代码,推荐用vscode(人家推荐我也就跟个风)
详情就不介绍了,因为我不用嘻嘻
学习P1 C++如何从源文件变为可执行文件_如何将文本文档改为执行文件-CSDN博客,对于文章中一些自己没看明白的部分附上笔记。
致谢:gcc编译的四个阶段解析_preprocess only; do not compile, assemble or link-CSDN博客
2.简介
这一章学习如何将文本文档变成可执行文件。一般是四个步骤:
1.预处理;2.编译;3.汇编;4.链接
预处理阶段处理#include指令、宏展开和条件编译;编译阶段将.C或.CPP文件转换为汇编代码;汇编阶段则将汇编代码转化为目标代码(.o文件);链接阶段将所有目标文件与库文件结合生成可执行程序。通过不同选项(-E, -S, -c, -o)可以控制这些步骤,快速生成所需文件。
参数意义:
-E Preprocess only; do not compile, assemble or link;只预处理,不会编译、汇编、链接,生成.i文件
-S Compile only; do not assemble or link;只编译,不会汇编、链接,生成.s文件
-c Compile and assemble, but do not link; 编译和汇编,不会链接,生成.o文件
-o Place the output into ;指定输出文件名为file,这个名称不能跟源文件名同名,生成.exe文件
2.1 预处理
现有两个文件 main.c 和 log.h,然后将文件预处理生成 main.i
main.c源文件:
//#include <iostream>
#include "log.h" //头文件
#define my_INT int //预处理后替换后被删除
/* 注释 观察预处理后是否存在*/
my_INT main()
{
my_INT data = 8;
log();
return 0;
}
log.h源文件:
#pragma
void log()
{
int log = 5;
}
g++ -E main.cpp -o main.i
该操作就是将要包含(include)的文件插入原文件中、将宏定义展开、根据条件编译命令选择要使用的代码,最后将这些代码输出到一个“.i”文件中等待进一步处理。
具体为:
1.对源代码文件中文件包含关系(头文件)、预编译语句(宏定义)进行分析和替换,成预编译文件。
2.删除所有的#define,展开所有的宏定义。
3.处理所有的条件预编译指令,如“#if”、“#endif”、“#ifdef”、“#elif”和“#else”。
4.处理“#include”预编译指令,将文件内容替换到它的位置,这个过程是递归进行的,文件中包含其他文件。
5.删除所有的注释,“//”和“/**/”。
6.保留所有的#pragma 编译器指令,编译器需要用到他们,如:#pragma once 是为了防止有文件被重复引用。
7.添加行号和文件标识,便于编译时编译器产生调试用的行号信息,和编译时产生编译错误或警告是能够显示行号。
生成的 main.i 文件内容如下:
# 1 "main.cpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "main.cpp"
# 1 "log.h" 1
#pragma
void log()
{
int log = 5;
}
# 2 "main.cpp" 2
int main()
{
int date = 8;
log();
return 0;
}
2.2 编译
将预处理好的 main.i 编译为汇编文件,汇编文件为 .s 文件。
g++ -S main.i -o main.s
就是把C/C++代码(比如上面的".i"文件)“翻译”成汇编代码
具体为:
1.词法分析:利用类似于“有限状态机”的算法,将源代码程序输入到扫描机中,将其中的字符序列分割成一系列的记号。
2.语法分析:语法分析器对由扫描器产生的记号,进行语法分析,产生语法树。由语法分析器输出的语法树是一种以表达式为节点的树。
3.语义分析:语法分析器只是完成了对表达式语法层面的分析,语义分析器则对表达式是否有意义进行判断,其分析的语义是静态语义——在编译期能分期的语义,相对应的动态语义是在运行期才能确定的语义。
4.优化:源代码级别的一个优化过程。
5.目标代码生成:由代码生成器将中间代码转换成目标机器代码,生成一系列的代码序列——汇编语言表示。
6.目标代码优化:目标代码优化器对上述的目标机器代码进行优化:寻找合适的寻址方式、使用位移来替代乘法运算、删除多余的指令等。
2.3 汇编
g++ -c main.s -o main.o
汇编阶段就是将第二步输出的汇编代码翻译成符合一定格式的机器代码,在Linux系统上一般表现位ELF目标文件(OBJ文件)
具体为:
1.功能:.o是GCC生成的目标文件,除非你是做编译器和连接器调试开发的,否则打开这种.o没有任何意义。二进制机器码一般人也读不了。
2.将编译阶段生成的汇编文件转化成机器码,生成可重定位目标文件。
3.汇编器的汇编过程相对于编译器来说更简单,没有复杂的语法,也没有语义,更不需要做指令优化,只是根据汇编指令和机器指令的对照表一一翻译过来,汇编过程有汇编器as完成。经汇编之后,产生目标文件(与可执行文件格式几乎一样)xxx.o(Linux下)、xxx.obj(Windows下)。
2.4 链接
gcc -o hello hello.o
就是将汇编生成的OBJ文件、系统库的OBJ文件、库文件链接起来,最终生成可以在特定平台运行的可执行程序。
2.5 but
其实这上面的四部用一行指令就能实现
g++ main.cpp -o main
有一点c基础的小伙伴这一章应该不是什么大问题。
3.1 C++变量
里面有讲到一个布尔型变量,之前学c没见到过,所以搜索了一下。
C语言的布尔类型(_Bool)_c语言布尔类型-CSDN博客
在此之前的C语言中,使用整型int来表示真假。在输入时:使用非零值表示真;零值表示假。在输出时:真的结果是1,假的结果是0;(这里我所说的“输入”,意思是:当在一个需要布尔值的地方,也就是其它类型转化为布尔类型时,比如 if 条件判断中的的条件;“输出”的意思是:程序的逻辑表达式返回的结果,也就是布尔类型转化为其他类型时,比如 a==b的返回结果,只有0和1两种可能)。
所以,现在只要你的编译器支持C99(我使用的是Dev C++4.9.9.2),你就可以直接使用布尔型了。另外,C99为了让C和C++兼容,增加了一个头文件stdbool.h。里面定义了bool、true、false,让我们可以像C++一样的定义布尔类型。
P4 C++ 条件与分支(if)_if (ret)-CSDN博客、P5 C++循环(for,while)-CSDN博客
P6 C++控制流语句(continue, break, return)_控制流图遇到return、break、continue-CSDN博客
3.2 C++指针
经常傻傻分不清 * 和 & 的区别,帖子里面的一句话我觉得很能帮助我记忆:
如果我想知道这个变量的内存地址,我可以通过使用 & 运算符来做到这一点,如果我在一个已经存在的变量前面加上一个 & 符号,我们实际上是在问这个变量,嘿,你的内存地址是什么?
所以, & 后面带的是一个变量
3.3 C++引用
给变量取小名在很多函数中很常见。
下面这一种写法是错误的,并不能得到正确的add值。
#include <iostream>
void addNumber(int number1, int number2, int padd)
{
padd = number1+number2; //相加
}
int main()
{
int add = 5;
addNumber(1, 5, add);
std::cout<<add << std::endl; //打印出两数相加后的值
std::cin.get();
return 0;
}
下面这种写法才是正确的。