您的位置:首页 > 编程语言 > C语言/C++

C语言学习总结

2016-04-12 17:29 369 查看
自己做一个总结

在开发中,出现一些混淆,从前年开始决定认真学习下C ,主要看的书是C发明者Kernigan & Ritchie 的经典书 《C程序设计语言》、Andrew Koenig的《C陷阱与缺陷》

1、C语言 是 Ritchie 为开发Unix操作系统的应用程序而发明

1973年 由Dennis M. Ritchie设计和实现

1978年 C语言的定义 就是《The C Programming Language》第一版的参考手册,这可以叫做 K&R C

1983年 美国国家标准协会(ANSI)开始制定一个现代的\全面的C语言定义,最后在1988年发布,一般叫做: ANSI C,也叫 C89,ISO1990发布C标准,也叫C90

2000年3月 ANSI 发布 C99

2012年 发布C11

参考维基百科:https://en.wikipedia.org/wiki/ANSI_C
http://www.cnblogs.com/Gavin-Tang/p/3330921.html 【C语言的标准(K&R C, ANSI C, C89, C90, C99)】

C语言在widows系统上 Microsoft 定义一套 Visual C的标准,因此在跨平台时,需要使用宏定义来做特殊处理

#if defined(_MSC_VER)    //Visual C 编译
#elif defined(__GUNC__)  //GNU C 编译
endif


使用Visual Studio 进行编译时,V2012及之前的版本只支持 C89,最大的影响就是声明必须在代码块的开头。从VS2013开始才支持C99

2、C语言中的词法分析中的“贪心法”

从源代码到程序需要经历的过程:预处理、编译、链接
编译这个步骤中有:词法分析AST、中间代码、优化(-O3选项)、生成汇编代码、汇编器生成机器代码
1》将不同语言,进行词法分析,语法分析,得到对应语言的AST

2》这些AST之间有稍微的差别,然后把这些差别去除,生成通用AST

3》按照AST,生成中间代码(三地址代码,即:x=y op z)

4》对中间代码进行优化,这部分的任务很艰巨

5》根据目标机器的汇编描述,生成对应机器架构的汇编语言

6》到此编译器的任务就算完了,剩下的汇编器。

7》汇编器将其汇编成机器代码(010100101000101010010101010000101011110.。。。。。)

8》连接器ld将源码中调用的库函数连接进来

9》最后,是加载器,将可执行文件加载到内存,并执行之。


参考: http://www.2cto.com/kf/201207/139915.html http://www.cnblogs.com/ggjucheng/archive/2011/12/14/2287738.html http://blog.csdn.net/sonicling/article/details/6706152
理解下编译中的词法分析有助于理解一些容易有歧义的C代码表达式的最终运行方式。
例如: c=a+++b 表示的含义,这个时候首先得知道编译器如何理解3个加号 +++,是理解为a++ +b ,还是 a+ ++b

这个从字符 到 符号(token)的解析过程就属于 词法分析(AST,Abstract Syntax Tree),词法分析是将:1行的字符转换为符号串,符号是指C语言保留的关键字、变量名、函数名。关键字包括:数据类型名(char、int)、运算符(+、-、*、/、%)、属性关键字(const、extern、static、inline)等

C语言的词法分析原则是:从左至右依次读取1个字符,当前的字符串如果可能组成一个符号,则继续读取下一个字符,直到已经读取的字符串不能组成符号,则结束。
在词法分析时,遇到空格、制表符、换行符都表示符号的结束

举例:1行字符 a+++b

1、读取1个字符a,可能是1个变量或者函数名,继续读取下一个字符+,那么此时字符串“a+”不会是一个合法的符号,则得到1个符号 “a”
2、读取1个字符+,可能是1个运算符号,继续读取下一个字符+,那么此时字符串“++”可能是1个自加运算符号,继续读取下一个字符+,那么此时字符串“+++”不会是一个合法的符号,则得到1个符号 “++”
3、读取1个字符+,可能是1个运算符号,继续读取下一个字符b,那么此时字符串“+b”不会是一个合法的符号,则得到1个符号 “+”

4、读取1个字符b,可能是1个运算符号,继续读取下一个字符"\d",遇到换行符,表明当前符号结束,那么此时字符串“b”作为1个符号 “b”

最终编译器理解为: a++ +b

遇到一个题目:
#include<stdio.h>

void main()
{
int a=0;
int b=1;
int c5=a+++b;
printf("a=%d b=%d a+++b=%d \n",a,b,a+++b);
printf("a=%d b=%d a+++b=%d \n",a,b,c5);
}


最后的输出是:【gcc 4.9.2 32位编译】
a=2 b=1 a+++b=2
a=2 b=1 a+++b=1
如果能够理解到这个输出,应该表明你理解C语言比较好。对于词法分析、运算优先级、函数调用规范

C函数调用时,参数入栈是从右往左,有了这一点,应该就能理解上面的输出结果

【然后如果你接触过64位的C语言编程,你又会发现新奇的结果。。。。 妈蛋,我也曾经被弄蒙逼了。。。。】
下面是在VS2012中 64位运行的结果:



输出的结果不一样:a=1

原因是64位下 C语言函数的参数处理过程是从左到右

参考:
关于C函数调用时的参数入栈,可以阅读下面这个博客 http://blog.csdn.net/huangkangying/article/details/45154983
然后顺便说明下 VS2012 中关于C89的支持:变量声明必须在代码块的开头 否则会出现错误 C2143



在MSDN官网上有关于C2143的错误描述:



举例2:1行字符 a+++++b
这个是书《C陷阱与缺陷》中的题目 1.4
根据词法分析的贪心法则,词法分析的结果是 a++ ++ + b

但是编译时,会报错:
#include<stdio.h>

void main()
{
int a=0;
int b=1;
int c5=a+++++b;
}


在gcc 4.9.2 32位编译,报错:需要一个左值作为自增操作数

C1.4.c: In function ‘main’:
C1.4.c:7:12: error: lvalue required as increment operand
int c5=a+++++b;



需要学习一个概念: 左值、右值
变量是左值,表达式是右值
++、--这两个运算符的操作数不能是右值,上面编译器报错的原因就是 a++ 是一个表达式,是一个右值,不能作为自增运算符的操作数

关于C语言中的左值、右值,可以参考下面的帖子: http://www.360doc.com/content/14/0320/11/1317564_362108006.shtml
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: