当前位置: 首页 > news >正文

C语言入门4-函数和程序结构

函数举例

读取字符串,如果字符串中含有ould则输出该字符串,否则不输出。

#include <stdio.h>// 函数声明
int getLine(char s[], int lim);
int strindex(char s[], char t[]);int main() {char t[] = "ould";  // 要查找的目标子字符串char s[100];        // 存储输入行的字符数组,最大长度为 100// 循环读取输入的每一行,并进行处理while (getLine(s, 100) > 0) {  // 调用 getLine 函数获取一行输入,直到输入结束或达到最大限制if (strindex(s, t) != -1)  // 调用 strindex 函数查找子字符串 "ould"printf("%s", s);       // 如果找到,打印当前行}
}// 函数:从输入中获取一行字符,并存储在 s 中,最多存储 lim-1 个字符
int getLine(char s[], int lim) {int i, c;i = 0;while (--lim > 0 && (c = getchar()) != EOF && c != '\n')s[i++] = c;  // 将读取到的字符存储在数组 s 中if (c == '\n')s[i++] = c;  // 如果读取到换行符,也存储在数组 s 中s[i] = '\0';      // 添加字符串结束符return i;         // 返回当前行的字符个数(不包括结束符)
}// 函数:在字符串 s 中查找子字符串 t,如果找到返回第一个匹配的位置,否则返回 -1
int strindex(char s[], char t[]) {int i, j;for (i = 0; s[i] != '\0'; i++) {         // 遍历字符串 sfor (j = 0; s[i + j] == t[j] && t[j] != '\0'; j++);                                // 检查 s 中从位置 i 开始的子串是否与 t 匹配if (t[j] == '\0')                    // 如果 t 的末尾符号已达到return i;                        // 返回 t 在 s 中首次出现的位置}return -1;                                // 没有找到 t,则返回 -1
}

练习

  1. 编写函数strindex(s,t),返回 t 在 s 中最右边出现的位置,如果没有,则返回 -1。
// 函数:返回 t 在 s 中最右边出现的位置,如果不存在则返回 -1
int strindex(char s[], char t[]) {int i, j, index;index = -1;  // 初始化 index 为 -1,表示未找到 t 在 s 中的位置// 外循环遍历字符串 sfor (i = 0; s[i] != '\0'; i++) {// 内循环尝试在 s 的当前位置 i 开始查找与 t 匹配的子串for (j = 0; s[i + j] == t[j] && t[j] != '\0'; j++);  // 仅递增 j,直到找到不匹配的字符或者 t 的末尾符号// 如果 t 的末尾符号已经达到,说明在 s 中找到了与 t 完全匹配的子串if (t[j] == '\0') {index = i;  // 记录当前位置 i 为找到的最右边位置}}return index;  // 返回 t 在 s 中最右边出现的位置,或者 -1 如果未找到
}

返回值

举例
编写函数,将字符串转为double类型,并返回。

  • 第一种解决方案,分开处理整数部分和小数部分。
#include <stdio.h>
#include <ctype.h>double atof(char s[]);int main() {printf("%f\n", atof("-321.123"));
}double atof(char s[]) {int i, sign;double n;double decimal;n = 0;for (i = 0; isspace(s[i]); i++);sign = (s[i++] == '-') ? -1: 1;do {n = 10 * n + (s[i++] - '0');} while (s[i] != '\0' && s[i] != '.');if (s[i++] == '.') {decimal = 10;do {n += (s[i++] - '0') / decimal;decimal *= 10;} while (s[i] != '\0');}return sign * n;
}
  • 第二种解决方案,统一处理整数部分和小数部分。
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <float.h>double atof(char s[]);int main() {printf("%f\n", atof("-321.123"));  // 测试将字符串 "-321.123" 转换为浮点数并打印结果return 0;
}double atof(char s[]) {int i, sign;double n, power;// 跳过空白字符for (i = 0; isspace(s[i]); i++);// 确定符号位sign = (s[i] == '-') ? -1 : 1;if (s[i] == '+' || s[i] == '-')  // 跳过符号位i++;// 处理整数部分for (n = 0.0; isdigit(s[i]); i++)n = 10.0 * n + (s[i] - '0');// 处理小数部分if (s[i] == '.')i++;for (power = 1.0; isdigit(s[i]); i++) {n = 10.0 * n + (s[i] - '0');power *= 10.0;}return sign * n / power;
}
  • atof的基础上,编写atoi将会变得非常简单。
