首页 > 其他分享 >[笔记]C Primer Plus 第16章:C预处理器和C库

[笔记]C Primer Plus 第16章:C预处理器和C库

时间:2024-03-16 18:45:07浏览次数:22  
标签:include 函数 16 int ap Plus LIMIT Primer define

16.1 翻译程序的第一步

在预处理之前,编译器会进行一些翻译处理:

  1. 处理多字节字符和“三字符序列”(目前已很少使用),映射到源字符集;

  2. 将两个物理行(physical line)转换为一个逻辑行(logical line):

转换前:

printf("Wonder\
ful");

转换后:printf("Wonderful");

  1. 用空格替换注释,用1个空格替换所有空白字符(不包括换行符)。

16.2 明示常量:#define

有类对象宏(object-like macro)、类函数宏(function-like macro)。

每行#define由3部分组成——#define指令、宏和替换体:#define PX printf("x == %d\n", x),从宏替换为最终文本的过程称为宏展开(macro expansion)

双引号中的宏不会被展开:

#define TWO 2
printf("TWO");     //输出TWO
printf("%s", TWO); //输出2

宏可以用来指定const对象的初始值、const对象数组的大小等(而const却无法指定,见下例):

#define DEF_LIMIT 20
const int CONST_LIM = 50;
static int data1[DEF_LIMIT];  //有效
static int data2[CONST_LIM];  //无效
const int VAL1 = 2 * DEF_LIMIT;  //有效
const int VAL2 = 2 * CONST_LIM;  //无效

16.2.1 记号

宏的替换体可以看作记号(token)型字符串。比如#define SIX 2*2中有1个记号(2*2);#define SIX 2 * 3中有3个记号(2*3)。

16.2.2 重定义常量

在ANSI标准中,只有新定义和旧定义完全相同才允许重定义:

#define SIX 2 * 3
#define SIX 2 * 3 //允许
#define SIX 2*3   //不允许,token数量不同

如果需要重定义,使用#undef指令。

16.3 在#define中使用参数

类函数宏:#define MEAN(X, Y) ((X)+(Y)/2)

直接替换会产生一些运算上的问题,比如:

#define SQUARE(X) X*X

SQUARE(x+2) //预期为(x+2)^2,实际被替换为"x+2*x+2",即3x+2
//解决方法:参数带括号,如#define SQUARE(X) (X)*(X)

100/SQUARE(x) //预期为100/x^2,实际被替换为"100/x*x",即100
//解决方法:替换体带括号,如#define SQUARE(X) ((X)*(X))

//所以在#define MEAN(X, Y) ((X)+(Y)/2)中,参数和替换体都带了括号

SQUARE(++x) //预期为(x+1)^2,被替换为++x*++x,即(x+1)(x+2)
//暂无解决方法,不要在类函数宏中使用递增或递减运算符

16.3.1 用宏参数创建字符串:#运算符

C字符串的串联特性:printf("a" "b" "c");的输出是abc。

预处理运算符#可以将记号替换为字符串(称为“字符串化”(stringizing)):

#define PRINT_SQUARE(X) printf("The square of " #x "is %d\n", ((x)*(x)))

y=2;
PRINT_SQUARE(y);     //宏展开将#x替换为y,    输出"The square of y is 4"
PRINT_SQUARE(2 + 4); //宏展开将#x替换为2 + 4,输出"The square of 2 + 4 is 36"

16.3.2 预处理器粘合剂:##运算符

将2个记号组合成1个记号,如:#define XNAME(n) x ## n,则XNAME(4)将展开为x4,可以用于变量命名。

16.3.3 变参宏:...和__VA_ARGS__

以三个点"..."的方式表示,使类函数宏的参数数量可变:

#define PRINT(...) printf(__VA_ARGS__)

PRINT("ABC");
PRINT("VAL=%d, str=%s", 2, "Helloworld");

16.4 宏和函数的选择

多次调用时,函数只产生1份副本,节省空间,然而需要多次跳转,耗费时间;宏需要插入多次,耗费空间,然而无需跳转,节省时间。
简单的函数一般用宏定义。调用次数少,宏的效果不明显;调用次数多(如嵌套循环中),用宏有助于提高效率。

16.5 文件包含:#include

在头文件中,只声明不定义!

extern的作用是使一个文件中的全局变量或函数可以在其他文件中使用,实现共享。

步骤:

  1. 在file1.c中定义全局变量和函数(可供其他文件使用):
int val = 5;
int fun(){ return 0; }
  1. 在header.h中声明相关变量和函数:
extern int val = 5;
extern int fun();
  1. 在file2.c文件中包含.h头文件:#include "header.h"

  2. 编译时,只需要编译连接.c文件:gcc -o file file1.c file2.c

原理:

#include的原理是将头文件整个地复制到#include的位置,所以可以理解,编译连接时只用到.c文件(不用.h)。所以extern会使程序在其他.c文件中寻找定义。

img

16.6 其他指令

16.6.1 #undef

取消之前的#define定义。即使之前没有定义过,#undef也有效,所以当想使用宏定义却不确定之前是否定义过时,为了安全,仍可使用#undef

16.6.3 条件编译

1. #ifdef#else#endif

#ifdef#else#endif,可用于调试程序代码:

#define DEBUG

int main(){
    int total = 0;

    for (int i=0;i<10;i++){
        total += 1;
#ifdef DEBUG
        printf("current value:%d", total);
#endif
    }

    printf("total value:%d", total);
}

不调试时去掉#define DEBUG,调试时加上即可,从而不用去掉printf()调试代码。

2. #ifndef

①防止重复包含

如果.c文件同时包含了A.h和B.h,B.h又包含了A.h,则造成重复包含。为了避免重复包含,需要在.h文件开头加上#ifndef HEADER_NAME_H#endif(HEADER_NAME_H可任意更改,只要是唯一的能标识当前.h文件即可,一般用头文件名+_H)。(补充说明:在.h文件开头添加的原因是,包含.h文件相当于整个地复制到.c文件中)

②便于调试代码

#ifndef也可以用于方便代码调试,比如header.h中有:

#ifndef LIMIT
    #define LIMIT 100
#endif

则在包含了该头文件的.c文件中,可通过如下方式,测试较小的LIMIT数值:

#define LIMIT 10     //临时测试
#include "header.h"

或者用#undef

#include "header.h"
#undef LIMIT       //临时测试
#define LIMIT 10   //临时测试

这样就无需在header.h中修改LIMIT的值。

3. #if#elif

#if#elif后接整型常量表达式,示例:

#if SYS == 1
#include "ibm.h"
#elif SYS == 2
#include "vax.h"
#else
#include "general.h"
#endif

16.6.4 预定义宏

预定义宏 含义
DATE 当前日期,格式为 "MMM DD YYYY"
TIME 当前时间,格式为 "HH:MM:SS"
FILE 当前源文件的文件名
LINE 当前代码行号
STDC 如果编译器遵循 ANSI 标准,则为 1
__cplusplus 如果编译器用于 C++,则定义为一个数字

__func__是预定义标识符,展开为代表函数名的字符串。

16.6.5 #line#error

#line:重置行号和文件名。

#line 100
#line 10 "cool.c"

#error:发出一条错误消息。

#if __STDC_VERSION__ != 201112L
#error Not C11
#endif

16.6.6 #pragma(编译器设置)

16.6.7 泛型选择(C11)

泛型编程(generic programming)的代码没有特定类型,可以转换为指定类型。C11引入了泛型选择表达式,可以根据表达式的类型选择1个值。示例:

_Generic(x, int: 0, float: 1, double: 2, default: 3)

结合#define使用示例:

#define MYTYPE(X) _Generic((X), int: "int", float: "float", double: "double", default: "others")

当X为int类型,宏展开为字符串"int";X为float类型,宏展开为字符串"float";等等。

16.7 内联函数

编译器可能会将函数调用直接替换为内联代码。内联函数应当比较短小。如果多个文件需要用到同一内联函数,可以在头文件中定义内联函数(一般头文件只声明、不定义,内联函数是特例)。

16.8 _Noreturn

16.9 C库

16.10 数学库

16.11 通用工具库

16.11.1 exit()atexit()

16.11.2 qsort()(快速排序)

16.12 断言库

16.13 string.h库中的memcpy()memmove()

函数原型:

void* memcpy(void* restrict s1, const void* restrict s2, size_t n);
void* memmove(void* s1, const void* s2, size_t n);

从s2指向的位置拷贝到s1指向的位置。memcpy()不允许s1和s2内存区域有重叠;memmove()则没有这样的要求。

16.14 可变参数:stdarg.h

可变参数的函数,参数列表以"..."结尾,"..."之前的1个参数是可变参数的数量(如下面例子中的num)。

#include <stdarg.h>

