C语言学习-其他

预处理指令

预处理指令分为三种:宏定义、条件编译、文件包含

1) 所有的预处理指令都是以井号’#’开头 

2) 预处理指令在代码编译前执行

3) 预处理指令的位置可以随便写

4) 预处理指令的作用域:从编写指令的那一行开始,一直到文件结尾,或者人为结束

宏定义

定义:#define 宏名 值(结束宏:#undef 宏名)

宏名一般大写,变量名一般小写

宏定义分为三种:不带参数的宏、带参数的宏、空宏

  • 不带参数的宏定义

#define NUM 5

  • 带参数的宏定义

#define NUM(a,b) ((a)*(b))   //a,b为变量

注意:定义带参数的宏时,参数跟结果都要加小括号,否则可能会出错,如:a=c+d,b=c-d,则NUM(a,b)为c+d*c-d结果会出错

原因:因为宏定义执行时仅仅是将宏名替换为后面的值,并不运算,运算在编译运行时发生,如果不加小括号会导致运算出错

  • 定义空宏

#define NUM

使用宏定义替换时,双引号内""的宏不能被替换

#define COUNT 5
int main()
{
      char *name="COUNT";
}
//字符串name中的COUNT不能被替换

条件编译

基本用法

#if 条件1
...code1...
#elif 条件2
...code2...
#else
...code3...
#endif

1> 如果条件1成立,那么编译器就会把#if 与 #elif之间的code1代码编译进去(注意:是编译进去,不是执行,和平时用的if-else是不一样的)

2> 如果条件1不成立、条件2成立,那么编译器就会把#elif 与 #else之间的code2代码编译进去

3> 如果条件1、2都不成立,那么编译器就会把#else 与 #endif之间的code3编译进去

4> 注意,条件编译结束后,要在最后面加一个#endif,不然后果很严重

5> #if 和 #elif后面的条件一般是判断宏定义而不是判断变量,因为条件编译是在编译之前做的判断,宏定义也是编译之前定义的,而变量是在运行时才产生的、才有使用的意义

应用举例

#include <stdio.h>
#define MAX 11

int main()
{
     #if MAX==0
        printf("MAX为0");
     #elif MAX>0
        printf("MAX大于0");
     #else
        printf("MAX小于0");
     #endif
     return 0;
}

在第2行定义了一个宏MAX,当然在开发中这个MAX可能被定义在其他头文件中,现在只是为了方便演示,就写到main函数上面了。注意第6到第12行的条件编译语句。由于MAX为11,所以#elif的条件成立,第9行代码将会被编译进去,其实编译预处理后的代码是这样的:

/*stdio.h文件中的内容将会代替#include <stdio.h>的位置*/
int main()
{
      printf("MAX大于0");
      return 0;
}
输出结果:MAX大于0

其他用法
#if defined()和#if !defined()的用法

#if 和 #elif后面的条件不仅仅可以用来判断宏的值,还可以判断是否定义过某个宏。比如:

#if defined(MAX)
      ...code...
#endif

如果前面已经定义过MAX这个宏,就将code编译进去。它不会管MAX的值是多少,只要定义过MAX,条件就成立。

条件也可以取反:

#if !defined(MAX)
          ...code...
#endif
如果前面没有定义过MAX这个宏,就将code编译进去。

#ifdef和#ifndef的使用
#ifdef的使用和#if defined()的用法基本一致

#ifdef MAX
       ...code...
#endif

如果前面已经定义过MAX这个宏,就将code编译进去。

#ifndef又和#if !defined()的用法基本一致

#ifndef MAX
       ...code...
#endif

如果前面没有定义过MAX这个宏,就将code编译进去。

文件包含

一般形式

第1种形式#include <文件名>

第2种形式 #include "文件名"

<>尖括号表示系统自带的文件,””双引号表示自定义的文件

使用注意
  • 文件包含不允许循环包含(互相包含)
  • 使用#include指令可能导致多次包含同一个头文件,降低编译效率

解决方法:#ifndef TEMP

                  #define TEMP

                   此处为防止重复的声明

                 #endif