int atoi(char s[]) {return (int) atof(s);
}

练习

  1. 修改atof,使其可以转换由科学计数法表示的浮点数。(例如1.23e-3、-1.231e2)
#include <stdio.h>
#include <ctype.h>double atof(char s[]);int main() {printf("%f\n", atof("-321.123"));    // 测试普通的负浮点数printf("%f\n", atof("-3.123e2"));    // 测试科学计数法:-3.123 * 10^2printf("%f\n", atof("-3.123e-03")); // 测试科学计数法:-3.123 * 10^-3return 0;
}/** atof: 将字符串 s 转换为相应的双精度浮点数* 参数: s - 输入的字符串,可以包含整数、小数和科学计数法表示形式* 返回值: 对应的双精度浮点数*/
double atof(char s[]) {int i, sign, science_sign, e_n;double n;       // 存储尾数部分double power;   // 处理小数点和科学计数法中的幂次n = 0;e_n = 0;power = 1.0;    // 初始权重为1// 跳过空白字符for (i = 0; isspace(s[i]); i++);// 确定符号位sign = (s[i] == '-') ? -1 : 1;if (s[i] == '+' || s[i] == '-')i++;// 处理整数部分do {n = 10 * n + (s[i++] - '0');} while (isdigit(s[i]));// 处理小数部分if (s[i++] == '.') {for (; isdigit(s[i]); power *= 10)n = 10 * n + (s[i++] - '0');}n /= power;  // 将尾数部分除以权重,得到小数部分的值// 处理科学计数法部分if (s[i++] == 'e' || s[i++] == 'E') {science_sign = (s[i] == '-') ? -1 : 1;  // 确定科学计数法的指数符号if (s[i] == '+' || s[i] == '-')  // 跳过指数部分的符号位i++;// 处理指数部分do {e_n = 10 * e_n + (s[i++] - '0');} while (isdigit(s[i]));// 计算科学计数法中的指数部分的幂次for (int j = 0; j < e_n; j++)power *= 10;}// 返回最终结果,考虑科学计数法的符号影响return (science_sign == -1) ? sign * n / power : sign * n * power;
}

外部变量

内部变量相对立,可以简单的把外部变量理解为定义在函数外的变量,它在程序运行的期间一直存在。

下面是外部变量的应用。使用逆波兰表达式编写一个计算器,实现加减乘除的功能。逆波兰表达式(Reverse Polish Notation,RPN)是一种数学表达式的写法,其中操作符位于其操作数之后,而不是通常的中缀表示法中间。例如,表达式 3 + 4 在逆波兰表达式中表示为 3 4 +。

示例:

  • 中缀表达式 3 + 4 * 5 的逆波兰表示为 3 4 5 * +。
  • 中缀表达式 (1 + 2) * 3 - 4 的逆波兰表示为 1 2 + 3 * 4 -。
