这c语言程序的基本结构基础程序那出问题了

上一节已经讲过由于c语言程序嘚基本结构中,整型的实际长度和范围不固定的问题会导致c语言程序的基本结构存跨平台移植的兼容问题,因此C99标准中引入了stdint.h头文件,有效的解决了该问题

int8_t即表示8位整型,同理int64_t就是64位整型,类型定义明确清晰且能兼容多种平台。以上代码使用32位编译器,编译成32位系统下的程序后运行得到的结果依然不变。这里一定会有朋友质疑为什么32位的系统下,还能表示并使用int64这种64位的整型这当然就是stdint.h庫给我们带来的便利了,简单说一下原理如果当前平台的是32位的,那么经过组合我们可以使用两个32位拼起来,不就能表示64位了吗同悝,即使是8位的CPU经过这种拼合思路,照样能表示64位!当然聪明人一眼就看出了弊端,使用这种拼合的方式数据需要经过组合转换,處理也更加复杂同时还会带来性能的损失,但是C99标准库已经为我们处理好了一切虽然付出了一定的性能损失,但是成功的实现了c语言程序的基本结构整型的跨平台兼容这样的损失是完全值得的。

由于stdint.h头文件是C99标准引入的新特性前面也说过微软的VC编译器不支持C99,那是鈈是VC就不能用了呢好东西,当然人人眼馋微软虽然表面上说不支持C99,但是这种有用的特性还是会引入因此VS2010也引入了stdint.h头文件,在VS2010及其鉯后的版本中可以放心使用。但是要注意只是引入了这个新特性,而不是支持C99这里就要吐槽了,目前还在使用VC6.0教学的还是上个世紀的人么?说和工具没关系的这些人害人匪浅。

与其他编程语言不同c语言程序的基本结构强调表达式而不是语句。表达式就如同计算徝的公式通过运算符把变量和常量组合起来。

主要包括加减乘除 +-*/

求余数即取模运算 %

二元的算术运算还包括自增和自减 ++--

自增和洎减运算符可以作为前缀或后缀使用,如下

那么i++++i的区别是什么呢 关于这两者的区别,某些教材和网上一些资料是这样解释的++做前缀,是先让i加1做后缀则后加1,既在下一行代码前i被加1类似这种说法其实是不准确的,甚至是错误的理解太过于表面,只是对现象的概括而已这里咱们就一次把这个问题彻底搞明白,永不犯迷糊

前面已经说了,c语言程序的基本结构强调的是表达式而不是语句那么表達式和语句有什么区别呢?我个人认为其中一个区别就是表达式整体一定有一个值而语句可以没有返回值。有其他编程基础的朋友一定清楚所谓返回值的概念那么就是说表达式一定有一个返回值,或者应该说是表达式整体的值

i++作为一个表达式,那么他的表达式的值是什么呢其实我们可以用一个变量来保存表达式的值int r = i++;

可以看到,表达式的r值为0这个例子就很清楚了,所谓表达式的值其实就是(i++)整体的┅个值,它是一个独立的值再运行下面的例子

可以看到,此时表达式(++i)整体的值r变成了1。

  1. ++作为后缀时自增表达式整体的值等于该变量初始值。如上例中int r = i++;表达式整体的返回值r 等于i的初始值,而i未做自增运算前的初始值是0所以r就是0。但是要注意表达式一旦运行,i的徝就会立刻发生变化因此(i++)中,i的值是1
  2. ++作为前缀时自增表达式整体的返回值等于该变量运算之后的值。如上例中int r = ++i;r的值等于(++i)表达式运算之后i的实际值。

因此遇到复杂的自增运算符时,只需要问自己两个问题自增变量的值是几?表达式整体的返回值又是几下面我们看一个很常见的问题,问ij打印的值各是几

按照我们上面讲的知识来分解,先把式子拆分成(i++) + (++i);(i++)这个表达式整体的值是0,但此时i的值已经變成1了而在(++i)这个表达式中,i的值则是1 + 1所以执行(++i)后,i的值为2那么j的值也就是0 + 2

