预处理命令(#)
以**#**号开头的命令称为预处理命令。
C语言源文件需要经过编译、链接后才能生成可执行程序。
#include
**#include**叫作文件包含命令,用来引入对于的头文件(**.h**文件)。#include的处理过程很简单,就是将头文件的内容插入到该命令所在的位置,从而把头文件和当前源文件连接成一个源文件,这与复制粘贴的效果相同。
用法
方法1: #include <stdio.h>方法2: #include "stdio.h"
- 使用尖括号
**< >**,编译器会到系统路径下查找头文件; - 使用双引号
**" "**,编译器首先在当前目录下查找头文件,如果没有找到,再到系统路径下查找; - stdio.h这类标准头文件一般存放在系统路径下,而自己编写的头文件,一般存放在当前项目路径下,所以不能使用
**< >**,只能使用**" "**; - 头文件只能包含变量和函数声明,不能包含定义,否则多次引入时会出现重复定义的错误。
宏定义命令(#define)
宏定义只是简单的字符串替换,由预处理器进行替换处理;
typedef是在编译阶段由编译器处理的,并不是简单的字符串替换,是给原有的数据类型起一定新的名字,作为一种新的数据类型。宏定义形式
```cdefine 宏名 字符串
其中,宏定义中的字符串可以是数字、表达式、if语句、函数等。
<a name="ksrih"></a>### #define用法的说明- 宏定义不是说明或语句,行末不需要加分号;- 宏定义必须写在函数外面,其作用域是宏定义命令起,一直到源程序结束。- **如果需要中途终止其作用域,需要使用**`**#undef**`**命令**。- **代码中的宏名如果被引号包围,那么预处理程序不对其作宏替代**;```c#include <stdio.h>#define OK 100int main(){printf("OK\n"); //打印结果是“OK”而非100return 0;}
宏定义允许嵌套;
#define A 100#define B A*100
习惯上,宏名用大写字母表示,以便于与变量区别(允许小写字母);
- 可用宏定义表示数据类型;
```c
define UINT unsigned int
UNIT a,b;
<a name="iofHE"></a>### 带参数的宏定义**对带参数的宏,在展开的过程中不仅要进行字符串替换,还要用实参去替代形参**。<a name="MmIfw"></a>#### 带参数宏定义的形式```c#define 宏名(形参列表) 字符串其中,字符串中可以含有各个形参。
带参数宏定义的调用
宏名(实参列表);
举例
/* 用带参数宏定义实现找出两个数据的最大值 */#include <stdio.h>#define MAX(a, b) ((a > b) ? a : b)int main(void){int i, j, max;printf("请连续输入两个数字,按空格分开: ");scanf("%d %d", &i, &j);max = MAX(i, j);printf("max = %d\n", max);return 0;}
带参数宏定义的说明
带参数宏定义中,形参之间可以出现空格,但宏名和形参列表之间不能有空格;
如果:#define MAX(a, b) ((a > b) ? a : b)写成:#define MAX (a, b) ((a > b) ? a : b)编译器将认为MAX是一个无参数宏定义,宏调用结果将是:(a, b)((a > b) ? a : b)
在带参数宏定义中,不会为形参分配内存空间,因此不必指定数据类型;
- 在宏定义中,字符串的形参通常要用括号括起来,以避免出错;
```c
示例1:
include
define A(a) ((a) * (a))
int main(void) { int i, j; printf(“请输入数字: “); scanf(“%d”, &i); j = A(i + 1); printf(“test你好!!!!\n”); printf(“max = %d\n”, j); return 0; } //输入3 //输出16
include
define A(a) a *a
int main(void) { int i, j; printf(“请输入数字: “); scanf(“%d”, &i); j = A(i + 1); printf(“test你好!!!!\n”); printf(“max = %d\n”, j); return 0; } //输入3 //输出7
<a name="N6lGH"></a>### 宏参数的字符串化(#)和连接(##)<a name="LPUiF"></a>#### 宏参数的字符串化(#)<a name="c3oIU"></a>##### 定义形式```c#define STR(s) #s
用法
**#**用来将宏参数转换为字符串,即在宏参数的开头和末尾添加引号。
#include <stdio.h>#define STR(s) #sint main(void){printf("test你好!!!!\n");printf("%s\n", STR(abc)); //实参不添加引号,输出abcprintf("%s\n", STR("abc")); //实参添加引号,输出"abc"return 0;}
宏参数的连接(##)
定义形式
#define CONN1(a,b) a##e##b#define CONN2(a,b) a##b##00
用法
##称为连接符,用来将宏参数或其他的字符串连接起来。
#include <stdio.h>#define CONN1(a, b) a##e##b#define CONN2(a, b) a##b##00int main(void){printf("test你好!!!!\n");printf("%f\n", CONN1(8.8, 2)); //输出880.000000printf("%d\n", CONN2(12, 34)); //输出123400return 0;}
C语言中的预定义宏
ANSI C规定了以下几个预定义宏:__LINE__:表示当前代码的行号;__FILE__:表示当前文件的名称;__DATE__:表示当前的编译日期;__TIME__:表示当前的编译时间__STDC__:表示当要求程序严格遵循ANSI C标准时,该标识被赋值为1;__cplusplus:表示当编写C++程序时,该标识符被定义。示例:#include <stdio.h>#include <stdlib.h>int main(){printf("Date : %s\n", __DATE__);printf("Time : %s\n", __TIME__);printf("File : %s\n", __FILE__);printf("Line : %d\n", __LINE__);system("pause");return 0;}
条件编译
能够根据不同情况编译不同代码、产生不同目标文件的机制,称为条件编译。条件编译是预处理程序的功能,不是编译器的功能。
Windows有专用的宏_Win32,Linux有专用的宏__linux__。
VS/VC有两种编译模式,Debug和Release。调试阶段用Debug模式;最终发布程序用Release模式,该模式下,编译器会进行很多优化,提高程序运行效率,删除冗余信息。
#if
/* #if的用法 */#if 整型常量表达式1 //常量表达式中不能包含变量,且为整型程序段1#elif 整型常量表达式2 //#elif可省略程序段2#elif 整型常量表达式3程序段3#else //#else可省略程序段4#endif
#ifdef
/* #ifdef的用法 */#ifdef 宏名 //只能是宏名,不能是其他程序段1 //宏被定义执行#else //#else可省略程序段2#endif
#ifndef
/* #ifndef的用法 */#ifndef 宏名 //只能是宏名,不能是其他程序段1 //宏未被定义执行#else //#else可省略程序段2#endif
#error命令
**#error**命令用于在编译期间产生错误信息,并阻止程序的编译。
定义形式
#error error_msg
用法
示例1:#include <stdio.h>#ifdef WIN32#error 这个程序不能在mac上运行!!#endifint main(void){printf("test你好!!!!\n");return 0;}示例2:#include <stdio.h>#ifndef __cplusplus#error 这个程序不必须以C++方式编译!!#endifint main(void){printf("test你好!!!!\n");return 0;}
typedef
定义形式
typedef oldName newName; //末尾需要分号示例://常规定义别名typedef int INTEGER;INTEGER a,b;a = 1;b = 2;//给数组定义别名typedef char ARR20[20]; //ARR20是char [20]的新别名ARR20 a1; //等价于char a1[20];//给结构体定义别名typedef struct stu{char name[20];int age;char sex;}STU;STU body; //等价于struct stu body;//给指针类型定义别名typedef int (*POINT_TO_ARR)[4]; //POINT_TO_ARR是int *[4]的新别名,是一个二维数组指针类型POINT_TO_ARR p; //等价于int *p[4]; //二维数组指针//给函数指针类型定义别名typedef int (*POINT_TO_FUNC)(int, int);POINT_TO_FUNC pfunc;
用法
示例(为指针类型定义别名):#include <stdio.h>typedef char (*POINT_TO_ARR)[30]; //二维数组指针typedef int (*POINT_TO_FUNC)(int, int); //函数指针char str[3][30] ={"aaa","bbb","ccc"};int max(int a, int b){return ((a > b) ? a : b);}int main(void){POINT_TO_ARR parr = str;POINT_TO_FUNC pfunc = max;printf("test你好!!!!\n");printf("max = %d\n",(*pfunc)(3,4)); //输出4for (int i = 0; i < 3; i++){printf("str[%d]: %s\n", i, *(parr + i)); //读取二维数组列的数据}return 0;}
typedef与#define的区别
typedef int INTEGER; unsigned INTEGER n; //error
- 在连续定义多个变量时,`typedef`可以保证定义的所有变量均为同一类型,但`#define`不可以;```c#define PTR_INT int *PTR_INT p1,p2; //等价于int *p1;int p2;typedef int * PTR_INTPTR_INT p1,p2; //等价于int *p1;int *p2;
const变量
**const**变量被称为常量,它的值不能被改变,在整个作用域中保持固定,不可被修改。
定义形式
const type Name = value; //常量在创建后不可修改,初始化时要赋值其中,type是数据类型,name是变量名,在const中,type和name的位置可以互换,即:type const Name = value;
const与指针
**const**和指针一起使用时,可以限制指针变量本身,也可以限制指针指向的数据。
const int *p1; //指针所指向的数据是只读的,但p1本身的值可被修改int const *p2; //同上int * const p3; //指针变量是只读的,p3本身的值不能被修改const int * const p4; //指针和它所指向的数据都是只读的int const * const p5; //同上
记忆技巧
**const**后面紧接着指针变量名(如**int * const p3**)==>指针变量是只读的;存在两个**const**既描述数据类型,又描述指针变量(如**const int * const p4**)==>指针变量和它所指向的数据是只读的;剩下的情况,则表示的是指针所指向的数据是只读的。
const与函数形参
const通常用在函数的形参中,如果形参是一个指针,为了防止字函数内部修改指针所指向的数据,就可以用**const**来限制。
#include <stdio.h>#include <string.h>size_t strnch(const char *str, char ch){int count = 0;int len = strlen(str);for (int i = 0; i < len; i++){if (str[i] == ch){count++;}}return count;}int main(void){char *str = "fjsalfjalgvjljklfjgjklgslkajkl";char ch = 'a';int i = strnch(str, ch);printf("%d\n", i);return 0;}
随机数
一般使用**<stdlib.h>**头文件中的**rand()**函数来生成随机数。
定义形式
int rand(void);
- rand()函数可以生成一个0~RAND_MAX之间的随机整数;
- RAND_MAX是
头文件中的一个宏,是随机数的最大值,C语言标准并没有规定它的具体数值,只是规定它的值至少为32767。 ```c include
include
int main(){ int a = rand(); printf(“%d\n”,a); return 0; }
<a name="P4GCw"></a>## 随机数的本质rand()函数产生的随机数,**本质是伪随机数**,是根据一个数值按照某个公式推算出来的,这个数值称之为“种子”。种子和随机数之间的关系是一个正态分布。<br /><br />**种子在每次启动计算机的时候随机生成,一旦计算机启动后,它就不再变化**。<a name="flwqr"></a>## 重新播种**通过srand()函数来重新“播种”**。<a name="AtE3u"></a>### srand()的定义形式```cvoid srand(unsigned int seed);
使用时间作为参数生成随机数
/* 使用time()函数前需要包含time.h头文件 */srand((unsigned)time(NULL)); //使用rand()之前先播种int a = rand(); //生成随机数
生产一定范围的随机数
int a = rand() % 10; //产生0~9的随机数int a = rand() % 51 + 13; //产生13~63的随机数
连续生成随机数
#include <stdio.h>#include <stdlib.h>#include <time.h>int main(){int a, i;srand((unsigned)time(NULL)); //播种必须放在循环外//使用for循环生成10个随机数for (i = 0; i < 10; i++){// srand((unsigned)time(NULL));a = rand();printf("%d\n", a);}return 0;}
