C语言——宏、预处理、多文件
1 宏定义
#define _CRT_SECURE_NO_WARNINGS //vs内置宏-->对编译环境做操作,解决scanf函数,不用scanf_S
#include <stdio.h>
//1.宏替换
//-->符号常量
#define MAX 100
//宏魔法的说法
//瞎搞着玩玩
#define 整数 int
#define 主函数 int main()
#define 开始 {
#define 结束 }
主函数
开始
return 0;
结束
-----------------------------------------------------------------------------------------
//2.宏函数——>(和普通函数有什么区别,可以忽略参数类型)类型的泛化操作
//代参的宏
#define Max(a,b) ((a)>(b)?(a):(b))//最好带括号
//不带括号的宏
#define M(a) a+a
#define B(a) ((a)+(a))
-----------------------------------------------------------------------------------------
//3.多行宏——>替换语句
#define print_value(data) \//本来要写在一行,为了代码方便看可以用'\'链接符链接起来,用tab键, 别用空格
if(data%2==0){ \
printf("是偶数\n"); \
} \
else { \
printf("是奇数\n"); \
} \
-----------------------------------------------------------------------------------------
//4.带do_while(0)的宏——>限制作用域
#define test do{int num=0;}while(0)
-----------------------------------------------------------------------------------------
//5.系统自带宏
void test_func()
{
//双下划线 __FILE__
printf("文件URL:%s\n", __FILE__);
printf("行号:%d\n", __LINE__);
printf("日期:%s\n", __DATE__);
printf("时间:%s\n", __TIME__);
//编码方式
_WIN32;
_WIN64;
UNICODE;
//__linux__
//__GNUC__
//__UTF8__;
}
-----------------------------------------------------------------------------------------
int main()
{
//1
printf("%d\n", MAX);
int num1= 0;
//2
//泛型: 类型的泛化操作
printf("%d\n", Max(1, 2)); //((1)>(2)?(1):(2))
printf("%lf\n",Max(1.1, 2.2));
printf("%c\n", Max('a', 'b'));
//经典题型
int result = M(1 + 2) * 4;
//没有括号是单纯的替换
//1+2+1+2*4=12
printf("result=%d\n", result);//打印12
result = B(1 + 2) * 4;
printf("result=%d\n", result);//打印24
//3
print_value(10);
//4
int num = 999;//宏里面定义了num,自已也想定义num,就会冲突。-->用do_while(0)限制作用域
test;
printf("%d\n", num);
test_func();
scanf("%d", &num);
return 0;
}
2 条件编译
不是在程序的时候完成的,是在编译的时候就完成了–>通常不用变量作为条件判定的,可以用宏定义
目前来说可能有点鸡肋,这在处理不同操作系统、不同硬件平台或不同编译器时非常有用。条件编译通常使用预处理器指令来实现。
#include <stdio.h>
#include <stdlib.h>
#define MAX 100
//1
/*
#if
#if #else
#if #elif ... #else (elif就是else if)
#endif 必须作为结尾
*/
-----------------------------------------------------------------------------------------
//2.针对宏判定的操作
/*
#ifdef -->判定宏是否存在
#ifndef -->判定宏不存在
#undef -->取消宏
*/
void test_func()
{
#define NUM 10
printf("%d\n", NUM);
#ifdef NUM
printf("NUM是一个宏!\n");
#endif
#ifndef NUM
#define NUM 20
#endif
#undef NUM//取消宏
//错误 宏被取消了
//printf("%d\n", NUM);
}
-----------------------------------------------------------------------------------------
//3.编码设置
#ifdef UNICODE//如果定义了
#undef UNICODE//就取消定义
#endif
#ifdef __UTF8__
#endif
//4.跨平台操作(平台不同,库可能也不同)
#ifdef _WIN32
//---->
//#include <WinSock.h>
#elif __APPLE__
//---->各种
#elif _linux_
//-->
#endif
-----------------------------------------------------------------------------------------
int main()
{
//1
#if 0
printf("条件不成立 不执行\n");
#endif
int a = 1;
int b = 3;
//通常是不能用变量作为条件判断
//符号常量可以用来作为条件判断
#if a+b<2
printf("这里不成立!\n");
#endif
#if MAX==100
printf("MAX=100\n");
#elif MAX>100
printf("MAX>100\n");
#else
printf("MAX<100\n");
#endif
return 0;
}
3 编译指示
对编译器做操作
#pragma once//头文件只编译一次
//1.防止重复编译问题
#include <stdio.h>
#include <windows.h>
#include <mmsystem.h>
-----------------------------------------------------------------------------------------
//2.输出指定信息
#pragma message("你好帅!!!!")//在编译器编译的时候在输出栏里面打印,不会打印在控制台
-----------------------------------------------------------------------------------------
//3.忽略警告
#pragma warning(disable: 4996)
-----------------------------------------------------------------------------------------
//4.强制报错-->输出栏里产生未知的错误,但并不会让程序终端
#pragma error("强制报错!")//-->warning C4068: 未知的杂注“error”
-----------------------------------------------------------------------------------------
//5.引入特定库
#pragma comment(lib,"winmm.lib")//多媒体库,是在windows环境下的
-----------------------------------------------------------------------------------------
//6.设置对齐方式
#pragma pack(4)
struct Data
{
int age;
double num;
};
//7.#define 与 # ##
//转换为字符串
#define to_string(a) #a //单#可以把括号里的值直接转为字符串
//连接符
#define create_name(num) name##num//双##,相当于链接符
-----------------------------------------------------------------------------------------
int main()
{
//3 报错,错误代码c4996
int a = 1;
// error C4996
//scanf("%d", &a);
//6
printf("%zd\n", sizeof(struct Data));
//7
puts(to_string(12345)); //to_string(12345) ==> "12345"
int num = 123456;
//不能传变量
puts(to_string(num)); //to_string(num)==>"num"
int create_name(1) = 1234; //create_name(1) ==>name1
printf("%d\n", name1);
return 0;
}
4 多文件操作
问题1
可以多个源文件和头文件,一般写项目,一个源文件对应一个头文件
#pragma once如果没有,会出现重定义问题
//Node.h
#pragma once//自己要写头文件,系统自动添加的
#include <stdio.h>
#include <stdlib.h>
typedef struct Node
{
int data;
struct Node* next;
}Node;
//1 alt+enter-->快速创建定义
Node* create_node(int data);
//2 ctr+- 回到上一步
void print_list(Node* list);
//3
一般是一个.h 对应一个.c
.h 写的是声明
.c 写的实现
//4
.h不可能存在这种交叉的形式,会陷入递归
如果存在,就是设计有问题,需要重新设计
//5
注意extern 和 static 和 #pragma once 还有memset
#ifndef 大写文件名
#define 大写文件名
#endif
避免文件名重复存在,在标准库里这种操作很常见
//Node.c
#include "Node.h"
Node* create_node(int data)
{
return NULL;
}
void print_list(Node* list)
{
printf("测试代码!\n");
}
//test.c
int test_num = 999;
void print_test()
{
printf("没有.h的源文件!\n");
}
static void print_static()
{
printf("只能当前函数使用!\n");
}
//多文件操作.c
#include "Node.h"
extern int test_num; //extern 声明外部变量
extern void print_test();
extern void print_static(); //外部文件不可使用——>用static修饰了
int main()
{
Node* list = create_node(1);
print_list(list);
print_test();
printf("外部变量:%d\n", test_num);
//print_static();——> fatal error LNK1120: 1 个无法解析的外部命令
return 0;
}
问题2
头文件
//Student.h
#pragma once
//error C2011: “Student”:“struct”类型重定义
//
#ifndef _STUDENT_H_
#define _STUDENT_H_
#endif
typedef struct Student
{
char name[20];
int age;
int num;
}Student;
void print_student(Student stu);
//Node.h
#pragma once
#ifndef _NODE_H_
#define _NODE_H_
#endif
typedef struct Student Student;
typedef struct Node
{
Student* data; //用指针不用包含头文件,typedef struct Student Student;声明就行
struct Node* next;
}Node;
Node* create_node(Student* data);
//Array.h
#pragma once
#ifndef _ARRAY_H_
#define _ARRAY_H_
#endif
#include "Student.h"
typedef struct Array
{
Student arr[3];
int curSize;
}Array;
void print_array(Array data);
.c文件
//Student.c
#include "Student.h"
#include <stdio.h>
void print_student(Student stu)
{
printf("打印学生信息!\n");
}
//Node.c
#include "Node.h"
#include "Student.h"
#include <stdio.h>
Node* create_node(Student* data)
{
return NULL;
}
//Array.c
#include "Array.h"
#include <stdio.h>
void print_array(Array data)
{
printf("打印array测试!\n");
}
//main.c
#include <stdio.h>
#include "Array.h"
#include "Node.h"
#include "Student.h"
#include <string.h>//memset函数
int main()
{
Node* list = create_node(NULL);
Array arr;
memset(&arr, 0, sizeof(Array));//初始化一下
print_array(arr);
return 0;
}
交叉包含错误
.h不能互相包含
.c可以互相包含
头文件
//head1.h
#pragma once
#include <stdio.h>
#include "head2.h"
void print1();
//head2.h
#pragma once
#include <stdio.h>
#include "head1.h"
void print2();
源文件
//head1.c
#include "head1.h"
void print1()
{
printf("测试代码1!\n");
}
//head2.c
#include "head2.h"
void print2()
{
printf("测试函数2!\n");
}
//test.c
#include "head1.h"
#include "head2.h"
//error C1014: 包含文件太多: 深度 = 1024
int main()
{
return 0;
}