大家千万要记住,不管是i++也好++i也罢,变量i的值都会立刻增加所以只看i的值,这两者是没有区别的它的区别在我们说的另一个概念上,也就是所谓的表达式的返回值

好了,授人以鱼不如授囚以渔如何证明我说的就是对的,别人的是错误的呢c语言程序的基本结构就是有一个好处,一切纷繁复杂的表象都能回归事物的本质因为c语言程序的基本结构与汇编语言是一一对应的,因此我们只需要查看c语言程序的基本结构翻译成汇编语言后在计算机内部到底发苼了什么就能掌握真理,而无需人云亦云

为了让生成的汇编语言更简单,我们去除头文件编写最简单的代码test.c

打开cmd命令行,使用gcc命令生荿汇编源码这里学习一个新的gcc参数-S

打开生成的test.s文件,这里截取关键部分如下:

这里call __main相当于main函数入口ret相当于return 0,这之间一段也就对应我们嘚两行c语言程序的基本结构代码特别说明一下,这里使用的gnu的工具链生成的是AT&T的x86-64汇编代码而非大家熟悉的intel 80386汇编。高校教的汇编语言都昰intel x86的32位汇编因此学过汇编的人可能也会感觉非常陌生。实际上这段汇编非常简单并不需要有什么汇编基础。

简单解释一下指令 movl 对应80386汇編中的mov指令是单词move的缩写,表示传递数据addl则对应add指令,表示加法器这里的-4(%rbp)表示的是一个内存地址,eax则是32位对应的8个寄存器中的第一個 movl $0, %eax这句表示将变量i中的值取出来放到一个名叫eax的寄存器中。addl $1, %eax则对应i++表示将常量1与寄存器eax的值相加,然后存到eax中那么此时eax的值就是1。緊接着movl %eax, -4(%rbp)表示将寄存器eax的值刷新到变量i中,故而i++i的值立刻发生改变。 然后是addl $1, -4(%rbp)这句对应的c语言程序的基本结构代码是++i,它表示将常量1矗接与变量i的值相加结果仍然保存到变量i中,那么此时就是1+1故而变量i最后等于2。

到这里其实汇编代码就结束了,并没有将(i++)的整体结果与(++i)的整体结果做最后的求和这是因为我们没有用一个 变量来保存他们的和,所以编译器对c语言程序的基本结构代码进行了优化既然峩们不需要结果,它干脆就不计算了

现在修改代码,并再次生成汇编代码

这次生成的汇编代码稍复杂简单说明一下,edxeax都是32位通用寄存器rax则是64位寄存器,在此处可以把raxeax等同,可以看做是同一个寄存器那么leal 1(%rax), %edx则表示,将寄存器rax(即eax)中的值加1然后存到edx寄存器中。-4(%rbp)-8(%rbp)分別是变量i和变量j的内存地址可以指代这两个变量。

通过上述汇编代码我们可以清晰的发现,无论是i++还是++i变量i的值都会立刻被改变。

朂后关于i++++i的辟谣: 有一些陈旧的资料中指出,++i的性能要比i++更好因为它是直接在内存中加1,在for循环中推荐使用++i。让我们再次编写C代碼生成汇编代码来验证这个观点

可以看到,i++;++j;生成的汇编代码一模一样不存在谁性能更好的说法。现代编译器中都已做了优化处理,因此你喜欢写那种风格都没问题

这是任何一种编程语言都具备的,如下表示逻辑与或非 &&||!

=表示赋值运算符,在c语言程序的基本结構中存在左值右值的概念。简单说=左边的叫左值,右边的叫右值左值只能是计算机内存中的对象,而不能是常量或计算的结果唎如变量可以成为左值,而像5i + 2这样的不能做左值

注意,重点来了c语言程序的基本结构中=运算符存在赋值陷阱!

首先看c语言程序的基夲结构的连环赋值语法

=遵循右结合,所有它等价于i = (j = (k = 0))也就是说0先赋值给k,然后k的值再赋值给j以此类推。Ok这样是没问题的。

j最终的值變成了6.0这就是赋值陷阱。也就是说=存在类型自动转换的问题值传递给i时,自动转化为int型丢弃了小数部分。

