C语言编程(1)—— 预处理命令

预处理

什么是预处理

 在编译之前对源文件进行简单加工的过程,就称为预处理(即预先处理、提前处理)。以#开头的命令为预处理命令。
 预处理是C语言的一个重要功能,由预处理程序完成。当对一个源文件进行编译时,系统将自动调用预处理程序对源程序中的预处理部分作处理,处理完毕自动进入对源程序的编译。编译器会将预处理的结果保存到和源文件同名的.i文件中,例如 main.c 的预处理结果在 main.i 中。和.c一样,.i也是文本文件,可以用编辑器打开直接查看内容。

#include

功能特点

 #include 为文件包含命令,用来引入对应的头文件。#include 的处理过程就是将头文件的内容插入到该命令所在的位置,从而把头文件和当前源文件连接成一个源文件,这与复制粘贴的效果相同。

<> 与“”的区别

 使用尖括号< >和双引号" “的区别在于头文件的搜索路径不同:
 使用尖括号< >,编译器会到系统路径下查找头文件;比如标准库文件等
 使用双引号” ",编译器首先在当前目录下查找头文件,如果没有找到,再到系统路径下查找。一般情况我们自定义的头文件用双引号引用。

注意

 同一个头文件可以被多次引入,但是多次引入的效果和一次引入的效果相同,因为头文件在代码层面有防止重复引入的机制
 文件包含允许嵌套,即在一个被包含的文件中又可以包含另一个文件。
 不管是标准头文件,还是自定义头文件,都只能包含变量和函数的声明,不能包含定义。在头文件中定义定义函数和全局变量这种认知是原则性的错误!否则在多次引入时会引起重复定义错误。

#define

功能特点

 #define 叫做宏定义命令。即用一个标识符来表示一个字符串,如果在后面的代码中出现了该标识符,那么就全部替换成指定的字符串。
 宏定义的本质是在编译之前进行简单的替换,不对表达式进行任何的计算。
 宏定义中的宏名是标识符的一种,命名规则和变量相同。字符串可以是数字、表达式、if 语句、函数
 宏定义不是说明或语句,在行末不必加分号,如加上分号则连分号也一起替换
 宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。如要终止其作用域可使用#undef命令

带参数的宏定义

带参宏定义的一般形式:#define 宏名(形参列表) 字符串
带参宏调用的一般形式为:宏名(实参列表);
特点:
 a、对带参数的宏,在展开过程中不仅要进行字符串替换,还要用实参去替换形参(先替换字符串、再替换参数)
 b、在带参宏定义中,不会为形式参数分配内存,因此不必指明数据类型.

带参宏定义和函数的区别

区别:
 a、宏展开仅仅是字符串的替换,不会对表达式进行计算。且宏在编译之前就被处理掉了,它没有机会参与编译,也不会占用内存。
 b、函数是一段可以重复使用的代码,会被编译,会给它分配内存,每次调用函数,就是执行这块内存中的代码。