#include <stdio.h>
#include <stdlib.h>#define MAXOP 100  // 操作数和运算符的最大长度
#define NUMBER '0' // 表示获取到的是一个数字int getop(char []);
void push(double);
double pop();int main() {int type;double op2;char s[MAXOP];  // 用于存储获取的操作数或运算符的数组while ((type = getop(s)) != EOF) {  // 循环获取输入,直到遇到文件结尾switch (type) {case NUMBER:  // 如果是数字push(atof(s));  // 将字符串转换为浮点数并压入栈中break;case '+':  // 如果是加号push(pop() + pop());  // 弹出栈顶两个数相加后再压入栈中break;case '-':  // 如果是减号op2 = pop();  // 弹出第二个操作数push(pop() - op2);  // 弹出第一个操作数与第二个操作数相减后再压入栈中break;case '*':  // 如果是乘号push(pop() * pop());  // 弹出栈顶两个数相乘后再压入栈中break;case '/':  // 如果是除号op2 = pop();  // 弹出除数if (op2 != 0.0)push(pop() / op2);  // 弹出被除数与除数相除后再压入栈中(避免除数为零的情况)elseprintf("error: zero divisor\n");  // 若除数为零,则报错break;case '\n':  // 如果是换行符(表示结束一行计算)printf("\t%.8g\n", pop());  // 输出栈顶的结果(计算的最终结果)break;default:printf("error: unknown command %s\n", s);  // 若遇到未知命令,则报错break;}}
}#define MAXVAL 100  // 栈的最大深度int sp = 0;  // 栈顶指针,指向下一个空闲位置
double val[MAXVAL];  // 栈,用于存储操作数和中间结果void push(double f) {if (sp < MAXVAL)val[sp++] = f;  // 将操作数压入栈顶,并将栈顶指针向上移动elseprintf("error: stack full, can't push %g\n", f);  // 若栈已满,则报错
}double pop() {if (sp > 0)return val[--sp];  // 弹出栈顶操作数,并将栈顶指针向下移动else {printf("error: stack empty\n");  // 若栈为空,则报错return 0.0;  // 返回默认值}
}#include <ctype.h>int getch();
void ungetch(int);int getop(char s[]) {int i, c;while((s[0] = c = getch()) == ' ' || c == '\t');  // 跳过空白字符s[1] = '\0';if (!isdigit(c) && c != '.')  // 若不是数字且不是小数点return c;  // 直接返回运算符i = 0;if (isdigit(c))while (isdigit(s[++i] = c = getch()));  // 收集整数部分if (c == '.')while (isdigit(s[++i] = c = getch()));  // 收集小数部分s[i] = '\0';if (c != EOF)ungetch(c);  // 将多读入的字符放回输入缓冲区return NUMBER;  // 返回表示数字的标记
}#define BUFSIZE 100  // 输入缓冲区的大小char buf[BUFSIZE];  // 输入缓冲区
int bufp = 0;  // 输入缓冲区的下一个空闲位置int getch() {return (bufp > 0) ? buf[--bufp] : getchar();  // 获取字符(从缓冲区或标准输入中)
}void ungetch(int c) {if (bufp >= BUFSIZE)printf("ungetch: too many characters\n");  // 若缓冲区已满,则报错elsebuf[bufp++] = c;  // 将字符放回缓冲区
}

运行

1 2 - 4 5 + *-9
12 12 +24
12 121212error: stach empty0

程序核心原理:

while (next operator or operand is not end-of-file indicator)if (number)push itelse if (operator)pop operandsdo operationpush resultelse if (newline)pop and print top of stackelseerror

练习

  1. 为上面的程序添加取模运算,并且使其支持负数。
    支持负数
int getop(char s[]) {int i, c;// 跳过空白字符while ((s[0] = c = getch()) == ' ' || c == '\t');i = 0;if (c == '-' || c == '+') {  // 处理可能的负号或正号if (isdigit(c = getch())) {s[++i] = c;  // 如果后面紧跟着数字,则将符号和数字一起存入s数组} else {ungetch(c);  // 如果后面不是数字,则将字符放回输入缓冲区return s[0];  // 返回符号字符本身}}if (!isdigit(c) && c != '.')  // 如果不是数字且不是小数点,则直接返回该字符return c;if (isdigit(c))  // 收集整数部分while (isdigit(s[++i] = c = getch()));if (c == '.')  // 收集小数部分while (isdigit(s[++i] = c = getch()));s[i] = '\0';  // 添加字符串结束符if (c != EOF)ungetch(c);  // 将多读入的字符放回输入缓冲区return NUMBER;  // 返回表示数字的标记
}
  1. 支持命令:打印(输出堆栈中的数据)、复制(复制一个堆栈顶部数据)、交换(交换堆栈顶部的两个数据)、清理(清空堆栈数据)。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>// 常量定义
