新书推荐:1.3 第一个程序HelloWorld

本节必须掌握的知识点:

示例一源代码

编译、执行和注释

代码分析

1.3.1 示例一

第一步:新建项目

点击VS菜单“文件”>“添加”>“新建项目”,创建新项目MyProjectOne。接着解决方案管理器窗口看到新建的解决方案“MyProjectOne”。如图1-21所示。   

                        图1-21新建项目

第二步:新建源文件

接着在项目工程中编写第一个程序。选中“源文件”文件夹,点击鼠标右键,选择“添加”>“新建项”,填写源文件名称“OneDay.c”,设置源文件保存路径D:\code\asm_to_c\MyProjectOne\chapter1\helloworld。

第三步:编写源代码

剩下的事情就是编写C语言源代码文件了。源代码编辑窗口输入以下代码。

/*

   显示Hello World!

*/

#include <stdio.h>//C标准库输入输出头文件

int main(void)//函数头

{

    printf("Hello World!");//控制台窗口打印字符串

    return 0;//结束返回

}

提示

1.在编写C语言程序时,注意C语言是区分大小写和全角半角字符的,切记要在半角英文状态下编写代码,不然程序会报错。

2.代码书写过程中,请务必严格按照代码书写的规范编写代码【附录C】,这是对一个合格的程序员最基本的要求!

3.设置项目和源文件保存路径,可以自定义。

4.VS中创建源文件默认后缀名为C++源代码文件后缀“.cpp”,需要修改为C语言源文件后缀名“.c”。

5.C语言采用缩进方式表示上下文从属关系,大括号内的语句块缩进。

6.C语言语句使用分号表示语句结束。

1.3.2 编译、执行和注释

编译

按快捷键F7编译源程序,如图1-22所示。

                        图1-22编译源程序   

如“Hello World!”程序代码所示,通过字符序列创建的程序称为源程序(Source Program)。存放源程序的文件称为源文件(Source File)。在OneDay.c文件中写的字符序列,称为代码(Code)。代码经过预处理、编译、链接,最终生成的二进制可执行exe文件,称为程序(Program)。生成的exe文件是给用户使用的,而源代码是留给程序员自己保留的。                                                  

执行程序

按Ctrl+F5运行程序,会弹出一个黑色窗口,如图1-23所示,控制台窗口输出字符串  

“Hello World!”。我们的第一个程序 “Hello World!”,诞生了!别看代码仅有6行,但它涵盖了很多知识点。

                        图1-23控制台窗口输出结果

通过字符序列创建的程序,需要转化为计算机能够理解的二进制位序列(0和1)。

一般一个程序的完成通常需要经过编写源程序、编译、链接、调试几个步骤,如图1-24所示:                                                                                      

                        图1-24 程序编译链接过程                                                          

注释

    注释可以帮助程序员理解和维护源程序。添加必要的注释是程序员良好习惯之一。在“Hello World!”程序中,使用了多行注释/**/,在C语言程序中/*和*/之间的部分,编译器将中间的文字称为注释。编译源程序时会自动忽略注释内容。

    在C语言中,有两种注释方式:

●第一种注释方式://(单行注释)。如果只有“//”开头的语句,编译器认为在“//”这一行的文字称为注释。

例:

#include <stdio.h>                        

int main(void) 

{

       //单行注释,这一行为注释内容,对程序的运行是不受影响的。

      printf("Hello World!");

      return 0;

}

●第二种注释方式:/**/(多行注释)。如果需要注释的语句有多行,可以用/*和*/,把需要注释的内容写在/*和*/之间。

例:

/*                                     

       多行注释,在/*和*/之间的内容,编译器视为注释。

       在/*和*/之间的内容 ,对程序的运行是不受影响的。                

*/

#include <stdio.h>                        

int main(void) 

{

      printf("Hello World!");

      return 0;

}

 

总结

不管是单行注释“//”还是多行注释“/**/”,在注释里面写的任何内容,它们都不影响代码的运行,注释的作用是为了程序员阅读起来方便,相当于备注的作用。

1.3.3 代码分析

代码框架

我们编写的第一个程序“Hello World!”:

#include <stdio.h>                        

int main(void) 

       printf("Hello World!");

       return 0;

}

去掉代码“printf("Hello World!");”这一行代码,变为如下代码:

#include <stdio.h>                        

