内容纲要

预处理

在进行编译的第一遍扫描(词法扫描和语法分析)之前所作的工作。预处理是C语言的一个重要功能,是由预处理程序完成的。对一个源文件进行编译时,系统将自动引用预处理程序对源程序中的预处理部分作处理,处理完毕自动进入对源程序的编译。

预处理指令

指令 描述
#define 定义宏
#include 包含一个源代码文件
#undef 取消已定义的宏
#ifdef 如果宏已经定义,则返回真
#ifndef 如果宏没有定义,则返回真
#if 如果给定条件为真,则编译下面代码
#else #if 的替代方案
#elif 如果前面的 #if 给定条件不为真,当前条件为真,则编译下面代码
#endif 结束一个 #if……#else 条件编译块
#error 当遇到标准错误时,输出错误消息
#pragma 使用标准化方法,向编译器发布特殊的命令到编译器中

宏定义

用一个标识符来表示一个字符串,称为"宏"。在预编译处理时,将程序中定义的宏名用定义宏名的字符串去替换,称为“宏替换“。宏替换可以是常量也可以是表达式,如果在宏替换后不必加上;,若加上会一起替换。宏定义必须定义到函数之外,要终止其作用域后使用#undef指令。

无参宏定义

定义:

#define 标识符 字符串

例:

#include <stdio.h>

#define N x+x*y
int main()
{
    int x = 2, y = 1;
    printf("%d",N*N);
    //x+x*y*x+x*y=2+2*1*2+2*1=8
    return 0;
}

运行结果:

8

如上所示,N是通过字符串进行直接替换,而不是通常意义(x+x*y)*(x+x*y)=16。

有参宏定义

定义:

#define 宏名(形参列表) 字符串

例:

#include <stdio.h>

#define M(x) x>10?1:0
int main()
{
    int x = 2;
    printf("%d",M(x));

    return 0;
}

运行结果:

0

带参宏定义中宏名和形参直接不能有空格出现,形参不分配内存单元,不必做类型定义。宏调用中实参有具体的值去替换形参,必须作类型说明。

文件包含

文件包含命令是把指定的文件插入到命令行的位置取代改命令行,把指定的文件和当前的源程序文件连成一个源文件。

  1. 使用尖括号表示在包含文件中去查找(包含目录是由用户在设置环境时设置的),不去在源文件目录去查找。
#include <文件名>
  1. 使用双引号表示首先在当前源文件目录上查找,若未找到才到包含目录上去。
#include "文件名"

条件编译

预处理程序提供了条件编译的功能,按不同的条件去编译不同的程序部分,产生不同的目标代码。

定义:

#条件编译指令 条件
//语句
#else
//语句
#endif

例:

#include <stdio.h>

#define N 1
int main()
{
    #ifdef N
        int a[N];
    #else
        int a[1];
    #endif // N
    a[0] = 1;
    printf("%d",a[0]);

    return 0;
}

运行结果:

1

指针

指针可以用来表示各种数据结构,通过指针可以很轻松的进行各种中数据操作。

定义

数据类型 *指针变量名;

引用

指针

取地址

int a = 0;
int *p = &a;

指针p保存着a变量的地址,即为0xa950,通过取值符&来获得a的地址。

用值

#include<stdio.h>

int main()
{
   int a = 0;
   int *p = &a;
   *p = 1;
   printf("p的值为:%p,*&p的值为%d,a的值为:%d",p,*&p,a);
   return 0;
}

运行结果:

p的值为:000000000061FE14,*&p的值为000000000061FE14,a的值为:1

通过*运算符进行取值,与&相反。

数组指针

在一个数组中,由连续的存储空间组成,数组名就是由数组第一个元素的首地址。

#include<stdio.h>

int main()
{
   int a[10];
   *a = 1;
   printf("%d",a[0]);
   return 0;
}

运行结果:

1

所以*a=a[0],a=&a[0],两者相同。可用指针来进行数组的赋值运算,如*(a++)

二维数组

#include<stdio.h>

int main()
{
   int a[3][3];

   printf("a[0][0]=%d,a=%d\n",&a[0][0],a);
   printf("a[0][1]=%d,a[0]+1=%d\n",&a[0][1],*(a)+1/*a[0]+1*/);
   printf("a[1][0]=%d,a+1=%d\n",&a[1][0],a+1);
   return 0;
}

运行结果:

a[0][0]=6422000,a=6422000
a[0][1]=6422004,a[0]+1=6422004
a[1][0]=6422012,a+1=6422012

多维数组

#include<stdio.h>

int main()
{
   int b[3][3] = {1,2,3,4,5,6,7,8,9};
   int (*a)[3];
   a=b;
   printf("%d ",a[0][0]);
   printf("%d",a[1][0]);

   return 0;
}

运行结果:

1 4

其中int (*a)[3]中的括弧不能缺,若不加括号则表示为一个指针数组。其中的a是一个指针变量,代表一个指针变量里有一个一维数组。其中a+i表示指向了一维数组的首地址,*(a+i)+j则表示二维数组a[i][j]的地址。

字符串指针

#include<stdio.h>

int main()
{
   char str[] = "123";
   char *s = "556";
   /*
   char *s;
   s = "556";
   数组字符串不可通过str="123"来进行赋值操作
   */
   printf("str=%s,str[1]=%c\n",str,str[1]);
   printf("s=%s,s+1=%c",s,*(s+1));
   return 0;
}

运行结果:

str=123,str[1]=2
s=556,s+1=5

通过指针可以更灵活的给字符串进行赋值操作,字符串指针变量本身是一个指针变量用来存储字符串的首地址,在字符串中以’\0’来进行结尾操作,而字符串数组本身由若干元素组成,来进行整个字符串的存储。

函数指针

定义:

数据类型 (*函数指针变量名)(数据类型.....)
#include<stdio.h>

int add(int a,int b){
    return a+b;
}

int main()
{
   int (*p)(int,int) = add;
   printf("%d",(*p)(1,2));
   return 0;
}

运行结果:

3

回调函数

回调函数是指函数指针做为一个函数的参数来进行调用。

#include <stdio.h>

int add(int a,int b){
    return a+b;
}

int sum(int a,int b,int (*ad)(int,int)){
    return (*ad)(a,b);
}

int main()
{
    printf("%d",sum(1,2,add));
    return 0;
}

运行结果:

3

指针类型

定义 含义
int i 定义整型变量i
int *p p为指向整型数据的指针变量
int a[n] 定义整型数组a,有n个元素
int *p[n] 定义指针数组p,它由n个指向整型数据类型的指针元素组成
int (*p)[n] p为指向含有n个元素的一维数组的指针变量
int f() f为带回整型函数值的函数
int *p() p为带回一个指针值的函数,该指针指向整型数据类型
int (*p)() p为指向函数的指针,该函数返回一个整型值
int **p p是一个指针变量,它指向一个指向整型数据类型的指针变量
最后修改日期: 2020年11月27日

作者

留言

撰写回覆或留言

发布留言必须填写的电子邮件地址不会公开。