#define MAXOP 100    // 操作数或操作符的最大长度
#define NUMBER '0'   // 标识找到一个数
#define COMMAND 'C'  // 标识找到一个命令int getop(char []);
void push(double);
double pop();
int action(char []);// 主函数,执行逆波兰计算器
int main() {int type;           // 存储当前的操作符或操作数类型double op2;         // 存储第二个操作数char s[MAXOP];      // 存储输入的操作数或操作符// 主循环,处理输入while ((type = getop(s)) != EOF) {switch (type) {case COMMAND:  // 如果是命令,执行对应操作if (action(s) != 0)printf("error: unknow command %s\n", s);break;case NUMBER:  // 如果是数,将其压入栈中push(atof(s));break;case '+':  // 加法操作push(pop() + pop());break;case '-':  // 减法操作op2 = pop();push(pop() - op2);break;case '*':  // 乘法操作push(pop() * pop());break;case '/':  // 除法操作op2 = pop();if (op2 != 0.0)push(pop() / op2);elseprintf("error: zero divisor\n");break;case '%':  // 取模操作op2 = pop();if (op2 != 0)push((int) pop() % (int) op2);elseprintf("error: zero modulus\n");break;case '\n':  // 换行,输出栈顶元素的值printf("\t%.8g\n", pop());break;default:  // 未知操作符或操作数printf("error: unknown command %s\n", s);break;}}
}#define MAXVAL 100  // 栈的最大深度int sp = 0;          // 下一个空闲栈位置
double val[MAXVAL];  // 值栈// 将值压入栈中
void push(double f) {if (sp < MAXVAL)val[sp++] = f;elseprintf("error: stack full, can't push %g\n", f);
}// 从栈中弹出并返回值
double pop() {if (sp > 0)return val[--sp];else {printf("error: stack empty\n");return 0.0;}
}#include <ctype.h>int getch();
void ungetch(int);// 获取下一个操作数或操作符
int getop(char s[]) {int i, c;// 跳过空白字符while((s[0] = c = getch()) == ' ' || c == '\t');s[1] = '\0';i = 0;// 处理命令if (c >= 'a' && c <= 'z') {while (!isblank(s[++i] = c = getch()) && c != '\n');s[i] = '\0';if (c != EOF && c != '\n')ungetch(c);return COMMAND;}// 处理可能的负号或正号if (c == '-' || c == '+') {if (isdigit(c = getch())) {s[++i] = c;} else {ungetch(c);return s[0];}}// 处理数if (!isdigit(c) && c != '.')return c;if (isdigit(c))while (isdigit(s[++i] = c = getch()));if (c == '.')while (isdigit(s[++i] = c = getch()));s[i] = '\0';if (c != EOF)ungetch(c);return NUMBER;
}#define BUFSIZE 100char buf[BUFSIZE];  // 缓冲区,用于ungetch
int bufp = 0;       // 缓冲区指针// 获取下一个输入字符
int getch() {return (bufp > 0) ? buf[--bufp] : getchar();
}// 将字符放回输入中
void ungetch(int c) {if (bufp >= BUFSIZE)printf("ungetch: too many characters\n");elsebuf[bufp++] = c;
}// 定义命令字符串
#define PRINT "print"
#define DUPLICATE "duplicate"
#define SWAP "swap"
#define CLEAR "clear"// 执行命令
int action(char s[]) {int result, i;double op1, op2;result = -1;if ((result = strcmp(s, PRINT)) == 0) {for (i = 0; i < sp; i++)printf("\t%.8g", val[i]);printf("\n");} else if ((result = strcmp(s, DUPLICATE)) == 0) {op2 = pop();push(op2);push(op2);} else if ((result = strcmp(s, SWAP)) == 0) {op2 = pop();op1 = pop();push(op2);push(op1);} else if ((result = strcmp(s, CLEAR)) == 0) {sp = 0;}return result;
}

范围规则

局部变量的内存分配

  1. 声明时不分配存储空间

    • 当在函数内部声明局部变量时,编译器只会记录变量的名称、类型和作用域信息,并计算其在栈帧中的偏移量。此时并没有实际的内存分配。
    • 例如,在函数中声明一个局部变量 int localVar; 时,编译器知道 localVar 是一个 int 类型的变量,但没有立即为其分配内存。
  2. 函数调用时分配存储空间

    • 实际的内存分配发生在函数调用时。当函数被调用时,函数的栈帧在栈上被分配。栈帧包含了函数的所有局部变量的存储空间。
    • 当函数执行完毕并返回时,栈帧被销毁,局部变量的内存空间被释放。

在C语言中,全局变量在声明时确实会分配存储空间。以下是关于全局变量内存分配的详细解释:

全局变量的内存分配

  1. 声明和定义的区别

    • 声明:声明告诉编译器变量的类型和名字,但不分配存储空间。例如,在一个头文件中使用 extern 关键字声明一个变量,这仅仅是声明而不是定义:
      extern int globalVar;
      
    • 定义:定义实际分配存储空间。例如,在一个源文件中定义一个变量:
      int globalVar;
      
    • 当变量被定义时,存储空间会被分配。
  2. 分配时机

    • 全局变量在程序启动时分配存储空间,即在执行main函数之前,通常在程序加载到内存时由运行时系统完成。
    • 全局变量的存储空间分配在数据段(data segment)中,包括初始化的数据段(.data)和未初始化的数据段(.bss)。

示例代码

以下是一个全局变量的定义和使用示例:

#include <stdio.h>// 定义全局变量
int globalVar = 5;void exampleFunction() {printf("Global Variable: %d\n", globalVar);
}int main() {exampleFunction();return 0;
}

头文件

C语言中的头文件(Header Files)是以.h结尾的文件,主要用于声明函数、宏、常量和数据类型,以便在多个源文件中共享。

计算器程序拆分:
在这里插入图片描述

静态变量

在C语言中,static关键字用于声明静态变量。静态变量有几个重要特性和用途,具体如下:

局部静态变量

当在函数内部声明一个静态变量时,该变量的生命周期贯穿程序的整个运行时间,但它的作用域仍然是局部的。这意味着即使函数已经退出,该静态变量仍然保持其值,下次再次调用该函数时,变量不会重新初始化。

示例:
#include <stdio.h>void counter() {static int count = 0;  // 局部静态变量count++;printf("Count: %d\n", count);
}int main() {counter();  // 输出:Count: 1counter();  // 输出:Count: 2counter();  // 输出:Count: 3return 0;
}

在这个例子中,count变量在第一次调用counter函数时初始化为0,但在后续调用中,它保持其值而不是重新初始化。

全局静态变量

当在函数外部(即在文件的全局范围内)声明一个静态变量时,该变量的作用域被限制在声明它的文件内部。其他文件无法访问这个变量,即使使用extern关键字也不行。这在编写大型项目时非常有用,可以防止命名冲突。

示例:
// file1.c
static int globalVar = 0;  // 全局静态变量void modifyVar() {globalVar++;printf("globalVar in file1: %d\n", globalVar);
}// file2.c
extern void modifyVar();int main() {modifyVar();  // 输出:globalVar in file1: 1modifyVar();  // 输出:globalVar in file1: 2return 0;
}

在这个例子中,globalVar是一个静态全局变量,尽管file2.c调用了modifyVar函数,但它无法直接访问或修改globalVar

静态函数

除了静态变量,函数也可以被声明为静态的。静态函数只能在声明它们的文件中可见,这有助于实现文件级的封装,防止命名冲突。

示例:
// file1.c
static void staticFunction() {printf("This is a static function.\n");
}void publicFunction() {staticFunction();
}// file2.c
extern void publicFunction();int main() {publicFunction();  // 输出:This is a static function.// staticFunction();  // 错误:无法访问静态函数return 0;
}

在这个例子中,staticFunction只能在file1.c中被调用,其他文件无法调用它。

总结

static关键字在C语言中用于控制变量和函数的可见性和生命周期:

  • 局部静态变量:在函数内部声明,生命周期贯穿整个程序运行,但作用域局限于函数内。
  • 全局静态变量:在文件的全局范围内声明,作用域限于声明它的文件。
  • 静态函数:只能在声明它们的文件中可见。

使用static可以提高程序的模块化和封装性,减少命名冲突,并且在某些情况下可以提高程序的性能。

寄存器变量

在C语言中,register关键字用于声明变量为寄存器变量(register variables)。寄存器变量是一种提示,它告诉编译器将该变量存储在CPU寄存器中,以便快速访问和处理。然而,需要注意几点:

  1. 语法:声明一个寄存器变量的语法为在变量声明前加上register关键字,如下所示:

    register int x;
    
  2. 编译器提示register关键字只是对编译器的提示,它建议编译器将该变量存储在寄存器中,但并不强制。编译器可以选择忽略这个提示,特别是当寄存器变量的数量超过了可用的寄存器数量或者它不适合存储在寄存器中时。

  3. 无法取地址:不能对寄存器变量使用&运算符取地址,因为寄存器变量本身就可能不会在内存中有确切的地址。

  4. 使用场景:寄存器变量通常用于频繁访问和修改的局部变量,例如在循环中的计数器或者临时变量。它们的使用可以提高程序的执行速度,因为访问寄存器比访问内存要快。

  5. 效果限制:现代编译器通常能够根据优化算法自动决定哪些变量适合存储在寄存器中,因此register关键字的实际效果可能受限制或者无法感知到显著的性能改进。

总之,尽管register关键字曾经是一种常用的优化手段,但由于现代编译器的进步和优化策略,它的实际效果可能不如预期。因此,现代C程序员通常不会显式使用register关键字,而是依赖编译器自动进行优化。

初始化

在C语言中,变量的初始化是指在声明变量的同时为其赋予一个初始值。变量可以在声明时进行初始化,也可以在后续的代码中进行赋值操作。这里简要介绍C语言中变量初始化的几种方式和注意事项:

1. 声明时初始化

在声明变量的同时为其赋值,称为声明时初始化。示例如下:

int x = 10; // 声明一个整型变量x,并初始化为10
float y = 3.14f; // 声明一个浮点型变量y,并初始化为3.14
char ch = 'A'; // 声明一个字符型变量ch,并初始化为字符'A'

2. 后续赋值

变量可以在声明后的任何时候赋值,使用赋值运算符(=)将一个值赋给变量。示例如下:

int x; // 声明一个整型变量x
x = 20; // 给变量x赋值为20

3. 默认初始化

如果变量在声明时没有被显式初始化,它们将会被默认初始化。默认初始化的值取决于变量的存储类型和作用域:

  • 全局变量和静态变量:如果没有显式初始化,将会被初始化为0。
  • 局部变量:如果没有显式初始化,它们将包含一个随机值(未定义行为),因此在使用前最好显式初始化。

示例:

int global_var; // 全局变量,默认初始化为0void function() {static int static_var; // 静态变量,默认初始化为0int local_var; // 局部变量,未定义初始化值,可能包含随机值
}

4. 复合类型的初始化

  • 数组初始化
int arr[5] = {1, 2, 3, 4, 5}; // 声明一个包含5个元素的整型数组,并初始化

5. 字符串初始化

char str1[] = "Hello"; // 自动确定数组大小并初始化
char pattern[] = { 'o', 'u', 'l', 'd', '\0' };
char str2[10] = "Hello"; // 显式指定数组大小,初始化字符串

注意事项

  • 初始化与赋值的区别:初始化是在声明变量时给它一个初始值;赋值是在变量已经声明后修改其值。
  • 局部变量的初始化:局部变量如果没有显式初始化,其值是未定义的(不确定的),因此使用前最好显式初始化。
  • 全局变量和静态变量的初始化:它们如果没有显式初始化,默认会被初始化为0。

通过适当的初始化,可以确保变量在使用时具有正确的初始值,有助于避免潜在的错误和不确定行为。

递归

递归是指函数调用自身的过程。

示例:不借助printf的情况下,打印输入数字