int main(void) 

{

       return 0;

}

以上短短的5行代码可以视为代码框架。

头文件

    #include <stdio.h>是一条预处理指令。

’#’为预处理指令前缀。

include是预处理指令,正式编译源程序之前,先做预处理,将stdio.h头文件复制到此处。

<stdio.h>中的’<>’表示此头文件为C标准库定义的头文件,如果是自定义的头文件,则使用双引号,如”help.h”。 stdio.h为标准库输入输出头文件,包含与输入输出相关的函数声明、全局变量、数据类型和常量值。程序“Hello World!”调用的C语言标准库函数printf的原型声明就是在stdio.h头文件中。当编译函数printf时,因为已经在stdio.h头文件中将printf函数声明为外部C标准库函数,因此先将printf函数地址空下,待链接时,从C语言标准库中添加printf函数地址和函数定义。VS环境目录已经包含C标准库头文件目录和lib库目录。

解决方案资源管理器窗口选中项目MyProjectOne,鼠标右键“属性”>“配置属性”>VC++目录,打开VC++目录窗口如图1-25所示,方框内的包含目录即include头文件目录,库目录即lib库目录。

图1-25 C标准库include头文件目录和lib库目录

实验一:屏蔽stdio.h头文件

如果源程序中没有包含“#include <stdio.h>“这条预处理指令,当编译链接时,就无法确定printf函数名的来历,无法识别。

●源代码中屏蔽预处理指令://#include <stdio.h>。

●按F7编译时,底栏输出窗口会有如下错误提示:

1>------ 已启动生成: 项目: MyProjectOne, 配置: Debug Win32 ------

1>OneDay.c

1>d:\code\asm_to_c\myprojectone\chapter1\helloworld\oneday.c(12): warning C4013: “printf”未定义;假设外部返回 int

1>OneDay.obj : error LNK2019: 无法解析的外部符号 _printf,该符号在函数 _main 中被引用

1>D:\code\asm_to_c\MyProjectOne\Debug\MyProjectOne.exe : fatal error LNK1120: 1 个无法解析的外部命令

1>已完成生成项目“MyProjectOne.vcxproj”的操作 - 失败。

========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========

 

结论

编译时,输出窗口提示警告信息:oneday.c(12): warning C4013: “printf”未定义;

表示源代码第12行存在可疑问题,警告代码C4013。

链接时,输出窗口提示错误信息:OneDay.obj : error LNK2019: 无法解析的外部符号 _printf,该符号在函数 _main 中被引用。

表示链接时无法解析外部符号(函数名_printf),此函数名在main函数中被引用。

因为printf函数为外部库函数,如果屏蔽头文件<stdio.h>,缺少printf函数的声明,且找不到printf函数的定义,导致编译器无法解析外部符号函数名_printf,“printf”未定义。

【注】编译后的C语言函数名以下划线开头,如“_printf”、“_main”。

函数头

int main(void)//函数头

头文件下方的下方是main函数,main函数是整个程序的入口函数。我们可以把函数分为函数头和函数体两个部分。我们首先看函数头。

●int表示函数的返回值类型为int类型(32位有符号整数,参见《X86汇编语言基础教程》预备知识部分)。C语言的函数名前为返回值的数据类型,如果函数没有返回值,则类型为“void”类型,表示为空的意思。

●main为函数名。函数名的真实含义是函数定义的起始地址标号,用符号表示函数的地址。main函数名是C语言控制台程序固定的入口函数名,不可以使用其他函数名。

实验二:修改main函数名

如果源程序中没有包含main函数,则链接器因无法解析_main外部符号导致错误。

●源代码中将main函数名修改为hello:int hello(void)//函数头

●按F7编译时,底栏输出窗口会有如下错误提示:

1>------ 已启动生成: 项目: MyProjectOne, 配置: Debug Win32 ------

1>OneDay.c

1>MSVCRTD.lib(exe_main.obj) : error LNK2019: 无法解析的外部符号 _main,该符号在函数 "int __cdecl invoke_main(void)" (?invoke_main@@YAHXZ) 中被引用

1>D:\code\asm_to_c\MyProjectOne\Debug\MyProjectOne.exe : fatal error LNK1120: 1 个无法解析的外部命令

1>已完成生成项目“MyProjectOne.vcxproj”的操作 - 失败。

========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========

 

