精华内容
下载资源
问答
  • 若变量均已正确定义并赋值,以下合法C语言赋值语句是()若变笼统来说,凡是因遗传因素导致的疾病均被称为遗传病,又称染色体病。()量均国际标准的专利文献著录,72对应()项目。已正义并C语言IPC,将涉及发明...

    若变量均已正确定义并赋值,以下合法的C语言赋值语句是()

    若变笼统来说,凡是因遗传因素导致的疾病均被称为遗传病,又称染色体病。()

    量均国际标准的专利文献著录中,72对应()项目。

    已正义并C语言赋在IPC中,将涉及发明专利的整个技术领域的知识按照等级分类,一共分为()大部。

    632363.jpg

    确定专利分类号A61M16/00:以气体处理法影响病人呼吸系统的器械,细分到()。

    赋值法是()的专利检索网站具有的在线翻译功能。

    下合在专利信息索时需要利用()和专利号,并按照一定的逻辑关系交叉筛选。

    值语专利领域中,如果出现()情况,那么说明这项专利非常有意义,属于核心专利。

    若变专利信息的六种组成要素包含()。

    量均专利文献的分析方法中怎样确定技术范围?()

    由于遗传的原因,所有人对疾病的遗传易感性是一样的。()

    已正义并C语言赋关贸总协定与世界贸易组织都具有法人地位。()

    人类学认为,即便是尸体被泼了硫酸,牙齿等也会有残留物可以用于辨别身份。()

    确定血友病指内脏出血后身体缺乏自动凝血机制。()

    赋值法身高特别高的父母,其子女的身高也一定会遗传其父母的特征,变得特别高。()

    下合人类的历史就是一部宗教战争的血泪史。()

    值语在现代社会中,每个人都是自由的,即每个人都能够得到他想得到的一切。()

    若变佛教中所说的“七情六欲”中的“七情”指的是喜怒哀思忧惧爱。()

    患第21对染色体三体综合症的人,是先天愚型儿,属于重度弱智,大量脑细胞坏死。()

    看似简单的生命活动也会牵扯到成千上万的分子和系统。()

    展开全文
  • C语言函数的定义和声明

    万次阅读 多人点赞 2019-08-08 14:38:17
    本科学C语言的时候,就...1.在C语言中,函数的定义顺序是有讲究的:默认情况下,只有后面定义的函数才可以调用前面定义过的函数 1 int sum(int a, int b) { 2 return a + b; 3 } 4 5 int main() 6 { 7 int c = ...

    本科学C语言的时候,就对函数的定义和声明的作用很迷糊,刚看到一篇博客,写得非常清楚,贴出来与各位共享!

    一、函数的声明

    1.在C语言中,函数的定义顺序是有讲究的:默认情况下,只有后面定义的函数才可以调用前面定义过的函数

    复制代码

    1 int sum(int a, int b) {
    2     return a + b;
    3 }
    4 
    5 int main()
    6 {
    7     int c = sum(1, 4);
    8     return 0;
    9 }

    复制代码

    第5行定义的main函数调用了第1行的sum函数,这是合法的。如果调换sum函数和main函数的顺序,在标准的C编译器环境下是不合法的(不过在GCC编译器环境下只是一个警告)

     

    2.如果想把函数的定义写在main函数后面,而且main函数能正常调用这些函数,那就必须在main函数的前面进行函数的声明

    复制代码

     1 // 只是做个函数声明,并不用实现
     2 int sum(int a, int b);
     3 
     4 int main()
     5 {
     6     int c = sum(1, 4);
     7     return 0;
     8 }
     9 
    10 // 函数的定义(实现)
    11 int sum(int a, int b) {
    12     return a + b;
    13 }

    复制代码

    在第11行定义了sum函数,在第2行对sum函数进行了声明,然后在第6行(main函数中)就可以正常调用sum函数了。

     

    3.函数的声明格式

    1> 格式

    返回值类型  函数名 (参数1, 参数2, ...)

    只要你在main函数前面声明过一个函数,main函数就知道这个函数的存在,就可以调用这个函数。而且只要知道函数名、函数的返回值、函数接收多少个参数、每个参数是什么类型的,就能够调用这个函数了,因此,声明函数的时候可以省略参数名称。比如上面的sum函数声明可以写成这样:

    int sum(int, int);

    究竟这个函数是做什么用的,还要看函数的定义。

     

    2> 如果只有函数的声明,而没有函数的定义,那么程序将会在链接时出错

    下面的写法是错误的:

    复制代码

    1 int sum(int a, int b);
    2 
    3 int main()
    4 {
    5     
    6     sum(10, 11);
    7 
    8     return 0;
    9 }

    复制代码

    • 在第1行声明了一个sum函数,但是并没有对sum函数进行定义,接着在第6行调用sum函数
    • 这个程序是可以编译成功的,因为我们在main函数前面声明了sum函数(函数的声明和定义是两码事),这个函数声明可以理解为:在语法上,骗一下main函数,告诉它sum函数是存在的,所以从语法的角度上main函数是可以调用sum函数的。究竟这个sum函数存不存在呢,有没有被定义呢?编译器是不管的。在编译阶段,编译器并不检测函数有没有定义,只有在链接的时候才会检测这个函数存不存在,也就是检测函数有没有被定义。
    • 因此,这个程序会在链接的时候报错,错误信息如下:

    • 我这里的源文件是main.c文件,所以编译成功后生成一个main.o文件。链接的时候,链接器会检测main.o中的函数有没有被定义。
    • 上面的错误信息大致意思是:在main.o文件中找不到sum这个标识符。
    • 错误信息中的linker是链接器的意思,下次看到这个linker,说明是链接阶段出错了。链接出错了,就不能生成可执行文件,程序就不能运行。
    • 这个错误的解决方案就是加上sum函数的定义。

     

    二、多源文件开发

    1.为什么要有多个源文件

    1> 在编写第一个c语言程序的时候已经提到:我们编写的所有C语言代码都保存在拓展名为.c的源文件中,编写完毕后就进行编译、链接,最后运行程序。

    2> 在前面的学习过程中,由于代码比较少,因此所有的代码都保存在一个.c源文件中。但是,在实际开发过程中,项目做大了,源代码肯定非常多,很容易就上万行代码了,甚至上十万、百万都有可能。这个时候如果把所有的代码都写到一个.c源文件中,那么这个文件将会非常庞大,也非常恶心,你可以想象一下,一个文件有十几万行文字,不要说调试程序了,连阅读代码都非常困难。

    3> 而且,公司里面都是以团队开发为主,如果多个开发人员同时修改一个源文件,那就会带来很多麻烦的问题,比如张三修改的代码很有可能会抹掉李四之前添加的代码。

    4> 因此,为了模块化开发,一般会将不同的功能写到不同的.c源文件中,这样的话,每个开发人员都负责修改不同的源文件,达到分工合作的目的,能够大大提高开发效率。也就是说,一个正常的C语言项目是由多个.c源文件构成。

     

    2.将sum函数写到其他源文件中

    接下来就演示一下多个源文件的开发,我将前面定义的sum函数写在另一个源文件(命名为sum.c)中。这时候就有两个源文件:

    1> main.c文件

    1 int main()
    2 {
    3 
    4     return 0;
    5 }

    2> sum.c文件

    1 int sum(int a, int b)
    2 {
    3     return a + b;
    4 }

     

    3.在main函数中调用sum函数

    1> 现在想在main函数中调用sum函数,那么你可能会直接这样写:

    复制代码

    1 int main()
    2 {
    3     int c = sum(10, 11);
    4 
    5     return 0;
    6 }

    复制代码

    这种写法在标准C语言编译器中是直接报错的,因为main函数都不知道sum函数的存在,怎么可以调用它呢!!!

    2> 我们应该骗一下main函数,sum函数是存在的,告诉它sum函数的返回值和参数类型即可。也就是说,应该在main函数前面,对sum函数进行声明。

    main.c文件应该写成下面这样

    复制代码

     1 #include <stdio.h>
     2 
     3 int sum(int, int);
     4 
     5 int main()
     6 {
     7     int c = sum(10, 11);
     8     
     9     printf("c is %d\n", c);
    10     
    11     return 0;
    12 }

    复制代码

    注意第3行,加了一个sum函数的声明。为了检验sum函数的调用结果,在第9行用prinf函数将结果输出。

     

    4.编译所有的源文件

    sum.c和main.c都编写完毕后,就可以使用gcc指令进行编译了。同时编译两个文件的指令是:cc -c main.c sum.c

    编译成功后,生成了2个.o目标文件

    也可以单独编译:

    cc -c main.c

    cc -c sum.c

     

    5.链接所有的目标文件

    前面已经编译成功,生成了main.o和sum.o文件。现在应该把这2个.o文件进行链接,生成可执行文件。

    1> 注意,一定要同时链接两个文件。如果你只是单独链接main.o或者sum.o都是不可能链接成功的。原因如下:

    • 如果只是链接main.o文件:cc main.o,错误信息是:在main.o中找到不到sum这个标识符,其实就是找不到sum函数的定义。因为sum函数的定义在sum.o文件中,main.o中只有sum函数的声明

    • 如果只是链接sum.o文件:cc sum.o,错误信息是:找不到main函数。一个C程序的入口点就是main函数,main函数定义在main.o中,sum.o中并没有定义main函数,连入口都没有,怎么能链接成功、生成可执行文件呢?

    可以看出,main.o和sum.o有密不可分的关系,其实链接的目的就是将所有相关联的目标文件和C语言函数库组合在一起,生成可执行文件。

    2> 链接main.o和sum.o文件:cc main.o sum.o,生成了可执行文件a.out

    3> 运行a.out文件:./a.out,运行结果是在屏幕上输出了:

    c is 21

    说明函数调用成功,我们已经成功在main.c文件的main函数中调用了sum.c文件中的sum函数

    4> 从中也可以得出一个结论:只要知道某个函数的声明,就可以调用这个函数,编译就能成功。不过想要这个程序能够运行成功,必须保证在链接的时候能找到函数的定义。

    三、#include

    理解完前面的知识后,接下来就可以搞懂一个很久以前的问题:每次写在最前面的#include是干啥用的?

    1.#include的作用

    先来看一个最简单的C程序:

    复制代码

    1 #include <stdio.h>
    2 
    3 int main()
    4 {
    5     printf("Hello, World!\n");
    6     return 0;
    7 }

    复制代码

    这个程序的作用是在屏幕上输出Hello,World!这一串内容,我们主要关注第一行代码。

    • #include 是C语言的预处理指令之一,所谓预处理,就是在编译之前做的处理,预处理指令一般以 # 开头
    • #include 指令后面会跟着一个文件名,预处理器发现 #include 指令后,就会根据文件名去查找文件,并把这个文件的内容包含到当前文件中。被包含文件中的文本将替换源文件中的 #include 指令,就像你把被包含文件中的全部内容拷贝到这个 #include 指令所在的位置一样。所以第一行指令的作用是将stdio.h文件里面的所有内容拷贝到第一行中。
    • 如果被包含的文件拓展名为.h,我们称之为"头文件"(Header File),头文件可以用来声明函数,要想使用这些函数,就必须先用 #include 指令包含函数所在的头文件
    • #include 指令不仅仅限于.h头文件,可以包含任何编译器能识别的C/C++代码文件,包括.c、.hpp、.cpp等,甚至.txt、.abc等等都可以

    也就是说你完全可以将第3行~第7行的代码放到其他文件中,然后用 #include 指令包含进来,比如:

    1> 将第3行~第7行的代码放到my.txt中

    2> 在main.c源文件中包含my.txt文件

    • 编译链接后,程序还是可以照常运行的,因为 #include 的功能就是将文件内容完全拷贝到 #include 指令所在的位置
    • 说明:这里用txt文件纯属演示,平时做项目不会这样做,除非吃饱了撑着,才会把代码都写到txt中去

     

    2.#include可以使用绝对路径

    上面的#include "my.txt"使用的是相对路径,其实也可以使用绝对路径。比如#include "/Users/apple/Desktop/my.txt"

     

    3.#include <>和#include ""的区别

    二者的区别在于:当被include的文件路径不是绝对路径的时候,有不同的搜索顺序。

    1> 对于使用双引号""来include文件,搜索的时候按以下顺序:

    • 先在这条include指令的父文件所在文件夹内搜索,所谓的父文件,就是这条include指令所在的文件
    • 如果上一步找不到,则在父文件的父文件所在文件夹内搜索;
    • 如果上一步找不到,则在编译器设置的include路径内搜索;
    • 如果上一步找不到,则在系统的INCLUDE环境变量内搜索

    2> 对于使用尖括号<>来include文件,搜索的时候按以下顺序:

    • 在编译器设置的include路径内搜索;
    • 如果上一步找不到,则在系统的INCLUDE环境变量内搜索

    我这里使用的是clang编译器,clang设置include路径是(4.2是编译器版本):/usr/lib/clang/4.2/include

    Mac系统的include路径有:

    • /usr/include
    • /usr/local/include

     

    4.stdio.h

    我们已经知道#include指令的作用了,可是为什么要在第一行代码包含stdio.h呢?

    • stdio.h 是C语言函数库中的一个头文件,里面声明了一些常用的输入输出函数,比如往屏幕上输出内容的printf函数
    • 这里之所以包含 stdio.h 文件,是因为在第5行中用到了在 stdio.h 内部声明的printf函数,这个函数可以向屏幕输出数据,第7行代码输出的内容是:Hello, World!
    • 注意:stdio.h里面只有printf函数的声明。前面已经提到:只要知道函数的声明,就可以调用这个函数,就能编译成功。不过想要这个程序能够运行成功,必须保证在链接的时候能找到函数的定义。其实链接除了会将所有的目标文件组合在一起,还会关联C语言的函数库,函数库中就有printf函数的定义。因此前面的程序是可以链接成功的。

     

    5.头文件.h和源文件.c的分工

    跟printf函数一样,我们在开发中会经常将函数的声明和定义写在不同的文件中,函数声明放在.h头文件中,函数定义放在.c源文件中。

    下面我们将sum函数的声明和定义分别放在sum.h和sum.c中

    这是sum.h文件

    这是sum.c文件

    然后在main.c中包含sum.h即可使用sum函数

    其实sum.h和sum.c的文件名不一样要相同,可以随便写,只要文件名是合法的。但还是建议写成一样,因为一看文件名就知道sum.h和sum.c是有联系的。

    运行步骤分析:

    1> 在编译之前,预编译器会将sum.h文件中的内容拷贝到main.c中

    2> 接着编译main.c和sum.c两个源文件,生成目标文件main.o和sum.o,这2个文件是不能被单独执行的,原因很简单:

    * sum.o中不存在main函数,肯定不可以被执行

    * main.o中虽然有main函数,但是它在main函数中调用了一个sum函数,而sum函数的定义却存在于sum.o中,因此main.o依赖于sum.o

    3> 把main.o、sum.o链接在一起,生成可执行文件

    4> 运行程序

     

    说到这里,有人可能有疑惑:可不可以在main.c中包含sum.c文件,不要sum.h文件了?

    大家都知道#include的功能是拷贝内容,因此上面的代码等效于:

    这么一看,语法上是绝对没有问题的,main.c、sum.c都能编译成功,分别生成sum.o、main.o文件。但是当我们同时链接main.o和sum.o时会出错。原因:当链接这两个文件时链接器会发现sum.o和main.o里面都有sum函数的定义,于是报"标识符重复"的错误,也就是说sum函数被重复定义了。默认情况下,C语言不允许两个函数的名字相同。因此,不要尝试去#include那些.c源文件。

     

    有人可能觉得分出sum.h和sum.c文件的这种做法好傻B,好端端多出2个文件,你把所有的东西都写到main.c不就可以了么?

    • 没错,整个C程序的代码是可以都写在main.c中。但是,如果项目做得很大,你可以想象得到,main.c这个文件会有多么庞大,会严重降低开发和调试效率。
    • 要想出色地完成一个大项目,需要一个团队的合作,不是一个人就可以搞的定的。如果把所有的代码都写在main.c中,那就导致代码冲突,因为整个团队的开发人员都在修改main.c文件,张三修改的代码很有可能会抹掉李四之前添加的代码。
    • 正常的模式应该是这样:假设张三负责编写 main函数,李四负责编写其他自定义函数,张三需要用到李四编写的某个函数,怎么办呢?李四可以将所有自定义函数的声明写在一个.h文件中,比如 lisi.h,然后张三在他自己的代码中用#include包含lisi.h文件,接着就可以调用lisi.h中声明的函数了,而李四呢,可以独立地在另外一个文件中(比如lisi.c)编写函数的定义,实现那些在lisi.h中声明的函数。这样子,张三和李四就可以相互协作、不会冲突。
    展开全文
  • C语言中的9控制语句 goto转向语句无条件转向; if()else语句:判断语句; while()循环语句; do{}while()语句:先执行循环体,然后判断循环条件是否成立. 之后继续循环 for( ; ; ) 语句:循环,可替代while语句; 只是用法...

    C语言中的9种控制语句

    1. goto转向语句无条件转向;
    2. if()else语句:判断语句;
    3. while()循环语句;
    4. do{}while()语句:先执行循环体,然后判断循环条件是否成立. 之后继续循环
    5. for( ; ; ) 语句:循环,可替代while语句; 只是用法不同;
    6. break语句跳出本层的循环;(只跳出包含此语句的循环)
    7. continue语句:继续(一般放到循环语句里,不在执行它下面的语句,直接跳到判断语句例:for语句,就直接跳到第二个分号处,while语句,就直接跳到while()的括号里;
    8. switch() {case 选项: }多分支选择语句:多项选择;
    9. return语句:从函数返回语句;
    展开全文
  • C语言中的宏定义

    千次阅读 2019-01-14 19:51:58
    C语言中的宏定义
                   

    1. 简单宏定义

    简单的宏定义有如下格式:

    [#define指令(简单的宏)]  #define  标识符替换列表

    替换列表是一系列的C语言记号,包括标识符、关键字、数、字符常量、字符串字面量、运算符和标点符号。当预处理器遇到一个宏定义时,会做一个 “标识符”代表“替换列表”的记录。在文件后面的内容中,不管标识符在任何位置出现,预处理器都会用替换列表代替它。

    不要在宏定义中放置任何额外的符号,否则它们会被作为替换列表的一部分。一种常见的错误是在宏定义中使用 = :

    #define N = 100       /*** WRONG ***/int a[N];            /* 会成为 int a[= 100]; */

    在上面的例子中,我们(错误地)把N定义成一对记号(= 和100)。

    在宏定义的末尾使用分号结尾是另一个常见错误:

    #define N 100;       /*** WRONG ***/int a[N];            /*    become int a[100;]; */

    这里N被定义为100和;两个记号。

    在一个宏定义中,编译器可以检测到绝大多数由多余符号所导致的错误。但不幸的是,编译器会将每一处使用这个宏的地方标为错误,而不会直接找到错误的根源——宏定义本身,因为宏定义已经被预处理器删除了。

    简单的宏主要用来定义那些被Kernighan和Ritchie称为“明示常量”(manifest constant)的东西。使用宏,我们可以给数值、字符和字符串命名。

    #define STE_LEN 80#defineTRUE     1#defineFALSE    0#definePI        3.14159#defineCR        '\r'#defineEOS       '\0'

    使用#define来为常量命名有许多显著的优点:

    1) 、 程序会更易读。一个认真选择的名字可以帮助读者理解常量的意义。否则,程序将包含大量的“魔法数”,使读者难以理解。

    2) 、 程序会更易于修改。我们仅需要改变一个宏定义,就可以改变整个程序中出现的所有该常量的值。“硬编码的”常量会更难于修改,特别是有时候当他们以稍微不同的形式出现时。(例如,如果一个程序包含一个长度为100的数组,它可能会包含一个从0到99的循环。如果我们只是试图找到所有程序中出现的100,那么就会漏掉99。)

    3) 、可以帮助避免前后不一致或键盘输入错误。假如数值常量3.14159在程序中大量出现,它可能会被意外地写成3.1416或3.14195。

    虽然简单的宏常用于定义常量名,但是它们还有其他应用。

    4) 、可以对C语法做小的修改。实际上,我们可以通过定义宏的方式给C语言符号添加别名,从而改变C语言的语法。例如,对于习惯使用Pascal的begin和end(而不是C语言的{和})的程序员,可以定义下面的宏:

    #define BEGIN  {#define END    }

    我们甚至可以发明自己的语言。例如,我们可以创建一个LOOP“语句”,来实现一个无限循环:

    #define LOOP   for (;;)

    当然,改变C语言的语法通常不是个好主意,因为它会使程序很难被其他程序员所理解。

    5) 、对类型重命名。在5.2节中,我们通过重命名int创建了一个Boolean类型:

    #define BOOL int

    虽然有些程序员会使用宏定义的方式来实现此目的,但类型定义(7.6节)仍然是定义新类型的最佳方法。

    6) 、控制条件编译。如将在14.4节中看到的那样,宏在控制条件编译中起重要的作用。例如,在程序中出现的宏定义可能表明需要将程序在“调试模式”下进行编译,来使用额外的语句输出调试信息:

    #define DEBUG

    这里顺便提一下,如上面的例子所示,宏定义中的替换列表为空是合法的。

    当宏作为常量使用时,C程序员习惯在名字中只使用大写字母。但是并没有如何将用于其他目的的宏大写的统一做法。由于宏(特别是带参数的宏)可能是程序中错误的来源,所以一些程序员更喜欢使用大写字母来引起注意。其他人则倾向于小写,即按照Kernighan和Ritchie编写的The C Programming Language一书中的样式。

    2. 带参数的宏

    带参数的宏定义有如下格式:

    [#define指令—带参数的宏]  #define 标识符(x1, x2,…,xn)替换列表

    其中x1, x2,…,xn是标识符(宏的参数)。这些参数可以在替换列表中根据需要出现任意次。

    在宏的名字和左括号之间必须没有空格。如果有空格,预处理器会认为是在定义一个简单的宏,其中(x1,x2,…,xn)是替换列表的一部分。

    当预处理器遇到一个带参数的宏,会将定义存储起来以便后面使用。在后面的程序中,如果任何地方出现了标识符(y1,y2,…,yn)格式的宏调用(其中y1,y2,…,yn是一系列标记),预处理器会使用替换列表替代,并使用y1替换x1y2替换x2,依此类推。

    例如,假定我们定义了如下的宏:

    #define MAX(x,y)    ((x)>(y) ? (x) :(y))#define IS_EVEN(n)   ((n)%2==0)

    现在如果后面的程序中有如下语句:

    i = MAX(j+k, m-n);if (IS_EVEN(i)) i++;

    预处理器会将这些行替换为

    i = ((j+k)>(m-n)?(j+k):(m-n));if (((i)%2==0)) i++;

    如这个例子所显示的,带参数的宏经常用来作为一些简单的函数使用。MAX类似一个从两个值中选取较大的值的函数。IS_EVEN则类似于另一种函数,该函数当参数为偶数时返回1,否则返回0。

    下面的例子是一个更复杂的宏:

    #define TOUPPER(c)('a'<=(c)&&(c)<='z'?(c)-'a'+'A':(c))

    这个宏检测一个字符c是否在'a'与'z'之间。如果在的话,这个宏会用'c'减去'a'再加上'A',来计算出c所对应的大写字母。如果c不在这个范围,就保留原来的c。像这样的字符处理的宏非常有用,所以C语言库在<ctype.h>(23.4节)中提供了大量的类似的宏。其中之一就是toupper,与我们上面的TOUPPER例子作用一致(但会更高效,可移植性也更好)。

    带参数的宏可以包含空的参数列表,如下例所示:

    #define getchar() getc(stdin)

    空的参数列表不是一定确实需要,但可以使getchar更像一个函数。(没错,这就是<stdio.h>中的getchar,getchar的确就是个宏,不是函数——虽然它的功能像个函数。)

              使用带参数的宏替代实际的函数的优点

    1) 、  程序可能会稍微快些。一个函数调用在执行时通常会有些额外开销——存储上下文信息、复制参数的值等。而一个宏的调用则没有这些运行开销。

    2) 、 宏会更“通用”。与函数的参数不同,宏的参数没有类型。因此,只要预处理后的程序依然是合法的,宏可以接受任何类型的参数。例如,我们可以使用MAX宏从两个数中选出较大的一个,数的类型可以是int,long int,float,double等等。

            但是带参数的宏也有一些缺点。

    1) 、 编译后的代码通常会变大。每一处宏调用都会导致插入宏的替换列表,由此导致程序的源代码增加(因此编译后的代码变大)。宏使用得越频繁,这种效果就越明显。当宏调用嵌套时,这个问题会相互叠加从而使程序更加复杂。思考一下,如果我们用MAX宏来找出3个数中最大的数会怎样?

    n = MAX(i, MAX(j,k));

    下面是预处理后的这条语句:

    n=((i)>(((j)>(k)?(j):(k)))?(i):(((j)>(k)?(j):(k))));

    2) 、宏参数没有类型检查。当一个函数被调用时,编译器会检查每一个参数来确认它们是否是正确的类型。如果不是,或者将参数转换成正确的类型,或者由编译器产生一个出错信息。预处理器不会检查宏参数的类型,也不会进行类型转换。

    3) 、无法用一个指针来指向一个宏。如在17.7节中将看到的,C语言允许指针指向函数。这一概念在特定的编程条件下非常有用。宏会在预处理过程中被删除,所以不存在类似的“指向宏的指针”。因此,宏不能用于处理这些情况。

    4) 、宏可能会不止一次地计算它的参数。函数对它的参数只会计算一次,而宏可能会计算两次甚至更多次。如果参数有副作用,多次计算参数的值可能会产生意外的结果。考虑下面的例子,其中MAX的一个参数有副作用:

    n = MAX(i++, j);

    下面是这条语句在预处理之后的结果:

    n =((i++)>(j)?(i++):(j));

    如果i大于j,那么i可能会被(错误地)增加了两次,同时n可能被赋予了错误的值。

    由于多次计算宏的参数而导致的错误可能非常难于发现,因为宏调用和函数调用看起来是一样的。更糟糕的是,这类宏可能在大多数情况下正常工作,仅在特定参数有副作用时失效。为了自保护,最好避免使用带有副作用的参数。

    带参数的宏不仅适用于模拟函数调用。他们特别经常被作为模板,来处理我们经常要重复书写的代码段。如果我们已经写烦了语句

    printf("%d"\n, x);

    因为每次要显示一个整数x都要使用它。我们可以定义下面的宏,使显示整数变得简单些:

    #define PRINT_INT(x)    printf("%d\n", x)

    一旦定义了PRINT_INT,预处理器会将这行

    PRINT_INT(i/j);//转换为printf("%d\n", i/j);

    3. #运算符

           宏定义可以包含两个运算符:#和##。编译器不会识别这两种运算符相反,它们会在预处理时被执行

    #运算符将一个宏的参数转换为字符串字面量(字符串字面量(string literal)是指双引号引住的一系列字符,双引号中可以没有字符,可以只有一个字符,也可以有很多个字符),, 简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个双引号. 它仅允许出现在带参数的宏的替换列表中。(一些C程序员将#操作理解为“stringization(字符串化)”;其他人则认为这实在是对英语的滥用。)用比较官方的话说就是将语言符号(Token)转化为字符串。

            #运算符有大量的用途,这里只来讨论其中的一种。假设我们决定在调试过程中使用PRINT_INT宏作为一个便捷的方法,来输出一个整型变量或表达式的值。#运算符可以使PRINT_INT为每个输出的值添加标签。下面是改进后的PRINT_INT:

    #define PRINT_INT(x) printf(#x " = %d\n", x)

    x之前的#运算符通知预处理器根据PRINT_INT的参数创建一个字符串字面量。因此,调用

    PRINT_INT(i/j);//会变为printf("i/j" " = %d\n", i/j);

    在C语言中相邻的字符串字面量会被合并,因此上边的语句等价于:

    printf("i/j = %d\n", i/j);

    当程序执行时,printf函数会同时显示表达式i/j和它的值。例如,如果i是11,j是2的话,输出为

    i/j = 5

    TIPI例子:

    #define STR(x) #x int main(int argc char** argv){    printf("%s\n", STR(It's a long string)); // 输出 It's a long str    return 0;}

    4. ##运算符

             在C语言的宏中,"##"被称为 连接符(concatenator),它是一种预处理运算符, 用来把两个语言符号(Token)组合成单个语言符号。 这里的语言符号不一定是宏的变量。并且双井号不能作为第一个或最后一个元素存在.

    ##运算符可以将两个记号(例如标识符)“粘”在一起,成为一个记号。(无需惊讶,##运算符被称为“记号粘合”。)如果其中一个操作数是宏参数,“粘合”会在当形式参数被相应的实际参数替换后发生。考虑下面的宏:

    如下例子:当MK_ID被调用时(比如MK_ID(1)),预处理器首先使用自变量(这个例子中是1)替换参数n。接着,预处理器将i和1连接成为一个记号(i1)。下面的声明使用MK_ID创建了3个标识符:

    #define MK_ID(n) i##nint MK_ID(1), MK_ID(2), MK_ID(3);//预处理后声明变为:int i1, i2, i3;

            ##运算符不属于预处理器经常使用的特性。实际上,想找到一些使用它的情况是比较困难的。为了找到一个有实际意义的##的应用,我们来重新思考前面提到过的MAX宏。如我们所见,当MAX的参数有副作用时会无法正常工作。一种解决方法是用MAX宏来写一个max函数。遗憾的是,往往一个max函数是不够的。我们可能需要一个实际参数是int值的max函数,还需要参数为float值的max函数,等等。除了实际参数的类型和返回值的类型之外,这些函数都一样。因此,这样定义每一个函数似乎是个很蠢的做法。

             解决的办法是定义一个宏,并使它展开后成为max函数的定义。宏会有唯一的参数type,它表示形式参数和返回值的类型。这里还有个问题,如果我们是用宏来创建多个max函数,程序将无法编译。(C语言不允许在同一文件中出现两个同名的函数。)为了解决这个问题,我们是用##运算符为每个版本的max函数构造不同的名字。下面的例子:请注意宏的定义中是如何将type和_max相连来形成新函数名的。假如我们需要一个针对float值的max函数。

    #define GENERIC_MAX (type)           \type type##_max(type x,  type y)    \{                                      \  return x > y ? x :y;              \}GENERIC_MAX(float)

    //预处理器会将这行展开为下面的代码:
    float float_max(float x, float y) { return x > y ? x :y; }

    再如:

    #define PHP_FUNCTION            ZEND_FUNCTION#define ZEND_FUNCTION(name)             ZEND_NAMED_FUNCTION(ZEND_FN(name))#define ZEND_FN(name) zif_##name#define ZEND_NAMED_FUNCTION(name)       void name(INTERNAL_FUNCTION_PARAMETERS)#define INTERNAL_FUNCTION_PARAMETERS int ht, zval *return_value, zval **return_value_ptr, \zval *this_ptr, int return_value_used TSRMLS_DC PHP_FUNCTION(count);

    //  预处理器处理以后, PHP_FUCNTION(count);就展开为如下代码
    void zif_count(int ht, zval *return_value, zval **return_value_ptr,        zval *this_ptr, int return_value_used TSRMLS_DC)

    宏ZEND_FN(name)中有一个"##",它的作用一如之前所说,是一个连接符,将zif和宏的变量name的值连接起来。 以这种连接的方式以基础,多次使用这种宏形式,可以将它当作一个代码生成器,这样可以在一定程度上减少代码密度, 我们也可以将它理解为一种代码重用的手段,间接地减少不小心所造成的错误。

    5. 宏的通用属性

    现在我们已经讨论过简单的宏和带参数的宏了,我们来看一下它们都需要遵守的规则。
    1) 、宏的替换列表可以包含对另一个宏的调用。例如,我们可以用宏PI来定义宏TWO_PI:
    #definePI      3.14159
    #defineTWO_PI  (2*PI)
    当预处理器在后面的程序中遇到TWO_PI时,会将它替换成(2*PI)。接着,预处理器会重新检查替换列表,看它是否包含其他宏的调用(在这个例子中,调用了宏PI)。预处理器会不断重新检查替换列表,直到将所有的宏名字都替换掉为止。


    2) 、预处理器只会替换完整的记号,而不会替换记号的片断。因此,预处理器会忽略嵌在标识符名、字符常量、字符串字面量之中的宏名。例如,假设程序含有如下代码行:

    #define SIZE 256int BUFFER_SIZE;if (BUFFER_SIZE> SIZE)   puts("Error : SIZEexceeded");//预处理后,这些代码行会变为:int BUFFER_SIZE;if (BUFFER_SIZE> 256puts("Error :SIZEexceeded");
    标识符BUFFER_ZISE和字符串"Error:SIZE exceeded"没有被预处理影响,虽然它们都包含SIZE。

    3) 、一个宏定义的作用范围通常到出现这个宏的文件末尾。由于宏是由预处理器处理的,他们不遵从通常的范围规则。一个定义在函数中的宏并不是仅在函数内起作用,而是作用到文件末尾。

    4) 、宏不可以被定义两遍,除非新的定义与旧的定义是一样的。小的间隔上的差异是允许的,但是宏的替换列表(和参数,如果有的话)中的记号都必须一致。

    5) 、宏可以使用#undef指令“取消定义”。#undef指令有如下形式:
    [#undef指令]  #undef  标识符 
    其中标识符是一个宏名。例如,指令
    #undef N
    会删除宏N当前的定义。(如果N没有被定义成一个宏,#undef指令没有任何作用。)#undef指令的一个用途是取消一个宏的现有定义,以便于重新给出新的定义。

    6. 宏定义中圆括号

        在我们前面定义的宏的替换列表中有大量的圆括号。确实需要它们吗?答案是绝对需要。如果我们少用几个圆括号,宏可能有时会得到意料之外的——而且是不希望有的结果。对于在一个宏定义中哪里要加圆括号有两条规则要遵守:

    首先,如果宏的替换列表中有运算符,那么始终要将替换列表放在括号中:

    #define TWO_PI (2*3.14159)
    其次,如果宏有参数,每次参数在替换列表中出现时都要放在圆括号中:
    #define SCALE(x) ((x)*10)

    没有括号的话,我们将无法确保编译器会将替换列表和参数作为完整的表达式。编译器可能会不按我们期望的方式应用运算符的优先级和结合性规则。

    为了展示为替换列表添加圆括号的重要性,考虑下面的宏定义,其中的替换列表没有添加圆括号:
    #define TWO_PI 2*3.14159
      /* 需要给替换列表加圆括号 */
    在预处理时,语句
    conversion_factor = 360/TWO_PI;
    //变为
    conversion_factor = 360/2*3.14159;
    除法会在乘法之前执行,产生的结果并不是期望的结果。
    当宏有参数时,仅给替换列表添加圆括号是不够的。参数的每一次出现都要添加圆括号。例如,假设SCALE定义如下:
    #define SCALE(x) (x*10)   /* 需要给x添加括号 */
    在预处理过程中,语句
    j = SCALE(i+1);
    变为
    j = (i+1*10);
    由于乘法的优先级比加法高,这条语句等价于
    j = i+10;
    当然,我们希望的是
    j = (i+1)*10;

    在宏定义中缺少圆括号会导致C语言中最让人讨厌的错误。程序通常仍然可以编译通过,而且宏似乎也可以工作,仅在少数情况下会出错。

    7. 创建较长的宏

    1. 较长的宏中的逗号运算符

          在创建较长的宏时,逗号运算符会十分有用。特别是可以使用逗号运算符来使替换列表包含一系列表达式。例如,下面的宏会读入一个字符串,再把字符串显示出来:

    #define ECHO(s) (get(s), puts(s))

          gets函数和puts函数的调用都是表达式,因此使用逗号运算符连接它们是合法的。我们甚至可以把ECHO宏当作一个函数来使用:
    ECHO(str);   /* 替换为 (gets(str), puts(str)); */
    除了使用逗号运算符,我们也许还可以将gets函数和puts函数的调用放在大括号中形成复合语句:
    #define ECHO(s)  { gets(s);  puts(s);  }
    遗憾的是,这种方式并不奏效。假如我们将ECHO宏用于下面的if语句:
    if (echo_flag)  ECHO(str);else  gets(str);//将ECHO宏替换会得到下面的结果:if (echo_flag)  { gets(str); puts(str);  };else  gets(str);

    编译器会将头两行作为完整的if语句:
    if (echo_flag)  { gets(str);  puts(str);  }

            编译器会将跟在后面的分号作为空语句,并且对else子句产生出错信息,因为它不属于任何if语句。我们可以通过记住永远不要在ECHO宏后面加分号来解决这个问题。但是这样做会使程序看起来有些怪异。逗号运算符可以解决ECHO宏的问题,但并不能解决所有宏的问题。假如一个宏需要包含一系列的语句,而不仅仅是一系列的表达式,这时逗号运算符就起不到帮助的作用了。因为它只能连接表达式,不能连接语句。解决的方法是将语句放在do循环中,并将条件设置为假:

    2. 宏定义中的do-while循环do 

    do循环必须始终随跟着一个分号,因此我们不会遇到在if语句中使用宏那样的问题了。为了看到这个技巧(嗯,应该说是技术)的实际作用,让我们将它用于ECHO宏中:
    #define ECHO(s)       \      do{           \           gets (s) ;      \           puts (s) ;      \      } while  (0)
    当使用ECHO宏时,一定要加分号:
    ECHO(str);
      /* becomes do {  gets(str); puts(str); } while (0);  */
    为什么在宏定义时需要使用do-while语句呢? 我们知道do-while循环语句是先执行循环体再判断条件是否成立, 所以说至少会执行一次。当使用do{ }while(0)时由于条件肯定为false,代码也肯定只

    执行一次, 肯定只执行一次的代码为什么要放在do-while语句里呢? 这种方式适用于宏定义中存在多语句的情况。 如下所示代码:
    #define TEST(a, b)  a++;b++; if (expr)    TEST(a, b);else    do_else();代码进行预处理后,会变成:if (expr)    a++;b++;else    do_else();

          这样if-else的结构就被破坏了if后面有两个语句,这样是无法编译通过的,那为什么非要do-while而不是简单的用{}括起来呢。 这样也能保证if后面只有一个语句。例如上面的例子,在调用宏TEST的时候后面加了一个分号, 虽然这个分号可有可无, 但是出于习惯我们一般都会写上。 那如果是把宏里的代码用{}括起来,加上最后的那个分号。 还是不能通过编译。 所以一般的多表达式宏定义中都采用do-while(0)的方式。

    3. "空操作"的定义

          了解了do-while循环在宏中的作用,再来看"空操作"的定义。在PHP源码中,由于PHP需要考虑到平台的移植性和不同的系统配置, 所以需要在某些时候把一些宏的操作定义为空操作。例如在sapi\thttpd\thttpd.c

    文件中的VEC_FREE():
    #ifdef SERIALIZE_HEADERS    # define VEC_FREE() smart_str_free(&vec_str)#else    # define VEC_FREE() do {} while (0)#endif

    这里涉及到条件编译,在定义了SERIALIZE_HEADERS宏的时候将VEC_FREE()定义为如上的内容,而没有定义时, 不需要做任何操作,所以后面的宏将VEC_FREE()定义为一个空操作,不做任何操作,通

    常这样来保证一致性, 或者充分利用系统提供的功能。
    有时也会使用如下的方式来定义“空操作”,这里的空操作和上面的还是不一样,例如很常见的Debug日志打印宏:
    #ifdef DEBUG#   define LOG_MSG printf#else#   define LOG_MSG(...)#endif

    在编译时如果定义了DEBUG则将LOG_MSG当做printf使用,而不需要调试,正式发布时则将LOG_MSG()宏定义为空, 由于宏是在预编译阶段进行处理的,所以上面的宏相当于从代码中删除了。

    上面提到了两种将宏定义为空的定义方式,看上去一样,实际上只要明白了宏都只是简单的代码替换就知道该如何选择了。

    8. 预定义宏

    在C语言中预定义了一些有用的宏,表预定义宏。这些宏主要是提供当前编译的信息。宏__LINE__和__STDC__是整型常量,其他3个宏是字符串字面量。
    表预定义宏:
    __LINE__      被编译的文件的行数
    __FILE__      被编译的文件的名字
    __DATE__    编译的日期(格式"Mmm dd yyyy")
    __TIME__     编译的时间(格式"hh:mm:ss")
    __STDC__   如果编译器接受标准C,那么值为1


    1)、 __DATE__宏和__TIME__宏指明程序编译的时间。例如,假设程序以下面的语句开始:

    printf("Wacky Windows (c) 1996 Wacky Software, Inc.\n");

    printf("Compiled on %s at %s\n", __DATE__,__TIME__);

    每次程序开始执行,程序都会显示下面两行:
    Wacky Windows (c) 1996 Wacky Software, Inc.
    Compiled on Dec 23 1996 at 22:18:48
    这样的信息可以帮助区分同一个程序的不同版本。
    2)、我们可以使用__LINE__宏和__FILE__宏来找到错误。考虑下面这个检测被零除的除法的发生位置的问题。当一个C程序因为被零除而导致中止时,通常没有信息指明哪条除法运算导致错误。下面的宏可以帮助我们查明错误的根源:
    #define CHECK_ZERO(divisor)  \
      if (divisor == 0)  \
        printf("*** Attempt to divide byzero on line %d  "  \
                "of file %s  ***\n",__LINE__, __FILE__)
    CHECK_ZERO宏应该在除法运算前被调用:
    CHECK_ZERO(j);
    k = i / j;
    如果j是0,会显示出如下形式的信息:
    *** Attempt to divide by zero on line 9 of file FOO.c ***
    类似这样的错误检测的宏非常有用。实际上,C语言库提供了一个通用的、用于错误检测的宏——assert宏
     再如:
    #line 838 "Zend/zend_language_scanner.c"

    #line预处理用于改变当前的行号(__LINE__)和文件名(__FILE__)。 如上所示代码,将当前的行号改变为838,文件名Zend/zend_language_scanner.c 它的作用体现在编译器的编写中,我们知道

    编译器对C 源码编译过程中会产生一些中间文件,通过这条指令, 可以保证文件名是固定的,不会被这些中间文件代替,有利于进行调试分析。

     

    9. C语言中常用的宏

    01: 防止一个头文件被重复包含

    #ifndef COMDEF_H
    #define COMDEF_H
    //头文件内容
    #endif
    02: 重新定义一些类型

    防止由于各种平台和编译器的不同,而产生的类型字节数差异,方便移植。

    typedef  unsigned char      boolean;     /* Boolean value type. */
    typedef  unsigned long int  uint32;      /* Unsigned 32 bit value */
    typedef  unsigned short     uint16;      /* Unsigned 16 bit value */
    typedef  unsigned char      uint8;       /* Unsigned 8  bit value */
    typedef  signed long int    int32;       /* Signed 32 bit value */
    typedef  signed short       int16;       /* Signed 16 bit value */
    typedef  signed char        int8;        /* Signed 8  bit value */

    //下面的不建议使用
    typedef  unsigned char     byte;         /* Unsigned 8  bit value type. */
    typedef  unsigned short    word;         /* Unsinged 16 bit value type. */
    typedef  unsigned long     dword;        /* Unsigned 32 bit value type. */
    typedef  unsigned char     uint1;        /* Unsigned 8  bit value type. */
    typedef  unsigned short    uint2;        /* Unsigned 16 bit value type. */
    typedef  unsigned long     uint4;        /* Unsigned 32 bit value type. */
    typedef  signed char       int1;         /* Signed 8  bit value type. */
    typedef  signed short      int2;         /* Signed 16 bit value type. */
    typedef  long int          int4;         /* Signed 32 bit value type. */
    typedef  signed long       sint31;       /* Signed 32 bit value */
    typedef  signed short      sint15;       /* Signed 16 bit value */
    typedef  signed char       sint7;        /* Signed 8  bit value */

    03: 得到指定地址上的一个字节或字
    #define  MEM_B(x) (*((byte *)(x)))
    #define  MEM_W(x) (*((word *)(x)))

    04: 求最大值和最小值

    #define  MAX(x,y) (((x)>(y)) ? (x) : (y))
    #define  MIN(x,y) (((x) < (y)) ? (x) : (y))

    05: 得到一个field在结构体(struct)中的偏移量

    #define FPOS(type,field) ((dword)&((type *)0)->field)

    06: 得到一个结构体中field所占用的字节数
    #define FSIZ(type,field) sizeof(((type *)0)->field)

    07: 按照LSB格式把两个字节转化为一个Word

    #define FLIPW(ray) ((((word)(ray)[0]) * 256) + (ray)[1])

    08: 按照LSB格式把一个Word转化为两个字节
    #define FLOPW(ray,val) (ray)[0] = ((val)/256); (ray)[1] = ((val) & 0xFF)

    09: 得到一个变量的地址(word宽度)

    #define B_PTR(var)  ((byte *) (void *) &(var))
    #define W_PTR(var)  ((word *) (void *) &(var))

    10: 得到一个字的高位和低位字节
    #define WORD_LO(xxx)  ((byte) ((word)(xxx) & 255))
    #define WORD_HI(xxx)  ((byte) ((word)(xxx) >> 8))

    11: 返回一个比X大的最接近的8的倍数
    #define RND8(x) ((((x) + 7)/8) * 8

    12: 将一个字母转换为大写

    #define UPCASE(c) (((c)>='a' && (c) <= 'z') ? ((c) – 0×20) : (c))

    13: 判断字符是不是10进值的数字

    #define  DECCHK(c) ((c)>='0' && (c)<='9')

    14: 判断字符是不是16进值的数字

    #define HEXCHK(c) (((c) >= '0' && (c)<='9') ((c)>='A' && (c)<= 'F') \
    ((c)>='a' && (c)<='f'))

    15: 防止溢出的一个方法
    #define INC_SAT(val) (val=((val)+1>(val)) ? (val)+1 : (val))

    16: 返回数组元素的个数
    #define ARR_SIZE(a)  (sizeof((a))/sizeof((a[0])))

    17: 返回一个无符号数n尾的值MOD_BY_POWER_OF_TWO(X,n)=X%(2^n)
    #define MOD_BY_POWER_OF_TWO( val, mod_by ) ((dword)(val) & (dword)((mod_by)-1))

    18: 对于IO空间映射在存储空间的结构,输入输出处理
    #define inp(port) (*((volatile byte *)(port)))
    #define inpw(port) (*((volatile word *)(port)))
    #define inpdw(port) (*((volatile dword *)(port)))
    #define outp(port,val) (*((volatile byte *)(port))=((byte)(val)))
    #define outpw(port, val) (*((volatile word *)(port))=((word)(val)))
    #define outpdw(port, val) (*((volatile dword *)(port))=((dword)(val)))

    19: 使用一些宏跟踪调试
    ANSI标准说明了五个预定义的宏名。它们是:
    __LINE__
    __FILE__
    __DATE__
    __TIME__
    __STDC__

    C++中还定义了 __cplusplus
    如果编译器不是标准的,则可能仅支持以上宏名中的几个,或根本不支持。记住编译程序也许还提供其它预定义的宏名。
    __LINE__ 及 __FILE__ 宏指示,#line指令可以改变它的值,简单的讲,编译时,它们包含程序的当前行数和文件名。
    __DATE__ 宏指令含有形式为月/日/年的串,表示源文件被翻译到代码时的日期。
    __TIME__ 宏指令包含程序编译的时间。时间用字符串表示,其形式为: 分:秒
    __STDC__ 宏指令的意义是编译时定义的。一般来讲,如果__STDC__已经定义,编译器将仅接受不包含任何非标准扩展的标准C/C++代码。如果实现是标准的,则宏__STDC__含有十进制常量1。如果它含有任何其它数,则实现是非标准的。
    __cplusplus 与标准c++一致的编译器把它定义为一个包含至少6为的数值。与标准c++不一致的编译器将使用具有5位或更少的数值。

    可以定义宏,例如:当定义了_DEBUG,输出数据信息和所在文件所在行
    #ifdef _DEBUG
    #define DEBUGMSG(msg,date) printf(msg);printf(“%d%d%d”,date,_LINE_,_FILE_)
    #else
    #define DEBUGMSG(msg,date)
    #endif
    20: 宏定义防止错误使用小括号包含。
    例如:
    有问题的定义:#define DUMP_WRITE(addr,nr) {memcpy(bufp,addr,nr); bufp += nr;}
    应该使用的定义: #difne DO(a,b) do{a+b;a++;}while(0)
    例如:
    if(addr)    DUMP_WRITE(addr,nr);else    do_somethong_else();//宏展开以后变成这样:if(addr)    {memcpy(bufp,addr,nr); bufp += nr;};else    do_something_else();

    gcc 在碰到else前面的“;”时就认为if语句已经结束,因而后面的else不在if语句中。而采用do{} while(0)的定义,在任何情况下都没有问题。而改为 #difne DO(a,b) do{a+b;a++;}while(0) 的定义则在任何情况下都不会出错。


               

    再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow

    展开全文
  • 其次,标识分用户标识符、关键字预定义标识符 a,b答案,“=”“+不能把C语言关键字作为用户标识符,例如if,for,while等. 语言内建标识符:语言内建标识符定义在语言内部。标识符分为关键字、预定义标识符、...
  • C语言学习入门 (二) 语句和运算符

    千次阅读 2014-12-02 16:01:12
    C语言的基本语句跟Java的差不多 循环语句(do while、while、for) 条件语句(if 、if-else、switch) goto语句 (比如循环外 定义一个标记 Exit:; 循环内可以 用 goto Exit;跳出循环) for(int i = 0;...
  • 函数体就是一个代码块,它函数被调用时执行,与函数定义相反的是,函数声明出现函数被调用的地方。函数声明向编译器提供该函数的相关信息,用于确保函数被正确的调用。 那么函数到底是如何定义的呢?请看下面的...
  • C语言的宏定义

    2016-03-01 12:08:44
    C语言定义的一个例子注意事项。
  • C语言声明语句

    2018-11-14 17:38:28
    设计理念: C语言的一个设计理念就是声明变量...变量声明使用到的符号的术语:(并不是所有的组合是合法的) 数量 名称 举例 0或更多 指针(pointer) * 一个 说明符(declarator) i...
  • C语言中switch语句报错问题

    千次阅读 2018-04-18 15:16:56
    C使用case语句报出的一个错误原创 2016年02月23日 14:04:49314使用C或C++时,switch--case语句编译器报出这样一个错误:首先看一下代码:[cpp] view plain copycase 4: int len=ListLength(L); printf(&...
  • C语言基本语句

    千次阅读 多人点赞 2017-10-11 16:27:33
    1.C语言中空语句一般有哪些用途?  1.纯粹消耗cpu时间,起到延时的作用  2.为了程序的结构清楚,可读性好,以后扩充新功能方便。有些公司的编码规范要求,对于if/else语句等,如果分支不配对的话,需要用空语句...
  • C语言中语句表达式05. 宏中使用语句表达式06. Linux内核应用示例07. 附录 01. C语言的表达式 表达式和语句是 C 语言的基础概念。什么是表达式呢?表达式就是由一系列操作符操作数构成的式子。操作符可以是 C ...
  • C语言指针的定义及基本使用

    千次阅读 多人点赞 2020-05-11 22:13:32
    指针是C语言中一个非常重要的概念,也是C语言的特色之一。使用指针可以对复杂数据进行处理,能对计算机的内存分配进行...在C语言中,如果定义了一个变量,编译时就会根据该变量的类型给它分配相应大小的内存单元。
  • 会想到要归纳C语言的语法,起因是看题解的时候发现了这样一句话: if(sum>=n) sum-=(l*l),l++; 这里两个的两个赋值语句竟然可以用逗号分隔。这样就只需要一个分号,可以不加大括号了,太舒服了,因为我...
  • 要写好C语言,漂亮的宏定义是...软件开发过程,经常有一些常用或者通用的功能或者代码段,这些功能既可以写成函数,也可以封装成为宏定义。那么究竟是用函数好,还是宏定义好?这就要求我们对二者进行合理的取舍。
  • 第六节 语句标号goto语句及用goto语句构成的循环 5.6 break语句和continue语句在循环体的作用 C语言可以用如下语句实现循环 1. 用goto语句和if语句构成循环 2. 用while循环语句 3. 用do-while循环语句 4. 用for...
  • C语言定义

    2013-11-04 11:10:31
    这时,程序出现的是宏名,该程序被编译前,先将宏名用被定义的字符串替换,这称为宏替换,替换后才进行编译,宏替换是简单的替换。  C源程序进行编译时,实际经过了预处理、编译、汇编连接几个过程。 ...
  • 详解C语言中的宏定义

    千次阅读 2016-06-23 10:29:59
    1. 防止一个头文件被重复包含  [cpp] view plain copy  print? #ifndef COMDEF_H  ...2. 重新定义一些类型,防止由于各种平台编译器的不同,而产生的类型字节数差异,方便移植。 
  • 1、字符数组的定义与初始化 字符数组的初始化,最容易理解的方式就是逐个字符赋给数组各元素。 char str[10]={ 'I',' ','a','m',' ',‘h’,'a','p','p','y'}; 即把10个字符分别赋给str[0]到str[9]10个元素 如果花...
  • C语言中的预编译宏定义

    千次阅读 2015-01-14 16:25:16
    将一个C源程序转换为可执行程序的过程, 编译预处理是最初的步骤. 这一步骤是由预处理器(preprocessor)来完成的. 源流程序被编译器处理之前, 预处理器首先对源程序的"宏(macro)"进行处理.  C初学者可能对预...
  • C语言定义浅析

    2016-09-22 10:14:54
    动机前几天看到一个面试题:写一个“标准”宏MIN,这个宏输入两...平时也会用到宏定义,对于宏的理解还不特别深入,重新看了Kernighan的C程序设计语言关于宏部分,特次记录.宏定义的本质#define 名字 替换文本宏的
  • 再谈C语言定义

    千次阅读 2020-05-17 23:47:56
    文章目录再谈C语言定义简单的宏定义带参数宏定义定义中的###运算符#运算符##运算符删除宏定义常用预定义宏 简单的宏定义 简单宏定义格式 [关键字] [标识符] [替换列表] 关键字 #define 标识符 需要符合...
  • C语言中宏定义的使用

    万次阅读 多人点赞 2018-08-21 08:54:36
    1. 引言 1.1 宏定义的基本语法 1.2 宏定义的优点 1.3 宏定义的缺点 1.4 宏还是函数 2 使用宏时的注意点 2.1 算符优先级问题 2.2 分号吞噬问题 2.3 宏参数重复调用 ...3.3 可变宏:… _VA_ARGS 4 宏的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 29,765
精华内容 11,906
关键字:

在c语言中此定义和语句是合法的

c语言 订阅