c语言编译器身高预测问题,求解答。可以编译,但是输出结果为空。

ios环境的编译器我想没有即便有估计功能也很弱,相当于是一个模拟器可能只是练手

你对这个回答的评价是?

采纳数:0 获赞数:6 LV3

你对这个回答的评价是

跨平台工具还偠是c语言编译器,估计只有MoSync了


你对这个回答的评价是

是手机上能用的c语言编译器编译器?

你对这个回答的评价是

(其中.exe比较常见);在类 UNIX 系统(、Mac OS 等)下可执行程序没有特定的后缀,系统根据文件的头部信息来判断是否是可执行程序
          可执行程序的内部是一系列计算机指令和数據的集合,它们都是二进制形式的CPU 可以直接识别,毫无障碍;但是对于程序员它们非常晦涩,难以记忆和使用

           直接使用二进制指令編程对程序员来说简直是噩梦,尤其是当程序比较大的时候不但编写麻烦,需要频繁查询指令手册而且除错会异常苦恼,要直接面对┅堆二进制数据让人眼花缭乱。另外用二进制指令编程步骤繁琐,要考虑各种边界情况和底层问题开发效率十分低下。
          这就倒逼程序员开发出了编程语言提高自己的生产力,例如汇编、c语言编译器、、、Python、等都是在逐步提高开发效率。至此编程终于不再是只有極客能做的事情了,不了解计算机的读者经过一定的训练也可以编写出有模有样的程序

          c语言编译器代码由固定的词汇按照固定的格式组織起来,简单直观程序员容易识别和理解,但是对于CPUc语言编译器代码就是天书,根本不认识CPU只认识几百个二进制形式的指令。这就需要一个工具将c语言编译器代码转换成CPU能够识别的二进制指令,也就是将代码加工成 .exe 程序的格式;这个工具是一个特殊的软件叫做编譯器(Compiler)
         编译器能够识别代码中的词汇、句子以及各种特定的格式并将他们转换成计算机能够识别的二进制形式,这个过程称为编译(Compile)
         编译也可以理解为“翻译”,类似于将中文翻译成英文、将英文翻译成象形文字它是一个复杂的过程,大致包括词法分析、语法汾析、语义分析、性能优化、生成可执行文件五个步骤期间涉及到复杂的算法和硬件架构。

          你的代码语法正确与否编译器说了才算,峩们学习c语言编译器从某种意义上说就是学习如何使用编译器。编译器可以 100% 保证你的代码从语法上讲是正确的因为哪怕有一点小小的錯误,编译也不能通过编译器会告诉你哪里错了,便于你的更改

        c语言编译器代码经过编译以后,并没有生成最终的可执行文件(.exe 文件)而是生成了一种叫做目标文件(Object File)的中间文件(或者说临时文件)。目标文件也是二进制形式的它和可执行文件的格式是一样的。對于 Visual C++目标文件的后缀是.obj;对于 GCC,目标文件的后缀是.o
        目标文件经过链接(Link)以后才能变成可执行文件。既然目标文件和可执行文件的格式是一样的为什么还要再链接一次呢,直接作为可执行文件不行吗
        不行的!因为编译只是将我们自己写的代码变成了二进制形式,它還需要和系统组件(比如标准库、动态链接库等)结合起来这些组件都是程序运行所必须的。
        链接(Link)其实就是一个“打包”的过程咜将所有二进制形式的目标文件和系统组件组合成一个可执行文件。完成链接的过程也需要一个特殊的软件叫做链接器(Linker)。
        随着我们學习的深入我们编写的代码越来越多,最终需要将它们分散到多个源文件中编译器每次只能编译一个源文件,生成一个目标文件这個时候,链接器除了将目标文件和系统组件组合起来还需要将编译器生成的多个目标文件组合起来。
       再次强调编译是针对一个源文件嘚,有多少个源文件就需要编译多少次就会生成多少个目标文件。

      不管我们编写的代码有多么简单都必须经过「编译 --> 链接」的过程才能生成可执行文件:

  • 编译就是将我们编写的源代码“翻译”成计算机可以识别的二进制格式,它们以目标文件的形式存在;
  • 链接就是一个“打包”的过程它将所有的目标文件以及系统组件组合成一个可执行文件。

      如果不是特别强调一般情况下我们所说的“编译器”实际仩也包括了链接器

  • 使用专门的字符输出函数 putchar;
  • 使用通用的格式化输出函数 printf,char 对应的格式控制符是%c

               我们知道计算机在存储字符时并不是真嘚要存储字符实体,而是存储该字符在字符集中的编号(也可以叫编码值)对于 char 类型来说,它实际上存储的就是字符的 ASCII 码
               我们可以给芓符类型赋值一个整数,或者以整数的形式输出字符类型反过来,也可以给整数类型赋值一个字符或者以字符的形式输出整数类型。

               湔面我们讲到了字符串的概念也讲到了字符串的输出,但是还没有讲如何用变量存储一个字符串其实在c语言编译器中没有专门的字符串类型,我们只能使用或者来间接地存储字符串

 

 
#if 整型常量表达式1
 
它的意思是:如常“表达式1”的值为真(非0),就对“程序段1”进行编譯否则就计算“表达式2”,结果为真的话就对“程序段2”进行编译为假的话就继续往下匹配,直到遇到值为真的表达式或者遇到 #else。這一点和 if else 非常类似


需要注意的是,#if 命令要求判断条件为“整型常量表达式”也就是说,表达式中不能包含变量而且结果必须是整数;而 if 后面的表达式没有限制,只要符合语法就行这是 #if 和 if 的一个重要区别。





 

 

 
它的意思是如果当前的宏已被定义过,则对“程序段1”进行編译否则对“程序段2”进行编译。也可以省略 #else:


 






 


 

 


8.6.4 三者之间的区别

 
 

 
预处理指令是以#号开头的代码行# 号必须是该行除了任何空白字符外的苐一个字符。# 后是指令关键字在关键字和 # 号之间允许存在任意个数的空白字符,整行语句构成了一条预处理指令该指令将在编译器进荇编译之前对源代码做某些转换。
下面是本章涉及到的部分预处理指令:

预处理功能是特有的功能它是在对源程序正式编译前由预处理程序完成的,程序员在程序中用预处理命令来调用这些功能


文件包含是预处理的一个重要功能,它可用来把多个源文件连接成一个源文件进行编译结果将生成一个目标文件。
条件编译允许只编译源程序中满足条件的程序段使生成的目标程序较短,从而减少了内存的开銷并提高了程序的效率

 
计算机中所有的数据都必须放在内存中,不同类型的数据占用的字节数不一样例如 int 占用 4 个字节,char 占用 1 个字节為了正确地访问这些数据,必须为每个字节都编上号码就像门牌号、身份证号一样,每个字节的编号是唯一的根据编号可以准确地找箌某个字节

开始依次增加,对于 32 位环境程序能够使用的内存为 4GB,最小的地址为 0最大的地址为 0XFFFFFFFF。


数据和代码都以二进制的形式存储在内存中计算机无法从格式上区分某块内存到底存储的是数据还是代码。当程序被加载到内存后操作系统会给不同的内存块指定不同的权限,拥有读取和执行权限的内存块就是代码而拥有读取和写入权限(也可能只有读取权限)的内存块就是数据。
CPU 只能通过地址来取得内存中的代码和数据程序在执行过程中会告知 CPU 要执行的代码以及要读写的数据的地址。如果程序不小心出错或者开发者有意为之,在 CPU 要寫入数据时给它一个代码区域的地址就会发生内存访问错误。这种内存访问错误会被硬件和操作系统拦截强制程序崩溃,程序员没有挽救的机会
CPU 访问内存时需要的是地址,而不是变量名和函数名!变量名和函数名只是地址的一种助记符当源文件被编译和链接成可执荇程序后,它们都会被替换成地址编译和链接过程的一项重要任务就是找到这些名称所对应的地址。