解释:如果没有定义TEMP,则进入条件编译,定义TEMP并执行“防止重复的声明”一次,然后结束条件编译,下次再调用时TEMP宏定义已存在,将不会进入条件编译,从而防止被重复声明

typedef

作用:给已经存在的类型起一个新的名称

使用场合:

  • 基本数据类型

  • 指针

  • 结构体

  • 枚举

  • 指向函数的指针

#include <stdio.h>

typedef int Integer;
typedef unsigned int UInteger;
typedef float Float;

int main()
{
     Integer a=-1;  //此时Integer即相当于int
     UInteger b=2;  //此时UInteger即相当于unsigned int
     Float c=3;  //此时Float即相当于float
     printf("%d,%d,%.2f",a,b,c); 
     return 0;
}

在第3、第4、第5行分别给int、unsigned int、float起了个别名,然后在main函数中使用别名定义变量,用来跟原来的基本类型是完全一样的。输出结果:-1,2,3.00

给类型起别名后,原来的int、float还是可以正常使用的:

int a=9;
float b=1.25;
也可以在别名的基础上再起一个别名

typedef int Integer;
typedef Integer MyInteger;

typedef与指针

除开可以给基本数据类型起别名,typedef也可以给指针起别名

#include <stdio.h>

typedef char *String;

int main()
{
     String str="itcast";  //此时String即相当于char *
}

typedef与结构体

给结构体起别名可以使代码更加简洁明

默认情况下结构体变量的使用

 //定义结构体类型
struct Point{
      float x;
      float y;
};

int main()
{
      //定义结构体变量
      struct Point p;
      p.x=1.2;
      p.y=1.5;
      return 0;
}
默认情况下,定义结构体变量需要带struct关键字

使用typedef给结构体起别名

//定义结构体类型
struct Point{
       float x;
       float y;
};

//起别名
typedef Point MyPoint;

int main()
{
      MyPoint p;  //此时MyPoint相当于struct Point
      p.x=1.2;
      p.y=1.5;
      return 0;
}
在第7行给结构体Point起了个别名叫做MyPoint,然后在11行使用MyPoint定义了一个结构体变量p,不用再带上struct关键字了
其实第1~第7行的代码可以简写为:

//定义结构体类型的同时起别名
typedef struct Point{
        float x;
        float y;
} MyPoint;
甚至可以省略结构体名称:

//可省略结构体类型名
typedef struct{
       float x;
       float y
} MyPoint;

typedef与指向结构体的指针

typedef可以给指针、结构体起别名,当然也可以给指向结构体的指针起别名

#include <stdio.h>

//定义结构体类型并起别名
typedef struct{
       float x;
       float y;
} MyPoint;

//给结构体指针起别名
typedef MyPoint *PP;

int main()
{
     MyPoint p={10.0,20.0};
     //利用别名定义结构体指针a
     PP a;
     //结构体指针a指向结构体p
     a=&p;
     printf("%f,%f",a->x,a->y);
     return 0;
}

在第4行定义了一个结构体,顺便起了个别名叫MyPoint,第10行为指向结构体的指针定义了别名PP。然后在main函数中使用这2个别名。

输出结果:10.000000,20.000000

typedef与枚举类型

使用typedef给枚举类型起别名也可以使代码简洁。

//定义枚举类型
enum Season{
     spring,
     summer,
     autumn,
     winter
};
//起别名
typedef enum Season Season;

int main()
{
       //定义枚举变量
      Season s=spring;
      return 0;
}


在第2行定义了枚举类型,在第9行起了别名为Season,然后在第11行直接使用别名定义枚举变量,不用再带上enum关键字了。

第1行~第9行代码可以简化为:

typedef enum Season{
          spring,
          summer,
          autumn,
          winter
} Season;

甚至可以省略枚举名称,简化为:

typedef enum{
       spring,
       summer,
       autumn,
       winter
}  Season;

typedef与指向函数的指针

先来回顾下函数指针的知识

#include <stdio.h>

//定义一个求和函数
int sum(int a,int b)
{
      return a+b;
}

int main()
{
     //定义一个指向函数的指针同时指向sum函数
     int (*p)(int,int)=sum;
     
     //利用指针p调用sum函数
     int a=(*p)(2,3);
     printf("sum=%d",a);
     return 0;
}
在第4行定义了一个sum函数,第12行定义了一个指向sum函数的指针变量p,可以发现,这个指针变量p的定义比一般的指针变量看来复杂多了,不利于理解。
第15行调用了p指向的sum函数,输出结果:sum=5

为了简化代码和方便理解,我们可以使用typedef给指向函数的指针类型起别名

#include <stdio.h>

//定义一个求和函数
int sum(int a,int b)
{
      return a+b;
}

//起别名
typedef int (*MySum)(int,int);

int main()
{
     //利用别名定义一个指向函数的指针同时指向sum函数
     Mysum p=sum;
     
     //利用指针p调用sum函数
     int a=(*p)(2,3);
     printf("sum=%d",a);
     return 0;
}
看第9行,意思是:给指向函数的指针类型,起了个别名叫MySum,被指向的函数接收2个int类型的参数,返回值为int类型。
在第14行直接用别名MySum定义一个指向sum函数的指针变量p,这样看起来简单舒服多了。第17行的函数调用是一样的。

typedef与#define

用#define 代替typedef时,定义指针是不能被替代的

#include <stdio.h>

//用typedef给char *起别名
typedef char *String;

//用#define替换char *
#define String2 char *

int mian()
{
     //用typedef起的别名定义
     String str1,str2;  //定义了两个字符串指针str1、str2
    
     //用#define替换定义
     String2 str3,str4  //表示定义了字符串指针str3,定义了字符变量str4.因为#define仅仅是替换,并不是类型别名定义,替换后为char * a,b;而*只跟a结合,并不会同时与b结合。
}
第12行表示定义了两个字符串指针str1、str2

第15行表示定义了字符串指针str3,定义了字符变量str4

因为#define仅仅是替换,并不是类型别名定义,替换后即为:char *str3,str4;

所以,以后给类型起别名,最好使用typedef,而不是使用#define

static与extern

static和extern对函数的作用

外部函数(extern):定义的函数能被本文件和其他文件访问。

1. 默认情况下所有函数都是外部函数。

2. 不允许有同名的外部函数。

内部函数(static):定义的函数只能被本文件访问,其他文件不能访问。

1. 允许不同文件中有同名的内部函数。

static对函数的作用:

1. 完整的定义一个内部函数

2. 完整的声明一个内部函数

extern 对函数的作用:

1. 完整的定义一个外部函数

2. 完整的声明一个外部函数

(extern 可以省略,默认情况下声明和定义的函数都是外部函数)

static和extern对变量的作用

static和extern对全局变量的作用

static对全局变量的作用:

定义一个内部变量

extern对全局变量的作用:

声明一个外部变量,完整的变量声明需要用extern关键字

注意:extren 只能声明外部变量,不能完整定义外部变量,extern修饰的全局变量只是声明,需再定义后方能使用

全局变量分2种:

外部变量:定义的全局变量能被本文件和其它文件访问。

1. 默认情况下,所有的全局变量都是外部变量

2.全局变量可以重复定义,同一文件中的同名全局变量代表同一变量

3.不同文件中的同名外部变量,也都代表同一个变量。

4.可以将全局变量声明为局部变量后再使用

内部变量:定义的全局变量只能被本文件访问,不能被其他文件访问。

1. 不同文件中的同名内部变量,互不影响。

static 对局部变量的作用
  • static 修饰局部变量:

1. 延长局部变量的生命周期:直到程序结束的时候,局部变量才会被销毁。

2. 不改变局部变量的作用域。

举例:

//被static修饰的局部变量一直存在,直到程序结束

void test()
{
       int a=0;
       a++;
       printf(“a的值是%d\n”,a);

       //static修饰局部变量b
       static int b=0;
       b++;
       printf(“b的值是%d\n”,b);
}

int mian()
{

       test();
       test();
       return 0;
}

输出结果为:

a的值是1  b的值是1

a的值是1  b的值是2

  • static修饰局部变量的使用场合:

1. 如果某个函数的调用频率很高。

2. 这个函数内部的某个变量值是固定不变的。

递归

递归的两个条件:

1) 函数自己调用自己(return 函数自身)

2) 必须有个明确的返回值



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值