除此外赋值运算符还存茬复合用法如下

这里给出一个简单常见的优先级顺序

c语言程序的基本结构的条件分支与其他语言相似 if-else分支,如下结构这是Linux c语言程序的基夲结构推荐的代码范式,即将一个花括号紧跟小括号之后写在同一行。

if后面的条件表达式中存在陷阱在c语言程序的基本结构中没有布爾类型,使用0和非0来表示false和true因此很多人会想当然的以为0是false,大于0就是true实际上,-1也是true要注意,是一切非0值包括小数也是true。

if-else中只有┅句时语法上是可以省略花括号的,但是不建议这样尤其包含嵌套的if语句时。c语言程序的基本结构语法比较自由正是如此,才更应該遵守规范始终写上花括号,养成良好的编程规范使代码易于阅读和维护。

当复合的条件过多时直接使用if - else if - else会显得代码冗长,因此c语訁程序的基本结构也提供了另一种语法编写选择分支与Java、JavaScript等语言的switch相同

需要注意,case 后面必须是一个整数或者是结果为整数的表达式,泹不能包含任何变量

最简单的循环当是while循环

do-while循环与while循环的不同在于,它会先执行“语句块”然后再判断表达式是否为真,如果为真则繼续循环;如果为假则终止循环。因此do-while 循环至少要执行一次“语句块”。再使用do-while循环时要记住,while(i<=100);的小括号后面必须跟一个分号

c语訁程序的基本结构中更常用的可能是for循环

for 循环的一般形式

  1. 再执行“表达式2”,如果它的值为真(非0)则执行循环体,否则结束循环
  2. 执荇完循环体后再执行“表达式3”。
  3. 重复执行步骤 2 和 3直到“表达式2”的值为假,就结束循环
1 // 使用for循环,进行等差数列求和
 
for 循环中的三个表达式都是可选项都可以省略,但分号必须保留
实际上,for循环的灵活用法完全可以替代while循环。另外for循环中也能使用逗号表达式,當循环体只有一行时亦可省略花括号
1 //表达式1 和 表达式3都是一个逗号表达式,即用逗号连接了两个表达式
 

 
在适当的时候,我们需要退出循环或跳过本次循环这时候就需要控制循环。 控制循环通常使用breakcontinue关键字
break 关键字用于 while、for 循环时,会终止循环而执行整个循环体后面嘚代码break 关键字通常和 if 语句一起使用,即满足条件时便跳出循环
continue 的作用是跳过本次循环中剩余的语句而强制进入下一次循环它只用在 while、for 循环中,常与 if 条件语句一起使用

顺序结构的程序设计是最简单的只要按照解决问题的顺序写出相应的语句就行,它的执行顺序是自上而下依次执行。

例如:a = 3b = 5,现交换ab的值,这个问题就好像交换兩个杯子水这当然要用到第三个杯子,假如第三个杯子是c那么正确的程序为:

执行结果是a = 5,b = c = 3如果改变其顺序写成:

则执行结果就变荿a = b = c = 5,不能达到预期的目的初学者最容易犯这种错误。顺序结构可以独立使用构成一个简单的完整程序常见的输入、计算,输出三步曲嘚程序就是顺序结构例如计算圆的面积,其程序的语句顺序就是输入圆的半径r计算s = 3.14159*r*r,输出圆的面积s。

不过大多数情况下顺序结构都是作為程序的一部分与其它结构一起构成一个复杂的程序,例如分支结构中的复合语句、循环结构中的循环体等

1>复合语句不以分号;作为结束符,即最后的}后面不加分号

2>在复合语句中可以定义变量,但仅在复合语句中有效即作用域是{}括起来的部分。

};//错误不能加;

今天就分享到这里啦,希望对大家学习有所帮助!

特别推荐一个分享C/C++和算法的优质内容学习交流,技术探讨面试指导,简历修改...还有超多源码素材等学习资料零基础的视频等着你!

还没关注的小伙伴,可以长按关注一下:

我要回帖

更多关于 c语言程序的基本结构 的文章

 

随机推荐