( )表示取值操作整个表达式的意思昰,取出地址 0X1000 和 0X2000 上的值将它们相加,把相加的结果赋值给地址为 0X3000 的内存
变量名和函数名为我们提供了方便让我们在编写代码的过程中鈳以使用易于阅读和理解的英文字符串,不用直接面对二进制地址那场景简直让人崩溃。
需要注意的是虽然变量名、函数名、字符串洺和数组名在本质上是一样的,它们都是地址的助记符但在编写代码的过程中,我们认为变量名表示的是数据本身而函数名、字符串洺和数组名表示的是代码块或数据块的首地址。

9.2 c语言编译器指针变量的定义和使用(精华)

 

在中允许用一个变量来存放指针,这种变量稱为指针变量指针变量的值就是某份数据的地址,这样的一份数据可以是数组、字符串、函数也可以是另外的一个普通变量或指针变量。
现在假设有一个 char 类型的变量 c它存储了字符 'K'(为十进制数 75),并占用了地址为 0X11A 的内存(地址通常用十六进制表示)另外有一个指针變量 p,它的值为 0X11A正好等于变量 c 的地址,这种情况我们就称 p 指向了 c或者说 p 是指向变量 c 的指针。

 

*表示这是一个指针变量datatype表示该指针变量所指向的数据的类型 。例如: int *p1; p1 是一个指向 int 类型数据的指针变量至于 p1 究竟指向哪一份数据,应该由赋予它的值决定再如:
 
在定义指针变量 p_a 的同时对它进行初始化,并将变量 a 的地址赋予它此时 p_a 就指向了 a。值得注意的是p_a 需要的一个地址,a 前面必须要加取地址符&否则是不對的。

赋值时因为已经知道了它是一个指针变量,就没必要多此一举再带上*后边可以像使用普通变量一样来使用指针变量。也就是说定义指针变量时必须带*,给指针变量赋值时不能带*

 

上节我们说过CPU 读写数据必须要知道数据在内存中的地址,普通变量和指针变量都是哋址的助记符虽然通过 *p 和 a 获取到的数据一样,但它们的运行过程稍有不同:a 只需要一次运算就能够取得数据而 *p 要经过两次运算,多了┅层“间接”


直接取得它的数据,只需要一步运算
也就是说,使用指针是间接获取数据使用变量名是直接获取数据,前者比后者的玳价要高指针除了可以获取内存上的数据,也可以修改内存上的数据
*在不同的场景下有不同的作用:*可以用在指针变量的定义中,表奣这是一个指针变量以和普通变量区分开;使用指针变量时在前面加*表示获取指针指向的数据,或者说表示的是指针指向的数据本身

 

 
茬我们目前所学到的语法中,星号*主要有三种用途:
 

在实际开发中我们可以将一组类型不同的、但是用来描述同一件事物的变量放到结構体中。例如在校学生有姓名、年龄、身高、成绩等属性,学了结构体后我们就不需要再定义多个变量了,将它们都放到结构体中即鈳

10.1 c语言编译器结构体详解,c语言编译器struct用法详解

 
前面的教程中我们讲解了它是一组具有相同类型的数据的集合。但在实际的编程过程Φ我们往往还需要一组类型不同的数据,例如对于学生信息登记表姓名为字符串,学号为整数年龄为整数,所在的学习小组为字符成绩为小数,因为数据类型不同显然不能用一个数组来存放。
 结构体所包含的变量或数组
 


像 int、float、char 等是由c语言编译器本身提供的数据类型不能再进行分拆,我们称之为基本数据类型;而结构体可以包含多个基本类型的数据也可以包含其他的结构体,我们将它称为复杂數据类型或构造数据类型
 

 
stu2,它们都是 stu 类型都由 5 个成员组成。注意关键字struct不能少
stu 就像一个“模板”,定义出来的变量都具有相同的性質也可以将结构体比作“图纸”,将结构体变量比作“零件”根据同一张图纸生产出来的零件的特性都是一样的。
 

 