int sum(int num, ...){
    va_list ap;            //初始化va_list对象ap
    va_list ap_copy;
    va_start(ap, num);     //传入va_list对象ap和可变参数的数量num,使ap初始化
    va_copy(ap_copy, ap);  //将ap拷贝给ap_copy

    int sum=0;
    for (int i=0;i<num;i++)
        sum+=va_arg(ap, double);  //传入va_list对象ap和当前参数的类型
        //va_arg的第n次调用返回第n个可变参数
        //由于不支持退回之前的参数,所以需要ap_copy进行备份

    va_end(ap);      //清理工作
    va_end(ap_copy);
    return sum;
    }

标签:include,函数,16,int,ap,Plus,LIMIT,Primer,define
From: https://www.cnblogs.com/EndPoem-ZH/p/18076993

相关文章

  • P1116 车厢重组 洛谷
    附加AC代码噢噢噢噢噢噢噢噢噢噢噢噢噢噢噢噢哦哦哦!#车厢重组##题目描述在一个旧式的火车站旁边有一座桥,其桥面可以绕河中心的桥墩水平旋转。一个车站的职工发现桥的长度最多能容纳两节车厢,如果将桥旋转180 度,则可以把相邻两节车厢的位置交换,用这种方法可以重新排列车......
  • 3.16 模拟赛 T2 记录
    早上打的一场模拟赛,场上没想出来,下来看题解感觉还挺板的?操作比较复杂,考虑线段树,把\(n\)变为\(2^k\),这样就可以和树状数组操作的区间\([i-lowbit(i)+1,i]\)一一对应,因为此时线段树的每一层的每一段区间都一定是\(2\)的整数倍。维护两个标记一个是区间加标记,一个树状数组......
  • MybatisPlus[新]逆向工程,代码生成器
    MybatisPlus旧版本的代码生成器官方新版已经不在维护了.并在新版中,将内部的构造方法改成了private,导致新版本的myabtis-plus无法使用旧版本的代码生成器.下列配置是新版本的代码生成配置添加依赖<!--代码自动生成器依赖--><dependency><groupId>com.baomidou</......
  • 2024-03-16:用go语言,给你一个正整数数组 nums, 每一次操作中,你可以从 nums 中选择 任意
    2024-03-16:用go语言,给你一个正整数数组nums,每一次操作中,你可以从nums中选择任意一个数并将它减小到恰好一半。(注意,在后续操作中你可以对减半过的数继续执行操作)请你返回将nums数组和至少减少一半的最少操作数。输入:nums=[5,19,8,1]。输出:3。答案2024-03-16:......
  • Educational Codeforces Round 163 A-E
    A.SpecialCharacters构造。形如\(A\)和\(B\)这类单个字符构成的字符串对答案的贡献为\(0\),而\(AA\)和\(AAAA\)这类多个相同字符构成的字符串对答案的贡献固定为\(2\)​,则无法构造出奇数值,由第二类字符串拼接即可构造出偶数值。时间复杂度:\(O(n)\)。#include<bit......
  • 3.16
    关于今天早起由于换新的作息,提前半个小时起床,导致我太困根本没醒,\(7:50\)宿管给我喊醒这件事……不是为啥我上课还困啊?上午第五节课,超课表的写了个“公自(A)”,于是\(HZOI2024\)初中全体集结来机房。还有\(3.14\)模拟赛,是真没人过来告诉我一声啊,\(14:30\)看到有个进行中,十......
  • mybatis-plus代码生成
    添加依赖:<!--代码自动生成器依赖--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.5.3.1</version></dependency><dependency><gro......
  • 首师大附中集训D7日报(20231216)-总结部分
    今天讲dp,切题9/28,dp是一个庞杂的,成体系的知识,因此今日总结不以题解为主,而以知识点和涉及到的例题为主,题解参考笔记和pptDP1.背包问题都到了这个层次了背板子不可能有问题,主要是对于背包问题本质的理解。背包问题的转移说白了就是最普通的线性转移,并且是表现出无序性的,比如这道......
  • #16 2024.3.11
    糖丸了。638.The2ndUniversalCup.Stage17:JinanD?L没想到吧我先写了这个题。I?A我觉得很神秘的题啊,猜了个结论不知道为什么过了/yun。G?K简单slopetrick。M弱智几何题。E有点意思的flow,但是也挺好想的。B省选2023D1T2的弱化版(?我不太记得那个题了。......
  • 3月16日
    3月16日简报,星期六,农历二月初七, 1、中央网信办:2024年将重点整治蓄意造谣抹黑企业、企业家,以“舆论监督”名义对企业进行敲诈勒索等问题。2、梅菜扣肉预制菜原材料竟是劣质槽头肉!315晚会曝光。3、未来交通在雄安:打造全国首个规模化区域级数字化道路,无人驾驶公交已投运。4、上......