签到

05月06日
尚未签到

共有回帖数 0

    长街旧港00

    等级:
    前言----小话c语言
    不知道该怎么开头,不过开头的几个字都写了,就继续写下去吧。 看过很多以大话开头的书籍,觉得也不怎么样,觉得还没达到大话的层次,本人本着谦虚的精神,暂且以小话开头吧;可能读者看完,觉得连小话都谈不上,先不管这些了;如果读者确实都觉得连小话都谈不上,到时候我再改个名字好了,这样至少也对得起文章的标题。 言归正传,回到主题吧。 以前写过关于c语言的学习资料,发现它真不是容易就能写好的,里面涉及到很多很多的东西;如果是以基础为目的的,那需要描述的就更多了;如果是稍微提 高一些的,那么可能可以少写一些字。不过,以c语言的本质出发,实在太复杂了,如果让c语言之父来描述,估计那本书也没能足以表达他所有的精神和思想,毕 竟核心思想还是在Dennis Ritchie心里,不过他已经离我们而去了,先默哀一下。
    语言都是相通的 c语言,也是一种语言,和我们中国人平时说的中文其实是类似的,只不过一个最终是给机器来理解的,一个是让人来理解的。我不知道哪种应该更复杂,但 是,有一点是可以肯定的,是语言它的语法就不会太复杂,否则不要说是笨笨的机器,就是地球上应该是最高级的人类可能都不能很好地理解,如果这样,这种语言 的存在价值就需要思考了。"你好",这句话表示的就是个问候语,如果非要细节地分析内部的语法,可以理解成主谓结构吧,"你"是主语,"好"当成谓语 吧。"int i = 1;" 理解成定义一个整形变量i; int表示变量类型, 后面跟着变量名,再跟着赋值号=,再跟着一个赋值的数据,最后以分号结束即可。这个结构可以 用如下的表达式来表达: 类型 类型名 = 初值; 上面的看起来,不是很复杂,就像理解"你从哪里来?", "我从这里来。"这样的话一样。
    为什么需要编译器 写完了int i = 1;之后,机器如何理解呢?当然,没有哪个机器能一下子理解这个。因为,有公司已经把机器的cpu设计成只能理解机器语言的了,那怎么办呢?只能用一种东 西将上面的语句翻译成机器指令,传给机器的cpu,机器就能理解并执行了,而,这个东西就是编译器。不过,有人可能会说,我使用了bash终端,我输入 ls -l再回车,就可以帮我执行命令?哪里有编译器? 解释器是什么 上面说的那个过程真没用到编译器,而是解释器。其实解释器就做了编译器的事情,首先会解析输入的字符串ls -l, 就像编译器解析int i = 1;这个字符串一样,然后解析其中的语义,最后执行对应的操作。

    解释器到底是什么 这个东西真的不用多想,它就是一个经过编译器编译ok的程序而已。
    解释器运行程序会比对应编译器编译后的程序运行的慢 说的基本是对的,一般是这样,因为解释器多了一层解释的过程,然后才执行。

    结束语 好了,关于解释器和编译器的比较就先到此为止,前言的内容我不想写一些废话,因为多打一句废话也是消耗能量的;
    printf函数,能不能简单点?----小话c语言(2)
    开头语
    上面一篇已经写过,c语言不仅仅只是语法很好玩,很让人捉摸不透,它的思想更是深奥,需要慢慢体会的。看了关于c语言的书籍不少于50本,每看一遍都 感觉认识深了一点,但是终究还是无法完全理解它背后最深层的思想,只是在不断地向它走近。正因为如此,下面的描述不会按照一个固定的说明文格式来编写,而 是采用对话的方式来将这个过程展现出来,这样应该能更多地展示出c语言的思想。
    格式: 问题(Question)将以 Q: 开头, 回答(Answer)将以 A: 作为开头。

    Q: c语言的HelloWorld版本是什么样子的?
    A: 起名为hello.c: [cpp] view plaincopy

    #include stdio.h

    int main()
    {
    printf("Hello World!");
    return 0;
    }

    Q: #include这条语句是什么意思?
    A: 它以#开头,它表示预处理过程,一般不把它当做编译过程,而是用一个单独的预处理器来处理。因为,从编译源代码的角度来说,将一个头文件插入到最终被编译的代码和编译源代码真的没什么一致的地方。


    Q:如何才能感受预处理器的存在呢?
    A: 一般的编译命令都提供预处理选项,可以查看预处理后的源代码是什么样子。
    [Windows-VS2010] 可以使用cl.exe命令来查看预处理后的源代码:

    cl /P hello.c


    会在同目录下生成一个.i的预处理后的文件hello.i, 内容如下(因为篇幅问题,无法将所有内容上传,否则保存不了,省略号省略了大部分代码): [cpp] view plaincopy

    #line 1 "hello.c"
    #line 1 "d:\Program Files\Microsoft Visual Studio 10.0\VC\INCLUDE\stdio.h"

    .............................
    .............................

    #line 739 "d:\Program Files\Microsoft Visual Studio 10.0\VC\INCLUDE\stdio.h"

    #line 2 "hello.c"

    int main()
    {
    printf("Hello World!");
    return 0;
    }

    Q:看了那个文件,文件实在是太长了!
    A: 是的,这其实也是c和c++的一个缺点,#include将源代码插入的方式可能会导致被编译的源代码长度迅速增加,这也是预编译出现的一个原因。也可以 看到其实hello.c代码那几行也就是最后的那几句,前面都是#include头文件插入的代码,确实大的很直接。
    Q:hello.i文件最后几句,有#line 2 "hello.c"这句,它是什么意义?
    A: #符号开头,依然是编译器的指令,它表示当前行被设置为2,文件名是hello.c.因为,之前的#includestdio.h**入 了很多其它文件的内容,但是在hello.c中,第二行是接着#include stdio.h后面的,所以将行数重新设置,这样才能正确地解析hello.c的代码行数。

    Q:cl命令我怎么知道/P参数是预处理作用的?
    A:当然可以用cl /?命令来得到它的帮助,然后找到需要功能的参数。




    Q: 再回到hello.c里面吧。main函数必须有个返回值吗?
    A: 是的,其实这很能体现分层思想,任何一个应用程序最终都可能有个返回值,供调用它的程序来使用,所以在这里有个int的返回值
    也是可以理解的。

    Q: 怎么查看调用一个程序的返回值呢?
    A: [Windows] 在命令行下,可以使用echo %errorlevel%来获取它的值。
    [Mac]在命令行下,可以使用echo $?来获取。
    [Ubuntu]同[Mac].
    比如,hello.c的代码编译成hello.exe, cl hello.c:


    Q: 可以返回的不是0吗?
    A: 当然可以。修改hello.c源代码为: [cpp] view plaincopy

    #include stdio.h

    int main()
    {
    printf("Hello World!");
    return 1;
    }
    再次编译运行后,使用echo%errorlevel%得到:


    Q: 那么printf函数的原型简化版就是 [cpp] view plaincopy

    int __cdecl printf(const char * _Format, ...);

    A:是的,也可以再简化成 [cpp] view plaincopy

    int printf( const char * _Format, ...);

    Q: __cdecl是什么?
    A: 这是一种函数调用方式;对应的还有__stdcall,__ fastcall等等,它们有一定的区别,比如函数压栈顺序,参数优先保存在寄存器中等,c语言默认是__cdecl方式。


    Q: const char * _Format中的const可以去掉吗?
    A: 可以的,但是用上const表示不可更改的变量,printf函数内部不可以改变_Format指向的字符串,这是一种防御性编程的思想。


    Q: c++中的const不是表示常量吗?
    A: 是的,但是c语言中的const表示的是一个变量,但是不可更改。这个关键字在c语言和c++中是有区别的。


    Q: 举个例子。
    A: 比如在c语言中用const修饰的一个变量,不可以作为静态数组的参数个数,因为它是一个变量;在c++中,这是可以的。
    如hello.c代码修改为如下: [cpp] view plaincopy

    #include stdio.h

    int main()
    {
    const int size = 10;
    int arr[size];
    return 0;
    }
    出现编译错误:




    可以看到,/TP命令行将所有文件当成.cpp来编译,即也会把hello.c代码当成.cpp来编译。


    Q: 这么说来,编译器编译源代码不一定看后缀名的了?
    A: 当然是的,对于编译器来说,只要给文本即可,对于后缀只不过在通常情况下按照指定类型代码编译而已,可以指定将
    某种扩展名的代码当成特定类型代码编译的。


    Q: printf函数原型参数中的三个点表示什么?
    A: 它表示可变参数,即是参数个数不能确定,也许是1个,也许2个或者更多。



    Q: 为什么要这么设计?
    A: 因为对于输出功能来说,该输出多少东西,设计者开始是不知道的,所以交给了程序员来是实现,编译器只需要根据获取
    到的参数最后正确转换给内部处理函数即可。
    比如: [cpp] view plaincopy

    printf("Hello World!");
    printf("My name is %s", "xichen");
    printf("My name is %s, age is %d", "xichen", 25);
    Q: 那么printf函数返回什么呢?

    A: 它返回成功输出的字节数。
    源代码: [cpp] view plaincopy

    #include stdio.h

    int main()
    {
    int ret;
    ret = printf("abc");
    printf(" ret is %dn", ret);
    ret = printf("Hello World!");
    printf(" ret is %dn", ret);
    ret = printf("My name is %s", "xichen");
    printf(" ret is %dn", ret);

    return 0;
    }
    输出:


    楼主 2016-03-31 14:23 回复

共有回帖数 0
  • 回 帖
  • 表情 图片 视频
  • 发表

登录直线网账号

Copyright © 2010~2015 直线网 版权所有,All Rights Reserved.沪ICP备10039589号 意见反馈 | 关于直线 | 版权声明 | 会员须知