结论

实验二中,链接时出现错误提示:MSVCRTD.lib(exe_main.obj) : error LNK2019: 无法解析的外部符号 _main,该符号在函数 "int __cdecl invoke_main(void)" (?invoke_main@@YAHXZ) 中被引用。

虽然已经将main函数名修改为hello,但是编译时错误提示信息明确的告知无法解析外部符号_main,即未找到_main函数名。

"int __cdecl invoke_main(void)"表示在调用main函数时需要引用“_main”。

因此,在C语言程序中至少应该包含一个程序入口函数,main函数不可缺省。

●main函数名后圆括号内为函数参数列表,示例一中的main函数参数为“void”,void是空的意思,表示没有参数,void也可以不写:int main()//函数头。

我们可以在微软MSDN网站查阅“main函数和命令行参数”,以下是MSDN网站提供的说明:

*******************************************************************************

main 函数没有声明,因为它内置于语言中。如果有,则 main 的声明语法如下所示:

int main();

int main(int argc, char *argv[]);

函数的原型声明同样以分号结束。如果 main函数中未指定返回值,编译器会提供零作为返回值。注:VS编译器默认缺省整数数据类型为int类型,浮点数据类型为double

标准命令行参数

main 函数参数可以方便进行命令行分析。 argc 和 argv 的类型由编程语言定义。名称 argc 和 argv 是传统名称,也可以按自己的意愿命名。

自变量定义如下所示:

argc
包含 argv 后面的参数计数的整数,表示参数的个数。 argc 参数始终大于或等于 1。

argv

表示由用户输入的命令行参数,以 null 结尾的字符串数组,即命令行参数数组。按照约定,argv[0] 是用于调用程序的命令。 argv[1] 是第一个命令行参数。 命令行的最后一个参数是 argv[argc - 1],并且 argv[argc] 始终为 NULL。

*******************************************************************************

 

总结

C89/C99/C11标准文档中,main函数有两种形式的函数声明:

1.int main();

这是示例一采用的方式。是否还记得,我们在创建项目时,选择的是Windws控制台程序。意味着项目程序采用Windows控制台窗口的形式运行,而不是命令行的方式运行。因此,main函数中不需要命令行参数。

2.int main(int argc, char *argv[]);

第二种函数声明,表示程序采用命令行方式运行。程序运行时操作系统需要先获取命令行参数,然后才可以运行。因此main函数需要带有命令行参数。Unix和Linux操作系统以命令行方式执行程序时,需要采用第二种main函数声明方式。

3.int main(int argc,char *argv[],char *envp[]);

这是main函数的第三种函数声明方式。这种方式比第二种方式多了一个参数envp[],用于获取环境变量,这种形式源于编译器的扩展。全局变量environ可以代替参数envp[]的作用,获取或者设置环境变量可以使用getenv或putenv,因此也没有必要使用该形式,不建议使用。

提示

       本书所有示例都使用C语言Windows控制台程序(创建项目时选择控制台应用程序),而在《Windows API每日一练》一书中创建的都是C语言Windows窗口程序(创建项目时选择桌面应用程序)。

       所谓的控制台窗口,其实是DOS系统文本显示模式的延伸,我们所看到的Windows控制台输出窗口其实就是显存的一部分,可以视为由行和列构成的二维数组。将要输出的内容写入到该显存就可以在控制台窗口显示出来了。关于Windows控制台窗口的设置可以参阅《X86汇编语言教程》第三部分第四十章Win32控制台程序。

函数体

{

              printf("Hello World!");//控制台窗口打印字符串

              return 0;//结束返回

}

函数头下方左右大括号部分的内容就是函数体了。C语言函数以大括号的形式定义语句块。左大括号表示函数开始的位置,右大括号表示函数结束。大括号中间的内容就是语句块。如果语句块内只有一行语句,可以省略大括号(对于初学者而言,不建议省略大括号)。如果语句块内包含多行语句,则不可以省略大括号。

示例一函数体中包含两条语句。

printf("Hello World!");

功能为调用printf函数在控制台窗口打印字符串"Hello World!"。printf为函数名,是C语言标准库中的控制台输出函数,圆括号内的实参是一个常量字符串,”;”表示语句结束。

return 0;//结束返回

表示函数结束,将控制权交还给操作系统,函数返回值为0。示例一OneDay.exe程序的执行过程为:Windows操作系统创建进程OneDay.exe,并从main入口函数开始执行。当main函数执行完毕,退出进程时,将返回值0返回给Windwos操作系统,结束进程。

实验三:屏蔽return 0;

如果源程序中屏蔽return 0;语句,程序仍然可以正常编译运行。

●源代码中屏蔽return 0;语句://return 0;//结束返回

●按Ctrl+F5,程序正常编译运行。

 

结论

main函数中可以缺省return 0;语句。如果 main 函数中未指定返回值,编译器会提供零作为返回值。右大括号表示函数结束返回。

 注意

1.与汇编语言不同,C语言区分大小写,编写源代码时需要特别注意。

2.C语言字符串常量又称为字符串字面量,用双引号,例如"Hello World!"。如果是单个字符,则使用单引号,例如’A’。

3.C语言语句中使用缩进表示上下文从属关系,这一点在后面的示例代码中会做说明。通常大括号内的语句使用缩进增强代码可读性。

4.C语言中,语句使用”;”结束,而代码块使用右大括号结束,左大括号表示语句块开始。

1.3.4 汇编解析

接下来我们把示例一的C语言代码翻译为汇编语言,然后再使用VS的反汇编功能,对比分析手工翻译的汇编代码与VS反汇编的代码有何区别,以此来理解C语言代码的实现和执行过程。

汇编代码

;FileName:OneDay.asm

;例1:示例代码1-第一个程序HelloWorld的汇编代码

;by:bcdaren

;2023.08.15

;===============================

;C标准库头文件和导入库

include vcIO.inc

.data

szMsg db "Hello World!",0

.code

start:

    invoke printf,offset szMsg;控制台窗口打印字符串

    invoke _getch   ;等待输入单个字符

    ret             ;结束返回

end start

●vcIO.inc头文件

;// vc15/17/19IO.inc declarations for standard I/O ,

;// console I/O Function prototypes

;// Copyright (c) FCL1990~2018. All rights reserved.

;// 2023-08-15

.686    ;支持.686及以上指令集

.MODEL flat, stdcall    ;flat平台内存模式,stdcall调用约定

option casemap : none   ;区分大小写

;//========================================

includelib msvcrt.lib   ;C运行时库(简称CRT)

;//========================================

;//  Function prototypes 

   printf   PROTO C : dword,:vararg  ;vararg参数:可变参数,参数个数不确定。

   scanf    PROTO C : dword,:vararg  ;dword参数 :格式字符串的有效地址。

   _getch    PROTO C : vararg         ;【注意】所有的参数都是dword型!

   _kbhit    PROTO C : vararg

   puts PROTO C : dword,:vararg      ;vararg参数:可变参数,参数个数不确定。

   gets PROTO C : dword,:vararg

本书的汇编开发环境采用masm32工具包。但是masm32开发环境只支持Windows API函数头文件,并不包含C语言标准库函数头文件stdio.h,因此我们自定义一个C语言标准库输入输出函数的函数声明头文件vcIO.inc。

vcIO.inc头文件中的“includelib msvcrt.lib”语句包含C运行时.lib文件,编译时链接“本机CRT启动的静态库。

接下来,vcIO.inc头文件中声明了6个C语言标准库中的输入输出函数,分别是printf、scanf、_getch、_kbhit、puts和gets。

printf函数:将格式化后的字符串输出到标准输出设备(控制台窗口)。

scanf函数:按用户指定的格式从键盘上把数据输入到指定的变量之中。

_getch函数:从stdio输入流中读取字符,即从键盘读取一个字符,但不显示在屏幕上。

_kbhit函数:检查当前是否有键盘输入,若有则返回一个非0值,否则返回0。

puts函数:向标准输出设备屏幕(控制台窗口)输出字符串并换行。

gets函数:从标准输入设备stdin(键盘)读取一行,并把它存储到 str 所指向的字符串中。

提示

1.汇编语言头文件后缀名为.inc。

2.“PROTO” 为函数声明伪指令,“C”为调用约定。

3.如果需要使用其他C语言标准库函数,可以在vcIO.inc头文件中添加函数原型声明。

4.本书仅支持32位ANSI C标准库函数。

5.C语言标准库中的函数名以下划线开头,以堆栈方式传递入口参数,返回值存放在eax寄存器中。

