黑马程序员——C语言基础---预处理指令及其他

------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------


一、不带参数的宏定义


1.一般形式
#define 宏名 字符串


 比如#define ABC 10


右边的字符串也可以省略,比如#define ABC


 


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


接下来写个程序根据圆的半径计算周长


3.使用习惯与注意
1> 宏名一般用大写字母,以便与变量名区别开来,但用小写也没有语法错误


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


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


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




5> 定义一个宏时可以引用已经定义的宏名
#define R  3.0
#define PI 3.14
#define L  2*PI*R
#define S  PI*R*R
 


二、带参数的宏定义


1.一般形式
#define 宏名(参数列表) 字符串


 


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


3.使用注意
1> 宏名和参数列表之间不能有空格,否则空格后面的所有字符串都作为替换的字符串

2> 带参数的宏在展开时,只作简单的字符和参数的替换,不进行任何计算操作。所以在定义宏时,一般用一个


3> 计算结果最好也用括号括起来


5.与函数的区别
从整个使用过程可以发现,带参数的宏定义,在源程序中出现的形式与函数很像。但是两者是有本质区别的:


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


2> 函数调用在程序运行时执行,而宏替换只在编译预处理阶段进行


示例:

/*
 1.所有的预处理指令都是以#开头
 2.预处理指令分3种
 1> 宏定义
 2> 条件编译
 3> 文件包含
 3.预处理指令在代码翻译成0和1之前执行
 4.预处理的位置是随便写的
 5.预处理指令的作用域:从编写指令的那一行开始,一直到文件结尾,可以用#undef取消宏定义的作用
 6.宏名一般用大写或者以k开头,变量名一般用小写
 */
#include <stdio.h>


//#define kCount 4

int main()
{
    char *name = "COUNT";
    
    printf("%s\n", name);
    
    #define COUNT 4
    
    int ages[COUNT] = {1, 2, 67, 89};
    
    
    
    for ( int i = 0; i<COUNT; i++) {
        printf("%d\n", ages[i]);
    }
    
    // 从这行开始,COUNT这个宏就失效
#undef COUNT
    
    int a = COUNT;
    
    return 0;
}

void test()
{
    
}

示例:

/*
 1.带参数的宏定义效率比函数高
 
 */

/*
int sum(int a, int b)
{
    return a + b;
}*/
#include <stdio.h>

#define sum(v1, v2) ((v1)+(v2))

#define pingfang(a) ((a)*(a))

int main()
{
    // pingfang(5+5) (10*10)
    // pingfang(5+5)
    // pingfang(5+5) (35)
    // pingfang(5+5)/pingfang(2)
    int c = pingfang(5+5)/pingfang(2);
    
    printf("c is %d\n", c);
    /*
    int c = sum(2, 3) * sum(6, 4);
    
    printf("c is %d\n", c);*/
    /*
    int a = 10;
    
    int b = 20;
    
    
    int c = sum(a, b);
    
    printf("c is %d\n", c);
    //int c = sum(a, b);*/
    
    return 0;
}

运行结果:





条件编译的概念


在很多情况下,我们希望程序的其中一部分代码只有在满足一定条件时才进行编译,否则不参与编译(只有参与编译的代码最终才能被执行),这就是条件编译。


一、基本用法


1 #if 条件1
2  ...code1...
3 #elif 条件2
4  ...code2...
5 #else
6  ...code3...
7 #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后面的条件一般是判断宏定义而不是判断变量,因为条件编译是在编译之前做的判断,宏定义也是编译之前定义的,而变量是在运行时才产生的、才有使用的意义


 


二、举个例子


 1 #include <stdio.h>
 2 
 3 #define MAX 11
 4 
 5 int main ()
 6 {
 7 #if MAX == 0
 8     printf("MAX是0");
 9 #elif MAX > 0
10     printf("MAX大于0");
11 #else
12     printf("MAX小于0");
13 #endif
14     return 0;
15 }


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




1 /*stdio.h文件中的内容将会代替#include <stdio.h>的位置*/

3 int main ()
4 {
5     printf("MAX大于0");
6     return 0;
7 }
代码变得非常简洁,输出结果:


 
三、其他用法


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


1 #if defined(MAX)
2     ...code...
3 #endif
如果前面已经定义过MAX这个宏,就将code编译进去。它不会管MAX的值是多少,只要定义过MAX,条件就成立。


条件也可以取反:


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


 


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


1 #ifdef MAX
2     ...code...
3 #endif
如果前面已经定义过MAX这个宏,就将code编译进去。


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


1 #ifndef MAX
2     ...code...
3 #endif
如果前面没有定义过MAX这个宏,就将code编译进去。

示例:

#include <stdio.h>

// 只要写了#if,在最后面必须加上#endif

//#define A 5

int main()
{
#ifndef A
//#ifdef A
//#if !defined(A)
    printf("哈哈\n");
#endif
    
    //int a = 10;
    /*
    if (a == 10)
    {
        printf("a是10\n");
    }
    else if (a == 5)
    {
        printf("a是5\n");
    }
    else
    {
        printf("a其他值\n");
    }*/
    /*
    
#if (A == 10)
    printf("a是10\n");
#elif (A == 5)
    printf("a是5\n");
#else
    printf("a其他值\n");
#endif
     
     */
    
    return 0;
}

运行结果:





文件包含


一、基本概念

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

二、一般形式

1.第1种形式#include <文件名>
直接到C语言库函数头文件所在的目录中寻找文件


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


示例:


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

#ifndef LISI_H
#define LISI_H

int sum(int a, int b);


#endif

#ifndef WANGWU_H
#define WANGWU_H
int minus(int a, int b);
#endif

/*
 1.<>表示系统自带的文件,""表示自定义的文件
 2.不允许循环包含,比如a.h包含b.h,b.h又包含a.h
 */

#include "lisi.h"
#include "wangwu.h"

#include <stdio.h>

int main()
{
    int c = sum(10, 19);
    
    printf("c is %d\n", c);
    
    return 0;
}

运行结果:



typeof

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

 

 2.使用场合:

 1> 基本数据类型

 2> 指针

 3> 结构体

 4> 枚举

 5> 指向函数的指针


示例:

#include <stdio.h>

typedef int MyInt;
typedef MyInt MyInt2;

// 给指针类型char *起一个新的类型名称String
typedef char * String;

/*
struct Student
{
    int age;
};
typedef struct Student MyStu;
*/

/*
typedef  struct Student
{
    int age;
} MyStu;
*/


typedef struct
{
    int age;
} MyStu;

/*
enum Sex {Man, Woman};
typedef enum Sex MySex;
*/

typedef enum {
    Man,
    Woman
} MySex;


typedef int (*MyPoint)(int, int);

int minus(int a, int b)
{
    return a - b;
}

int sum(int a, int b)
{
    return a + b;
}
/*
struct Person
{
    int age;
};

typedef struct Person * PersonPoint;
*/

typedef struct Person
{
    int age;
} * PersonPoint;

int main()
{
    // 定义结构体变量
    struct Person p = {20};
    
    PersonPoint p2 = &p;
    
    //struct Person *p2 = &p;
    
    //MyPoint p = sum;
    //MyPoint p2 = minus;
    //int (*p)(int, int) = sum;
    
    //int (*p2)(int, int) = minus;
    
    //p(10, 11);
    
    
    //MySex s = Man;
    //enum Sex s = Man;
    //enum Sex s2 = Woman;
    
   // struct Student stu3;
    //MyStu stu = {20};
    //MyStu stu2= {21};
    
    return 0;
}

void test2()
{
    String name = "jack";
    
    printf("%s\n", name);
}

void test()
{
    int a;
    MyInt i = 10;
    MyInt2 c = 20;
    
    MyInt b1, b2;
    
    printf("c is %d\n", c);
}

#include <stdio.h>

//#define Integer int