宏参数的字符串化和宏参数的连接(#和##)

#的用法和特点

 #用来将宏参数转换为字符串,即在宏参数的开头和末尾添加引号
实例:

#include <stdio.h>

#define S(string) #string

int main()
{
	printf("%s\n", S(abcdefg)); 	// 打印输出 "abcdefg"
	printf("%s\n", S("abcdefg"));   // 打印输出 "\"abcdefg\""
	return 0;
}

##的用法和特点

 ##称为连接符,用来将宏参数或其它的字符串连接起来

#include <stdion.h>

#define CONNECT_1(a, b) a##e##b##
#define CONNECT_2(a, b) a##b##56

int main()
{
	printf("%f\n", CONNECT_1(1.234, 3));	// 被展开为1.234e3    打印输出 1234.000000
	printf("%d\n", CONNECT_2(12, 34));	    // 被展开为123456     打印输出 123456
	return 0;
}

条件编译

什么是条件编译

  能够根据不同情况编译不同代码、产生不同目标文件的机制,称为条件编译。条件编译是预处理程序的功能,不是编译器的功能

实例

  a、输出红色的文字,并且要求跨平台,在 Windows 和 Linux 下都能运行,这个程序的难点在于,不同平台下控制文字颜色的代码不一样,我们必须要能够识别出不同的平台。Windows 有专有的宏_WIN32,Linux 有专有的宏__linux__

#include <stdio.h>
int main(){
    #if _WIN32
        system("color 0c");
        printf("http://c.biancheng.net\n");
    #elif __linux__
        printf("\033[22;31mhttp://c.biancheng.net\n\033[22;30m");
    #else
        printf("http://c.biancheng.net\n");
    #endif

    return 0;
}

  b、VS/VC 有两种编译模式,Debug 和 Release。在学习过程中,我们通常使用 Debug 模式,这样便于程序的调试;而最终发布的程序,要使用 Release 模式,这样编译器会进行很多优化,提高程序运行效率,删除冗余信息。

#include <stdio.h>
#include <stdlib.h>
int main(){
    #ifdef _DEBUG
        printf("正在使用 Debug 模式编译程序...\n");
    #else
        printf("正在使用 Release 模式编译程序...\n");
    #endif

    system("pause");
    return 0;
}

  当以 Debug 模式编译程序时,宏 _DEBUG 会被定义,预处器会保留第 5 行代码,删除第 7 行代码。反之会删除第 5 行,保留第 7 行。

#if 用法的一般格式

#if 整型常量表达式1
  程序段1
#elif 整型常量表达式2
  程序段2
#elif 整型常量表达式3
  程序段3
#else
  程序段4
#endif

#ifdef 用法的一般格式为:

#ifdef 宏名
  程序段1
#else
  程序段2
#endif

#ifndef 用法的一般格式为:

#ifndef 宏名
  程序段1
#else
  程序段2
#endif

注意:#if 后面跟的是“整型常量表达式”,而 #ifdef 和 #ifndef 后面跟的只能是一个宏名,不能是其他的

#error命令

功能特点

  #error 指令用于在编译期间产生错误信息,并阻止程序的编译。形式为

#error error_message

实例

  a、我们的程序针对Linux编写,不保证兼容Windows

#ifdef WIN32
#error This programme cannot compile at Windows Platform
#endif

  WIN32 是Windows下的预定义宏。当用户在Windows下编译该程序时,由于定义了WIN32这个宏,所以会执行 #error 命令,提示用户发生了编译错误,错误信息是:

This programme cannot compile at Windows Platform

  b、当我们希望以C++的方式来编译程序

#ifndef __cplusplus
#error 当前程序必须以C++方式编译
#endif

注意:报错信息不需要加引号 " " ,如果加上,引号会被一起输出

C语言中几个常用的预定义宏

 __ LINE__:   表示当前源代码的行号;
 __ FILE__:   表示当前源文件的名称;
 __ DATE__:  表示当前的编译日期;
 __ TIME__:   表示当前的编译时间;
 __ STDC__:  当要求程序严格遵循ANSI C标准时该标识被赋值为1;
 __ cplusplus: 当编写C++程序时该标识符被定义。
 __ FUNCTION__(或者__func__):表示当前的调用的函数名称

附录

在这里插入图片描述

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目 录 第1章 C语言 8 1.1 什么是局部程序块(local block)? 8 1.2 可以把变量保存在局部程序块中吗? 9 1.3 什么时候用一条switch语句比用多条if语句更好? 9 1.4 switch语句必须包含default分支吗? 10 1.5 switch语句的最后一个分支可以不要break语句吗? 11 1.6 除了在for语句中之外,在哪些情况下还要使用逗号运算符? 11 1.7 怎样才能知道循环是否提前结束了? 13 1.8 goto,longjmp()和setjmp()之间有什么区别? 13 1.9 什么是左值(lvaule)? 15 1.10 数组(array)可以是左值吗? 15 1.11 什么是右值(rvaule)? 16 1.12 运算符的优先级总能保证是“自左至右”或“自右至左”的顺序吗? 17 1.13 ++var和var++有什么区别? 17 1.14 取模运算符(modulus operator)“%”的作用是什么? 17 第2章 变量和数据存储 18 2.1. 变量存储在内存(memory)中的什么地方? 18 2.2. 变量必须初始化吗? 19 2.3. 什么是页抖动(pagethrashing)? 19 2.4. 什么是const指针? 20 2.5. 什么时候应该使用register修饰符?它真的有用吗? 21 2.6. 什么时候应该使用volatile修饰符? 21 2.7. 一个变量可以同时被说明为const和volatile吗? 22 2.8. 什么时候应该使用const修饰符? 23 2.9. 浮点数比较(floating-point comparisons)的可靠性如何? 23 2.10. 怎样判断一个数字型变量可以容纳的最大值? 24 2.11. 对不同类型的变量进行算术运算会有问题吗? 25 2.12. 什么是运算符升级(operatorpromotion)? 25 2.13. 什么时候应该使用类型强制转换(typecast)? 26 2.14. 什么时候不应该使用类型强制转换(typecast)? 27 2.15. 可以在头文件中说明或定义变量吗? 27 2.16. 说明一个变量和定义一个变量有什么区别? 27 2.17. 可以在头文件中说明static变量吗? 28 2.18. 用const说明常量有什么好处? 28 第3章 排序与查找 28 排序 28 查找 29 排序或查找性能? 30 3.1. 哪一种排序方法最方便? 32 3.2. 哪一种排序方法最快? 33 3.3. 对外存(磁盘或磁带)中而不是内存中的数据进行排序称为外部排序。 39 3.4. 1哪一种查找方法最方便? 44 3.5. 1哪一种查找方法最快? 46 3.6. 1什么是哈希查找? 51 3.7. 1怎样对链表进行排序? 53 3.8. 1怎样查找链表中的数据? 53 第4章 数据文件 59 4.1. 当errno为一个非零值时,是否有错误发生? 59 4.2. 什么是流(stream)? 59 4.3. 怎样重定向一个标准流? 60 4.4. 怎样恢复一个重定向了的标准流? 60 4.5. stdout能被强制打印到非屏幕设备上吗? 61 4.6. 文本模式(textmode)和二进制模式(binarymode)有什么区别? 61 4.7. 怎样判断是使用流函数还是使用低级函数? 62 4.8. 怎样列出某个目录下的文件? 62 4.9. 怎样列出一个文件的日期和时间? 63 4.10. 怎样对某个目录下的文件名进行排序? 66 4.11. 怎样判断一个文件的属性? 67 4.12. 怎样查看PATH环境变量? 69 4.13. 怎样打开一个同时能被其它程序修改的文件? 69 4.14. 怎样确保只有你的程序能存取一个文件? 71 4.15. 怎样防止其它程序修改你正在修改的那部分文件内容? 71 4.16. 怎样一次打开20个以上的文件? 72 4.17. 怎样避开"Abort,Retry,Fail”消息? 72 4.18. 怎样读写以逗号分界的本? 74 第5章 编译预处理 76 5.1. 什么是(macro)?怎样使用? 76 5.2. 预处理程序(preprocessor)有什么作用? 77 5.3. 怎样避免多次包含同一个头文件? 79 5.4. 可以用#include指令包含类型名不是".h"的文件吗? 80 5.5. 用#define指令说明常量有什么好处? 80 5.6. 用enum关键字说明常量有什么好处? 81 5.7. 与用#define指令说明常量相比,用enum关键字说明常量有什么好处? 81 5.8. 如何使部分程序在

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值