6.C语言输入输出分为stdin标准输入流、stdout标准输出流和stderr错误流,默认缓冲区大小为512个字节。

标准输入流stdio

任何一个应用程序都有可能要和一个文件或者设备进行交互,如果程序需要读取设备信息,那么就是通过stdin标准输入流来读取。标准输入流默认是键盘。

标准输出流stdout

stdout其实是一个文件,该文件可以将输出流输出到指定的设备中,比如网卡、打印机。

标准输出流不做任何操作时,默认是显示器的终端。(默认向显示器打印数据)。

标准错误流stderr

stderr无缓冲实时输出错误信息。例如fprintf(stderr, "错误号: %d\n", errno); errno代表的是错误的编号,不同的编号对应不同的错误信息,用于对当前程序中出现的错误进行反馈。可以在errno.h头文件中查阅错误定义。

#define ECHILD 10

#define EAGAIN 11

#define ENOMEM 12

#define EACCES 13

#define EFAULT 14

#define EBUSY 16

#define EEXIST 17

#define EXDEV 18

#define ENODEV 19

#define ENOTDIR 20

7.关于masm32编译环境参见《X86汇编语言基础教程》第二十八章32位汇编学习环境。

●makefile文件

# makefile OneDay.asm

NAME = OneDay

OBJS = $(NAME).obj

ML_FLAG = -c -coff

LINK_FLAG = /subsystem:Console

$(NAME).exe: $(OBJS)

    Link $(LINK_FLAG) $(OBJS)  

.asm.obj:

    ml $(ML_FLAG) $<   

clean:

    del *.obj

编译选项为“-c -coff” :只编译,生成符合公共目标文件格式的目标文件。

链接选项为“/subsystem:Console”生成控制台程序。

●编译

第一步:Win+R键打开管理员命令行提示符,运行cmd.exe。将默认路径C:\Users\16400>切换为当前编译目录D:\code\asm_to_c\MyProjectOne\chapter1\helloworld>。

第二步:输入nmake /a命令,显示以下内容:

Microsoft (R) 程序维护实用工具 14.16.27045.0 版

版权所有 (C) Microsoft Corporation。  保留所有权利。

        ml -c -coff OneDay.asm

Microsoft (R) Macro Assembler Version 6.14.8444

Copyright (C) Microsoft Corp 1981-1997.  All rights reserved.

 Assembling: OneDay.asm

        Link /subsystem:Console OneDay.obj

Microsoft (R) Incremental Linker Version 14.16.27045.0

Copyright (C) Microsoft Corporation.  All rights reserved.

此时,已完成编译链接,并生成OneDay.exe程序。如果编译链接出现错误,会显示相应的提示信息。

●运行结果(略)

●代码分析

    汇编源代码可以分为3个部分:

    第一部分:include vcIO.inc,将vcIO.inc头文件复制到include地址处

    第二部分:数据定义

.data

szMsg db "Hello World!",0

在.data全局数据段中,szMsg地址处已db(字节)为单位定义了一个零结尾的字符串"Hello World!"。

第三部分:代码

.code

start:

    invoke printf,offset szMsg;控制台窗口打印字符串

    invoke _getch   ;等待输入单个字符

    ret             ;结束返回

end start

在.code代码段中,编写汇编指令代码。end start表明程序的入口地址为start地址处,相当于C语言main函数的地址。

第一个invoke是高级汇编语句,调用C标准库中的printf函数,输出szMsg地址处的字符串,offset szMsg是printf函数的参数,为字符串在数据段内的偏移地址。这条汇编语句等价于C语句:

printf("Hello World!");

第二个invoke语句调用C标准库中的另一个函数getch,接收键盘单个字符的输入。按任意键结束程序。调用这个函数是为了方便观察printf函数输出的结果。这条汇编语句等价于C语句:

getchar();

最后一个ret指令结束main函数,并返回。等价语言C语句:return 0;

反汇编代码

在VS源代码编辑窗口鼠标选中main函数,按下F9下断点,或者直接鼠标点击该行最左侧边框下断点,如图1-26所示。

图1-26 VS下断点

按下F5,开始调试。以下是反汇编窗口输出的反汇编内容。

注:反汇编窗口的打开方式,点击VS“调试”>“窗口”>“反汇编”。

    --- d:\code\asm_to_c\myprojectone\chapter1\helloworld\oneday.c -----------------