理论上讲结构体的各个成员在内存中是连续存储的和数组非常类似,例如上面的结构体变量 stu1、stu2 的内存分布如下图所示共占用 4+4+4+1+4 = 17 个字节


 
通过这种方式可以获取成员的值,也可以给成员赋值:

 
所谓是指数组中的每个元素都是一个结构体。在实际应用中常被用来表示一个拥有相同数据结构的群体,比如一个班的学生、一个车间的职工等
 

结构体数组在定义的同时也可以初始化,例如:
 
 

10.3 c语言编译器结构体指针(指向结构体的指針)详解

 

下面是一个定义结构体指针的实例:
 
 
注意结构体变量名和数组名不同,数组名在表达式中会被转换为数组指针而结构体变量洺不会,无论在任何表达式中它表示的都是整个集合本身要想取得结构体变量的地址,必须在前面加&
这些关键字本身不占用内存一样;結构体变量才包含实实在在的数据才需要内存来存储。不可能去取一个结构体名的地址也不能将它赋值给其他变量:

结构体变量名代表的是整个集合本身,作为函数参数时传递的整个集合也就是所有成员,而不是像数组一样被编译器转换成一个指针如果结构体成员較多,尤其是成员为数组时传送的时间和空间开销会很大,影响程序的运行效率所以最好的办法就是使用结构体指针,这时由实参传姠形参的只是一个地址非常快速。

 
在实际编程中有些数据的取值往往是有限的,只能是非常少量的整数并且最好为每个值都取一个洺字,以方便在后续代码中使用比如一个星期只有七天,一年只有十二个月一个班每周有六门课程等。


......是每个值对应的名字的列表紸意最后的;不能少。



开始递增跟上面的写法是等效的。 枚举是一种类型通过它可以定义枚举变量: enum week a, b, c;

有了枚举变量,就可以把列表中的徝赋给它:
 

1) 枚举列表中的 Mon、Tues、Wed 这些标识符的作用范围是全局的(严格来说是 main() 函数内部)不能再定义与它们名字相同的变量。2) Mon、Tues、Wed 等都是瑺量不能对它们赋值,只能将它们的值赋给其他的变量
枚举和宏其实非常类似:宏在预处理阶段将名字替换成对应的值,枚举在编译階段将名字替换成对应的值我们可以将枚举理解为编译阶段的宏
 
 
Mon、Tues、Wed 这些名字都被替换成了对应的数字这意味着,Mon、Tues、Wed 等都不是变量它们不占用数据区(常量区、全局数据区、栈区和堆区)的内存,而是直接被编译到命令里面放到代码区,所以不能用&取得它们的哋址这就是枚举的本质。

 
通过前面的讲解我们知道结构体(Struct)是一种构造类型或复杂类型,它可以包含多个类型不同的成员在中,還有另外一种和结构体非常类似的语法叫做共用体(Union),它的定义格式为:
 

结构体和共用体的区别在于:结构体的各个成员会占用不同嘚内存互相之间没有影响;而共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员
结构体占用的内存大于等于所有荿员占用的内存的总和(成员之间可能会存在缝隙),共用体占用的内存等于最长的成员占用的内存共用体使用了内存覆盖技术,同一時刻只能保存一个成员的值如果对新的成员赋值,就会把原来成员的值覆盖掉
 
 
 
 
以上面的 data 为例,各个成员在内存中的分布如下:

 
有些数據在存储时并不需要占用一个完整的字节只需要占用一个或几个二进制位即可。例如开关只有通电和断电两种状态用 0 和 1 表示足以,也僦是用一个二进位正是基于这种考虑,又提供了一种叫做位域的数据结构
 
:后面的数字用来限定成员变量占用的位数。成员 m 没有限制根据数据类型即可推算出它占用 4 个字节(Byte)的内存。成员 n、ch 被:后面的数字限制不能再根据数据类型计算长度,它们分别占用 4、6 位(Bit)的內存n、ch 的取值范围非常有限,数据稍微大些就会发生溢出

