- 0、安装环境
- 1、C语言的第一个程序
- 2、C语言的编译过程
- 3、变量
- 4、常量
- 4.1、const
- 4.2、宏
- 5、数据类型的概念,
C语言只可以修改内存,汇编还可以操作寄存器。
本系列文章不适合纯小白看,更适合有一定编程基础来看
0、安装环境gcc安装参考, https://blog.csdn.net/jiqiren_dasheng/article/details/103775488 Visual Studio 2019运行C参考,(推荐2019版本) https://zhuanlan.zhihu.com/p/132445107
快捷键进行注释与接触注释:
注释 : Ctrl + k + c 取消注释 : Ctrl + k + u1、C语言的第一个程序
软件直接安装VC, 新建选择空项目,项目名称任意,
稍微改一下后缀,
先操作一个显存,输出一个最经典的hi,
std 是标准的意思, io 就是流(输入、输出) h 就是头的意思,
然后,调用printf函数来直接做输出, 接着使用main函数将printf函数包含,告诉操作系统,这个是入口。 这个main函数,可以有返回值,也可以没有返回值。 没有返回值,就在main函数之前加一个 void,
最终代码如下,点击调试器即可。
可以看到程序执行完毕之后,直接退出。
接着我们想要将电脑的计算器程序打开,就可以直接使用system函数,
当然,system函数,也需要导入一些库,即标准的lib库,具体代码如下:
当然也可以,将程序打印完内容之后“暂停”,
运行程序,这个输出完指定内容之后,直接暂停并没有和之前一样结束。
接着我们来研究C语言程序的编译过程, 将上述的代码保存到test.c文件内。 cmd之后输入gcc -v, 这个可能需要配置环境变量,
使用命令将test.c编译,
C语言编译的第一步被称为“预编译”,这个预编译都干什么?
1、将头文件展开,即导入的stdio.h、stdlib.h具体函数全部导入程序 2、宏定义替换 3、删除代码中的注释
第二是,宏定义替换(将变量替换为具体的值)
即预编译的第二步,将第五行的“乃子”替换为100
当然,在实际的测试中,gcc的编译不支持变量名称用中文定义。
将代码换下,“乃子”换为“a”
继续使用gcc编译test.c,可以看到最后这个a已经变为了100
同时看到注释也被删除
完成预编译之后,就是将代码编译为汇编语言了,
使用以下命令,将编译后的文件保存到test.s文件内,
打开test.s文件,这个汇编可能与部分同学学习的汇编感觉不一样呢?
其实只是语法的不同,该汇编语法是贝尔实验室的汇编语法(AT&T汇编)
该汇编语法常用于Linux上边,一般Linux逆向都会使用到该语法。 多说一点,在win系统上常用的汇编为win32
最后一步是二进制的转换,直接“-c”即可,
会生成一个“O文件”,
打开内容看看,
转一下16进制,
转化之后,
但是这种“O文件”还是操作系统无法直接执行的。执行是要操作系统把对应的二进制交给CPU,才能执行, 即必须变为操作系统认识的“样子”,在win系统上就是“pe”文件,即“.exe”
这个将“O文件”变为操作系统可识别的过程被称为“链接阶段” 直接什么参数都不加,就直接变为可执行的程序了,
双击生成的“exe”文件,
小结:
C语言的编译分为4步, 预编译(具体作用参考上边) 编译(将预编译的结果编译为汇编) 二进制转换(将汇编转换为二进制文本) 链接阶段(将二进制变为操作系统可识别的程序)3、变量
变量的本质是一片连续的内存,比如: int a =100 、、代表可以通过“a”这个“符号名”访问到这个“100”的内存,
代码运行看看,输出a的内存地址之后,打个断点,
将a的地址打印输出,
将内存地址复制出来,查找一下;
可以先调整为“4字节整数”,
第一个是16进制的64,转化为10进制就是100,
假设我们将,这个“64”改为 别的值(实际测试这个VC貌似没有找到修改的地方),
再次打印a的地址,这个地址是会变化的。
4、常量 4.1、constC语言,正常的定义常量的方式,是在变量之前加一个“const”, 这样下边假设再有想改变a变量的参数如a“ = 50”就会直接报语法错误,
第八行的,“%d”是以整数输出,变量a的值。
然而,这种“const”定义的常量并不是真正意义上的常量,是可以被修改的。
通过下边“修改内存”的间接方式就可以达到修改变量a的值的效果, (&a),取出a变量的地址, *符号,根据内存地址取出内容, a变量是int类型的,这里要进行类型转换,即(int*)的作用
直接输出40,
怎么样的常量算是“真正的”常量内,即"define"关键字定义的,
通过define关键字定义的常量,在取地址的第一步就会报语法错误,
题外话--------以下内容个人理解
这个原因是什么, 首先要明白,a=100的本质, a会被分配一块内存,但是这个100是寄存器生成的,然后把生成的100放到了a的内存。 修改a的值,即寄存器生成一个新的值50放到内存中,然后让a指向这个新的50 因为取地址是取得a的地址,所以就会造成a这个变量的值可以随意变化, 但是define是直接取得“100”的内存(跨过了a这个“中间商”) 当然,这个define只能是C语言中,真正的常量,还是可以通过汇编达到修改a的效果。 主要是C语言不能操作寄存器,4.2、宏
题外话
define被称为“宏定义”,这个“宏”是什么含义呢 这个宏,其实是被直接翻译过来的,本质是“macro”, 其作用就是“缩短代码”,其实这个作用和“传统意义上的函数的作用一致”, define a = asdasdasdaa很多很多,很长很长的指令asdasdasads 假设下边很多地方都要使用到这个“asdasdasdaa很多很多,很长很长的指令asdasdasads” 那在之后想修改程序要修改很多的地方,但是经过“宏定义”之后,仅仅修改a后边的值, 别的调用a值的地方,自然会跟着修改。 一个问题是,不是说define定义的不能被修改吗,这怎么又提到修改了, 不能被修改是程序执行的修改如: define a = 100 a = 50 但是这里的修改是说,直接改代码,即 define a =100 改为 define a =50 小结: 一句话概括“宏”:起到压缩指令长度的作用5、数据类型的概念,
%f代表以浮点型(float)的格式输出,
但是这样输出,输出的是“0.0000”
类似的,修改下图的两处,输出的也是0
再次稍微修改,直接输出了“负数”,
来简单的说下原理,
printf不会自动进行类型转换。想要输出比较接近的结果,可是手动进行类型转换。
这样输出的是100,



