黑马程序员-IOS-C语言预处理指令及杂项小计

-------------------------------------- android培训java培训ios培训期待与您交流! ---------------------------------

在C语言中,对源程序进行编译之前会对一些特殊的预处理指令作解释(例如#include),产生一个新的源程序(这个过程称为编译预处理),之后再进行通常的编译。为了与一般的C语句区分,所有的预处理指令都以符号“#”开头,并且结尾不用分号。预处理指令可以出现在程序的任意位置,它的作用范围是从它出现的位置到文件尾,一般预处理指令都写在源程序开头,这样它的作用域就是整个源文件。C语言的预处理指令主要有:宏定义、文件包含、条件编译。

宏定义

1,不带参数的宏定义

<1>,格式

#define 宏名 字符串  例如:#define CHINA  13

<2>,作用

在编译预处理时,将源程序中所有“宏名”替换成右边的”字符串“,常用来定义常量。

#include <stdio.h>
 
 // 源程序中所有的宏名PI在编译预处理的时候都会被3.14所代替
 #define PI 3.14
 
 // 根据圆的半径计radius算周长
 float girth(float radius) {
     return 2 * PI *radius;
 }
 
 int main ()
 {
     float g = girth(2);
     
     printf("周长为:%f", g);
     return 0;
 }

<3>,使用注意

1,宏名一般用大写字母,以便与变量名区别开,但是小写也没有语法错误。(建议用大写)

2,对程序中用双引号括起来的字符串内的字符不进行宏的替换操作。

3,在编译预处理用字符串替换宏名时,不作语法检查,只是简单的字符串替换。只有在编译的时候才对已经展开宏名的源程序进行语法检查。

4,宏名的有效范围是从定义位置到文件结束,可以用#undef命令终止宏定义的作用域。

5,定义一个宏时可以引用已经定义的宏名。

2,带参数的宏定义

<1>,格式

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

<2>,作用

在编译预处理时,将源程序中所有宏名替换成字符串,并将字符串中的参数用宏名右边参数列表中的参数替换。

#include <stdio.h>
//定义带参数的宏 宏名是SUM
#define SUM(a,b) (a+b)

int main()
{
    int x = SUM(90,10);
    
    printf("和是:%d\n",x);
    
    return 0;
    //运行结果是:和是:100
}
<3>,使用注意

1,宏名和参数列表之间不能有空格,否则空格后面的所有字符串都被作为替换的字符串。

2,带参数的宏在展开时,只作简单的字符和参数的替换,不进行任何的计算操作。所以在定义宏时,一般用一个小括号括住字符串的参数,如果参数和字符串之间及参数与参数之间是运算关系,计算结果也要用括号括起来。

<4>,带参数的宏与函数的区别

1,宏定义不涉及存储空间的分配参数类型匹配、参数传递、返回值问题

2,函数调用在程序运行时执行,而宏替换只在编译预处理阶段进行。所以带参数的宏比函数具有更高的执行效率。

条件编译

条件编译就是满足一定的条件才会进行编译,否则不参与编译。

1,基本用法(类似if  else语句)

#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 N 10

int main()
{
    //判断条件1
#if N == 0
    //如果条件1成立则编译
    printf("N的值是0\n");
    //判断条件2
#elif N > 0
    //如果条件2成立则编译
    printf("N的值大于0\n");
#else
    //如果条件1,2都不成立则编译
    printf("N的值小于0\n");
#endif//一定要加上#endif,否则所有代码都不会被编译!!
    
    return 0;
    //结果是:N的值大于0
}
2,其他用法

<1>#if defined()和#if !defined()的用法

#include <stdio.h>
//定义宏  必须用宏来作判断元素!!
#define N 10

int main()
{
    //判断N这个宏是否有定义,
#if defined(N)
    //如果N有定义则编译
    printf("N的值为:%d\n",N);
#endif
    return 0;
    //运行结果是:N的值为:10
}
#include <stdio.h>
int main()
{
//判断M这个宏是否有定义,
#if !defined(M)
    //如果M没有有定义则编译
    printf("M这个宏没有定义\n");
#endif
    return 0;
    //运行结果是:M这个宏没有定义
}

<2>,#ifdef 和 #ifndef的使用

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

2,#ifndef的使用和#if !defined()的用法基本一致

文件包含

文件包含就是#include 它将一个文件的全部内容拷贝到另一个文件中。

1,格式:

<1>,第一种形式 #include <文件名> 代表系统文件,直接到C语言库函数头文件所在目录中寻找文件。

<2>,第二种形式 #include "文件名"  系统会先在源程序当前目录下寻找,若找不到,再到操作系统的path路径中查找,最后才到C语言库函数头文件所在目录中查找

2,使用注意

<1>.#include指令允许嵌套包含,比如a.h包含b.h,b.h包含c.h,但是不允许递归包含,比如 a.h 包含 b.h,b.h 包含 a.h

<2>.使用#include指令可能导致多次包含同一个头文件,降低编译效率。

static和extern关键字

1,static的作用

<1>,在定义函数时,在函数的最左边加上static可以把该函数声明为内部函数(又叫静态函数),这样该函数就只能在其定义所在的文件中使用。如果在不同的文件中有同名的内部函数,则互不干扰。

<2>,static也可以用来声明一个内部函数。

<3>,static对变量的作用

1.extern可以用来声明一个全局变量,但是不能用来定义变量

2.默认情况下,一个全局变量是可以供多个源文件共享的,也就说,多个源文件中同名的全局变量都代表着同一个变量

3.如果在定义全局变量的时候加上static关键字,此时static的作用在于限制该全局变量的作用域,只能在定义该全局变量的文件中才能使用,跟其他源文件中的同名变量互不干扰

2,extern的作用

<1>,在定义函数时,如果在函数的最左边加上关键字extern,则表示此函数是外部函数,可供其他文件调用。C语言规定,如果在定义函数时省略extern,则隐含为外部函数。

<2>, 在一个文件中要调用其他文件中的外部函数,则需要在当前文件中用extern声明外部函数,然后就可以使用,这里的extern也可以省略。

typedef

typedef就是给各种数据类型重新定义一个新的名字,也可以在新的名字的基础上再起另外的名字:typedef 旧名字 新名字;  ---> typedef  int  Integer;-->typedef  Integer In

1,一般格式:typedef 旧名字 新名字;  ---> typedef  int  Integer;

#include <stdio.h>
//修改数据类型int的名字为in
typedef int in;
int main()
{
    in x = 90;
    in y = 10;
    in z = x + y;
    printf("x=%d,y=%d,z=%d\n",x,y,z);
    return 0;
    //运行结果是:x=90,y=10,z=100
}
2,typedef与指针

typedef也可以给指针起别名

#include <stdio.h>
//给char类型的指针起了个新的名字String
typedef char *String;
int main()
{
    //相当于char *st = "我在学习C语言!";
    String st = "我在学习C语言!";
    printf("%s\n",st);
    //运行结果:我在学习C语言!
    return 0;
}

3,typedef与结构体

#include <stdio.h>
//定义一个结构体
struct GoodPerson {
    int age;
    char *name;
};
//给结构体GoodPerson起一个新的名字gp
typedef struct GoodPerson gp;

int main()
{
    //定义结构体变量p
    gp p;
    //给结构体成员赋值
    p.age = 25;
    p.name = "雷锋";
    printf("好人的名字是%s,年龄是%d岁\n",p.name,p.age);
    return 0;
    
    //运行结果是:好人的名字是雷锋,年龄是25岁
}

4,typedef与指向结构体的指针

#include <stdio.h>
//定义结构体
struct MyCar {
    int wheel;
    char *colour;
};
//给结构体起新名
typedef struct MyCar Car;
//给指向结构体的指针起新名
typedef Car *mc ;

int main()
{
    //定义结构体变量并初始化
    Car c  = {4,"红色"};
    //定义指针变量并将结构体变量的地址赋给指针变量p
    mc p = &c;
    printf("车子的颜色是%s,有%d个轮子\n",p->colour,p->wheel);

    return 0;
    //运行结果是:车子的颜色是红色,有4个轮子
}
5,typedef与枚举类型

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

#include <stdio.h>

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

int main()
{
    // 定义枚举变量,使用typedef可以省去枚举关键字enum
    Season s = spring;
    
    printf("%d\n",s);
    return 0;
}

6,typedef与指向函数的指针

#include <stdio.h>

// 定义一个sum函数,计算a跟b的和
int sum(int a, int b) {
    int c = a + b;
    printf("%d + %d = %d\n", a, b, c);
    return c;
}
//给指向函数的指针起别名Mysum原名是(int (*p)(int, int))
typedef int (*MySum)(int, int);

int main() {
    // 定义一个指向sum函数的指针变量p
    MySum p = sum;
    
    // 利用指针变量p调用sum函数
    (*p)(4, 5);
    
    return 0;

7,typedef与宏定义define

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

#include <stdio.h>

//给char * 起个别名 String1
typedef char *String1;
//定义宏 宏名是String2
#define String2 char *

int main()
{
    //相当于char *str1;char *str2;
    String1 str1, str2;
    //相当于char *str3;char str4;宏定义只是简单的替换字符串,不会智能匹配
    String2 str3, str4;
    str1 = "ABC";
    str2 = "abc";
    str3 = "XYZ";
    str4 = 'z';//str4不能存放字符串,只能存放字符
    printf("str1=%s,str2=%s,str3=%s,str4=%c\n",str1,str1,str3,str4);
    return 0;
    //运行结果是:str1=ABC,str2=ABC,str3=XYZ,str4=z
}

递归

递归的条件:

<1>,函数自己调用自己。

<2>,必须有明确的返回值。

注意:递归必须要有明确的递归结束条件,作为递归出口。

#include <stdio.h>
/*
 设计一个函数,用来计算a的n次方(运用递归)
 */
//声明函数
int pow1(int a ,int n);
int main()
{
    //定义int类型变量x保存计算的结果
    int x = pow1(2,2);
    printf("计算结果是:%d\n",x);

    return 0;
}
//定义函数
int pow1(int a ,int n)
{
    //如果if条件成立 返回1
    if (n <= 0) return 1;//递归结束的条件
    
    //if条件不成立,函数循环调用函数自身,当n小于等于0时,将最后一次调用的结果返回给上次调用自身时函数的状态。
    return pow1(a, n-1) * a;
    /*调用第一次:pow1(2,2)
     第二次:pow1(2,2-1)*2
     第三次:pow1(2,1-1)*2   当n等于0是返回1 pow1(2,0)*2 == 1*2=2,然后将2返回给第二次调用函数时是状态,
     pow1(2,2-1)*2 == 2*2 再返回给第一次调用函数,pow1(2,2) == 4。
     
     */
}


-------------------------------------- android培训java培训ios培训期待与您交流! ---------------------------------


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值