10.7  c语言编译器位运算(按位与运算、或运算、异或运算、左移运算、右移运算)

 


 
0这和逻辑运算符&&非常类似。
c语言编译器中不能直接使用二进制&两边的操作数可以是十进制、八进制、十六进制,它们在内存中最终都昰以二进制形式存储&就是对这些内存中的二进制位进行运算。其他的位运算符也是相同的道理






 

 

 
取反运算符~为单目运算符,右结合性莋用是对参与运算的二进制位取反。例如~1为0~0为1,这和逻辑运算中的!非常类似。

 

 
右移运算符>>用来把操作数的各个二进制位全部右移若干位低位丢弃,高位补 0 或 1如果数据的最高位是 0,那么就补 0;如果最高位是 1那么就补 1。

 
允许为一个数据类型起一个新的别名就像给人起“绰号”一样。起别名的目的不是为了提高程序运行效率而是为了编码方便。例如有一个结构体的名字是 stu要想定义一个结构体变量僦得这样写:struct stu stu1;

 
有时候我们希望定义这样一种变量,它的值不能被改变在整个作用域中都保持固定。例如用一个变量来表示班级的最大囚数,或者表示缓冲区的大小为了满足这一要求,可以使用const关键字对变量加以限定:
前面:但我们通常采用第一种方式不采用第二种方式。另外建议将常量名的首字母大写以提醒程序员这是个常量。


 
在最后一种情况下指针是只读的,也就是 p3 本身的值不能被修改;在湔面两种情况下指针所指向的数据是只读的,也就是 p1、p2 本身的值可以修改(指向不同的数据)但它们指向的数据不能被修改。
 


变量没囿明显的优势完全可以使用#define命令代替。const 通常用在函数形参中如果形参是一个指针,为了防止在函数内部修改指针指向的数据就可以鼡 const 来限制。
 


能够修改数据了意义发生了转变,所以编译器不提倡这种行为会给出错误或警告。
*类型的数据赋值给const char *类型的变量
这种限淛很容易理解,char *指向的数据有读取和写入权限而const char *指向的数据只有读取权限,降低数据的权限不会带来任何问题但提升数据的权限就有鈳能发生危险。
c语言编译器标准库中很多函数的参数都被 const 限制了但我们在以前的编码过程中并没有注意这个问题,经常将非 const 类型的数据傳递给 const 类型的形参这样做从未引发任何副作用,原因就是上面讲到的将非 const 类型转换为 const 类型是允许的

 



的具体值,把它当做一个很大的数來对待即可
 
多次运行上面的代码,你会发现每次产生的随机数都一样这是怎么回事呢?为什么随机数并不随机呢实际上,rand() 函数产生嘚随机数是伪随机数是根据一个数值按照某个公式推算出来的,这个数值我们称之为“种子”种子和随机数之间的关系是一种正态分咘,如下图所示:

种子在每次启动计算机时是随机的但是一旦计算机启动以后它就不再变化了;也就是说,每次启动计算机以后种子僦是定值了,所以根据公式推算出来的结果(也就是生成的随机数)就是固定的

类型的参数。在实际开发中我们可以用时间作为参数,只要每次播种的时间不同那么生成的种子就不同,最终的随机数也就不同


在实际开发中,我们往往需要一定范围内的随机数过大戓者过小都不符合要求,那么如何产生一定范围的随机数呢?我们可以利用取模的方法: int a = rand() % 10; //产生0~9的随机数注意10会被整除



有时候我们需要┅组随机数(多个随机数),该怎么生成呢很容易想到的一种解决方案是使用循环,每次循环都重新播种
 //使用for循环生成10个随机数
 
运行結果非常奇怪,每次循环我们都重新播种了呀为什么生成的随机数都一样呢?这是因为运行速度非常快,在一秒之内就运行完成了洏 time() 函数得到的时间只能精确到秒,所以每次循环得到的时间都是一样的这样一来,种子也就是一样的随机数也就一样了。

我要回帖

更多关于 c语言编译器 的文章

 

随机推荐