/* printd: print n in decimal */
void printd(int n) {if (n < 0) {putchar('-');   // 如果n为负数,输出负号n = -n;         // 将n变为正数}if (n / 10)         // 如果n大于等于10,递归调用printd函数printd(n / 10);putchar(n % 10 + '0');  // 输出n的个位数字,将数字转换为字符
}

C预处理器

The C Preprocessor(C预处理器)是C语言编译过程中的一个重要组成部分,它在实际编译之前对源代码进行处理。预处理器指令由以 # 开头的命令组成,用于在编译之前执行一些文本替换和条件编译等操作。以下是C预处理器的常见用法和功能:

1. 包含文件 (#include)

#include 指令用于将外部文件的内容包含到当前文件中,通常用来包含标准库头文件或自定义头文件。

#include <stdio.h>  // 包含标准输入输出库的头文件
#include "myheader.h"  // 包含自定义头文件

2. 宏定义 (#define)

#define 指令用于定义宏,宏是一种简单的文本替换。宏定义通常用来定义常量、函数或代码片段。

#define PI 3.14159  // 定义常量PI
#define SQUARE(x) ((x) * (x))  // 定义宏函数计算平方
#define  forever  for (;;)    // 无限循环
#define  max(A, B)  ((A) > (B) ? (A) : (B)) // 函数

3. 条件编译 (#if, #ifdef, #ifndef, #endif)

条件编译指令用于根据条件包含或排除代码段。常见的条件编译指令有 #if, #ifdef, #ifndef#endif

#if !defined(HDR)
#define HDR
/* contents of hdr.h go here */
#endif

4. 条件语句

可以在 #if#ifdef 指令中使用 #include 来条件包含文件。

#if SYSTEM == SYSV
#define HDR "sysv.h"
#elif SYSTEM == BSD
#define HDR "bsd.h"
#elif SYSTEM == MSDOS
#define HDR "msdos.h"
#else
#define HDR "default.h"
#endif#include HDR

注意事项

  • 预处理器指令在编译前被处理,不是C语言的一部分,所以它们不受语法检查和类型检查的限制。
  • 使用预处理器可以增强代码的灵活性和可维护性,但过度使用可能会导致代码可读性降低和调试困难。
  • 宏定义和条件编译是预处理器最常见的用途,它们使得代码能够在不同平台和条件下进行编译。

综上所述,C预处理器提供了许多有用的工具和技术,可以在编译之前对源代码进行多种形式的处理,从而使得C语言在不同场景下具有更强的适应性和灵活性。

相关文章:

  • mysql 库存表 累计 sql语句 第一方法
  • Linux_软硬链接
  • RabbitMQ实践——交换器(Exchange)绑定交换器
  • ffmpeg 常用命令
  • 【鸿蒙】 模拟器运⾏
  • Java程序之动物声音“模拟器”
  • SpringMVC系列七: 手动实现SpringMVC底层机制-上
  • 5.3 Python len()函数:获取字符串长度或字节数
  • Dockerfile实战
  • Vue78-缓存路由组件
  • 深度学习:关于损失函数的一些前置知识(PyTorch Loss)
  • Python低溫半导体电子束量子波算法计算
  • excel按模板文件导出多个文件并压缩为ZIP格式返回前端
  • java小代码(1)
  • 前端技术栈三(vue+Axios)
  • ----------
  • JavaScript异步流程控制的前世今生
  • mysql外键的使用
  • mysql中InnoDB引擎中页的概念
  • Nodejs和JavaWeb协助开发
  • Phpstorm怎样批量删除空行?
  • Spark VS Hadoop:两大大数据分析系统深度解读
  • 给第三方使用接口的 URL 签名实现
  • 和 || 运算
  • 入门到放弃node系列之Hello Word篇
  • 十年未变!安全,谁之责?(下)
  • 为视图添加丝滑的水波纹
  • 自定义函数
  • 《码出高效》学习笔记与书中错误记录
  • 完善智慧办公建设,小熊U租获京东数千万元A+轮融资 ...
  • #include<初见C语言之指针(5)>
  • (附源码)springboot青少年公共卫生教育平台 毕业设计 643214
  • (附源码)计算机毕业设计SSM疫情下的学生出入管理系统
  • (附源码)计算机毕业设计大学生兼职系统
  • .net Stream篇(六)
  • .NET8.0 AOT 经验分享 FreeSql/FreeRedis/FreeScheduler 均已通过测试
  • .net经典笔试题
  • .NET面试题解析(11)-SQL语言基础及数据库基本原理
  • /etc/motd and /etc/issue
  • ::before和::after 常见的用法
  • [ vulhub漏洞复现篇 ] JBOSS AS 5.x/6.x反序列化远程代码执行漏洞CVE-2017-12149
  • [bug总结]: Feign调用GET请求找不到请求体实体类
  • [c#基础]DataTable的Select方法
  • [C++] 统计程序耗时
  • [CCIE历程]CCIE # 20604
  • [Contiki系列论文之2]WSN的自适应通信架构
  • [CTF]php is_numeric绕过
  • [Geek Challenge 2023] web题解
  • [HTML]Web前端开发技术7(HTML5、CSS3、JavaScript )CSS的定位机制——喵喵画网页
  • [jQuery]10 Things I Learned from the jQuery Source
  • [PHP源码阅读]empty和isset函数
  • [ruby on rails] array、jsonb字段
  • [USACO12DEC]逃跑的BarnRunning Away From…
  • [Verilog] Verilog 基本格式和语法
  • [VulnHub靶机渗透] Nullbyte