/*FileName:OneDay.c

;例1:示例代码1-第一个程序HelloWorld

;by:bcdaren

;2023.08.15

;===============================

/*

   显示Hello World!

*/

#include <stdio.h>//C标准库输入输出头文件

int main(void)//函数头

{

1.建立堆栈框架

00D51810  push        ebp 

00D51811  mov         ebp,esp 

00D51813  sub         esp,0C0h 

2.保护寄存器

00D51819  push        ebx 

00D5181A  push        esi 

00D5181B  push        edi 

3.初始化函数堆栈空间

00D5181C  lea         edi,[ebp-0C0h] 

00D51822  mov         ecx,30h 

00D51827  mov         eax,0CCCCCCCCh 

00D5182C  rep stos    dword ptr es:[edi] 

00D5182E  mov         ecx,offset _F2A4B1DC_oneday@c (0D5C003h) 

00D51833  call        @__CheckForDebuggerJustMyCode@4 (0D51212h) 

4.printf("Hello World!");//控制台窗口打印字符串

00D51838  push        offset string "Hello World!" (0D57B30h) 

00D5183D  call        _printf (0D5104Bh) 

00D51842  add         esp,4 

5.return 0;//结束返回

00D51845  xor         eax,eax  //返回值0

}

6.恢复寄存器

00D51847  pop         edi 

00D51848  pop         esi 

00D51849  pop         ebx 

7.释放堆栈框架

00D5184A  add         esp,0C0h 

00D51850  cmp         ebp,esp 

00D51852  call        __RTC_CheckEsp (0D5121Ch)  //校验堆栈

00D51857  mov         esp,ebp 

00D51859  pop         ebp 

00D5185A  ret 

    上述内容是VS编译器将C语言代码翻译成汇编后的代码,称为反汇编代码。我们可以把上述的反汇编代码分为7个部分,其中对应C语言函数体的部分为第4部分。

    ●反汇编与汇编代码比较

1.汇编指令

反汇编代码中的每一条指令对应一条机器指令,体现了代码的整个执行过程。

汇编代码采用高级汇编伪指令,一条invoke伪指令可以分解为多条汇编指令。例如:

invoke printf,offset szMsg;控制台窗口打印字符串

等价于

00D51838  push        offset string "Hello World!" (0D57B30h) 

00D5183D  call        _printf (0D5104Bh)

2.反汇编代码包含完整的堆栈框架,而汇编代码采用段的简化定义形式,省略了堆栈框架和寄存器保护,由编译器在编译时自动添加堆栈框架和寄存器保护。

3.反汇编代码仅包含代码段的内容,而汇编代码包含完整的源代码,包括头文件、数据段和代码段的定义。

4、反汇编代码中包含了编译器的优化代码,例如反汇编代码的第3部分初始化堆栈空间和第7部分释放堆栈框架时的堆栈校验以及恢复堆栈平衡。

●反汇编与C语言比较

    我们把C语言称为高级语言,意思为更接近人类的语言,编写C语言源代码的效率更高,更为简洁。我们可以将C语言称为高级汇编语言,是高级汇编的进一步简化。而C语言的实现都是建立在功能更为强大的编译器的基础上的。C语言由编译器将其翻译成汇编语言,然后再通过汇编器将其翻译为二进制可以执行程序。因此,可以将C语言视为在汇编语言基础上实现的。反汇编代码就是VS编译器将C语言翻译为汇编语言的代码。

C++、JAVA、Python等其他高级语言又是建立在C语言的基础上的。在计算机领域,几乎所有的基础软件都是由C语言实现的,C语言是各种不同计算机平台的通用语言。因此,在C语言基础上创建的其他高级语言当然可以实现跨平台的通用性。

因此,学习C语言是学习其他高级语言的有效途径,而本书从汇编语言的角度学习C语言,则更透彻的理解C语言的实现过程和本质。对于渴望追求真理的读者,建议先学习《X86汇编语言基础教程》,然后再学习C语言,这样对C语言的理解更为深刻,学习的效率也更高。

练习

  1. 在控制台窗口中实现“Hello World!”,熟记“Hello World!”程序代码。
  2. 自己编写程序在控制台窗口中显示“我的第一个程序,Hello World!诞生了”。

本文摘自编程达人系列教材《汇编的角度——C语言》。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值