//typedef int Integer;

//typedef unsigned long int MyInt;

#define String2 char *

typedef char * String;

int main()
{
    /*
    int a,b;
    int a;
    int b;
    */
    
    //s1、s2是char *指针
    String s1, s2;
    /*
     String s1;
     String s2;
     */
    s1 = "jack";
    s2 = "rose";
    
    // s3才是char *指针,s4只是char
    String2 s3, s4;
    /*
    char *s3, s4;
    char *s3;
    char s4;
    */
    //String2 s3 = "jake";
    
    /*
    String s1;
    String s2;
    */
    
    
    
    
    //Integer i = 10;
    
    return 0;
}


static和extain关键字


1.extain函数

先来理解2个概念:


外部函数:如果在当前文件中定义的函数允许其他文件访问、调用,就称为外部函数。C语言规定,不允许有同名的外部函数。
内部函数:如果在当前文件中定义的函数不允许其他文件访问、调用,只能在内部使用,就称为内部函数。C语言规定不同的源文件可以有同名的内部函数,并且互不干扰。
接下来就演示在一个源文件中调用另外一个源文件定义的函数,比如在main.c中调用one.c中定义的one函数。


注意点:


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

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

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

 

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

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

 

 static对函数的作用:

 1> 定义一个内部函数

 2> 声明一个内部函数

 

 extern对函数的作用:

 1> 完整地定义一个外部函数

 2> 完整地声明一个外部函数

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



示例:

void test();

//void test2();

int main()
{
    test();
    
    //test2();
    return 0;
}

//void test()
//{
//    
//}
static void test2()
{
    
}

#include <stdio.h>

// 声明一个内部函数
static void test2();

// 完整地定义一个外部函数
/*
extern void test()
{
    printf("调用了test函数\n");
}*/
// 默认情况下,所有的函数都是外部函数,所以可以省略extern
void test()
{
    printf("调用了test函数\n");
    
    test2();
}


// 定义一个内部函数
static void test2()
{
    printf("调用了test2函数\n");
}

运行结果:



 全局变量分2种:

 外部变量:定义的变量能被本文件和其他文件访问

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

 1> 不同文件中的同名外部变量,都代表着同一个变量

 

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

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

 

 static对变量的作用:

 定义一个内部变量

 

 extern对变量的作用:

 声明一个外部变量

 

 static对函数的作用:

 定义和声明一个内部函数

 

 extern对函数的作用:

 定义和声明一个外部函数(可以省略)


示例:

#include <stdio.h>

/*
 static修饰局部变量的使用场合:
 1.如果某个函数的调用频率特别高
 2.这个函数内部的某个变量值是固定不变的
 */

void test()
{
    static double pi = 3.14;
    
    double zc = 2 * pi * 10;
    
    int a = 0;
    a++;
    printf("a的值是%d\n", a); // 1
    
    /*
     static修饰局部变量:
     1> 延长局部变量的生命周期:程序结束的时候,局部变量才会被销毁
     2> 并没有改变局部变量的作用域
     3> 所有的test函数都共享着一个变量b
     */
    static int b = 0;
    b++;
    printf("b的值是%d\n", b); // 3
}

int main()
{
    for (int i = 0; i<100; i++) {
        test();
    }
    
    
    test();
    
    test();
    
    test();
    
    
    return 0;
}

#include <stdio.h>

void test();

// 定义一个外部变量

//int a; 这么多个a都是代表同一个a
//int a;
//int a;
//int a;
//int a;


// 定义一个内部变量
static int b;

// 声明一个外部变量
//extern int a;

int main()
{
    //b = 10;
    
    //test();
    
    extern int a;
    a = 10;
    
    /*
    a = 10;
    
    test();
    
    printf("a的值是%d\n", a);*/
    
    return 0;
}


int a;

#include <stdio.h>

int a;

static int b;

void test()
{
    printf("b的值是%d\n", b);
    
    /*
    printf("a的值是%d\n", a);
    
    a = 20;*/
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值