C语言入门(廿二)之预处理指令、宏、条件编译、文件包含、typedef、const

版权声明:如果觉得文章对你有用,转载不需要联系作者,但请注明出处 https://blog.csdn.net/jinxin70/article/details/83421382

预处理指令

什么是预处理指令:

在我们的文件翻译成0和1之前做的操作我们称之为预处理指令。一般情况预处理指令都是以#号开头的。

宏定义的格式

不带参数的宏定义:

#define 宏名 值

宏定义的作用:   
    会在程序翻译成0和1之前, 将所有宏名替换为 宏的值
 

宏定义在什么时候替换


 源代码 --> 预处理 -->汇编 -->二进制 -->可执行程序

注意:宏定义后面不要写分号

宏定义规范:

一般情况宏名都大写, 多个单词之间用_隔开, 并且每个单词全部大写。
有的公司又要求宏名以k开头, 多个单词之间用驼峰命名。

宏定义的作用域:

从定义的那一行开始, 一直到文件末尾。
虽然默认情况下宏定义的作用域是从定义的那一行开始, 一直到文件末尾. 但是我们也可以通过对应的关键字提前结束宏定义的作用域。(#undef COUNT)

带参数的宏定义:

#define SUM(v1, v2) v1+v2

#define 代表要定义一个宏
 SUM 宏的名称
 (v1, v2) 参数, 注意点, 不需要写数据类型
 v1+v2 用于替换的内容

宏定义并不会做任何运算, 无论是有参数还是没有参数都仅仅是在翻译成0和1之前做一个简单的"替换"

带参数的宏定义注意点


 1.一般情况下建议写带参数的宏的时候, 给每个参数加上一个()
 2.一般情况下建议写带参数的宏的时候, 给结果也加上一个()

如:

#define PF(v1) ((v1)*(v1))

什么时候用带参数的宏定义什么时候用函数?

如果函数内部的功能比较简单, 仅仅是做一些简单的运算那么可以使用宏定义, 使用宏定义效率更好, 运算速度更快。
如果函数内部的功能比较复杂, 不仅仅是一些简单的运算, 那么建议使用函数。

条件编译

#define DEBUG 1 // 0是调试阶段 1是发布阶段

#if DEBUG == 0
// 调试阶段
#define NJLog(format, ...) printf(format,## __VA_ARGS__)
#else
// 发布阶段
#define NJLog(format, ...)
#endif

条件编译和选择结构if的共同点

都可以对给定的条件进行判断, 添加满足或者不满足都可以执行特定的代码

条件编译和选择结构if的区别

1.生命周期不同
    if 运行时
    #if 编译之前
 2.#if需要一个明确的结束符号 #endif
    为什么需要一个明确的结束符号?
    如果省略掉#endif, 那么系统就不知道条件编译的范围, 那么会将满足条件之后的第二个条件之后的所有内容都清除
 3.if会将所有的代码都编译到二进制中
  #if只会将满足条件的部分一直到下一个条件的部分 编译到二进制中

条件编译的优点


 1.缩小应用程序的大小

应用场景:


 用于调试和发布阶段进行测试
 调试阶段: 程序写代码的阶段
 发布阶段: 上传到AppStore的阶段

预处理指令什么时候执行? 编译之前 。


变量什么时候定义? 执行了才会定义。

注意点: 条件编译不能用来判断变量, 因为不在同一个生命周期。

一般情况下, 条件编译是和宏定义结合在一起使用的。

#include <stdio.h>

#define SCORE 100
//#define COUNT 50

int main(int argc, const char * argv[]) {

    
#ifdef SCORE // 判断是否定义了后面的宏
    printf("score\n");
#elif COUNT
    printf("count\n");
#else
    printf("OTHER\n");
#endif
     
#ifndef SCORE // 是不是没有定义名称叫做SCORE的宏
    printf("no score\n");
#else
    printf("score\n");
#endif
    
    
    /*
#if defined(SCORE) // 判断是否定义了SCORE这个宏
    printf("score\n");
#else
    printf("no score\n");
#endif
    
#if !defined(SCORE) // 判断是否没有定义SCORE这个宏
    printf("no score\n");
#else
    printf("score\n");
#endif
     */
    

    return 0;
}

文件包含

#include <>
 <>会先去编译器环境下查找, 找不到再去系统的环境下查找

 #include ""
 ""会先在当前文件查找, 找不到再去编译器环境下查找, 找不到再去系统的环境下查找
 
 作用:
 将""或者<>中的内容完全拷贝过来
 
 注意:
 如何正确的编写.h文件
 如何防止循环拷贝  A拷贝B, B拷贝A
 间接拷贝问题  A拷贝B, B拷贝C, C拷贝D

typedef

什么是typedef, 它有什么作用

typedef可以给一个已知的数据类型起别名 (外号)
 
 利用typedef给数据类型起别名的格式:
 
 typedef 原有的数据类型 别名(外号);
 

注意:
 1. typedef不仅能给系统原有的数据类型起别名, 也可以给一个自定义的数据类型起别名。
 2. 利用typedef给数据类型起别名, 并不会生成一个新的数据类型, 仅仅是给原有的类型起了一个别名而已。

typedef可以给指向函数的指针起别名

// functionPotinter == int(*functionPotinter)(int , int)
typedef int(*functionPotinter)(int , int);
int main(int argc, const char * argv[]) {
   
    // 9如何定义变量 :   数据类型 变量名称;
//    int (*sumP)(int , int ) = sum;
    functionPotinter sumP = sum;
    printf("sum = %i\n", sumP(10 , 20));
    
//    int (*minusP)(int, int) = minus;
    functionPotinter minusP = minus;
    printf("minus = %i\n", minusP(20, 10));
    return 0;
}

可以给指针起别名

// String == char *
typedef char * String;
void test4()
{
    //    char *name = "lnj";
    // 注意: 如果给指针起别名之后, 那么以后利用别名定义变量就不用再加*了
    String name = "lnj";
    printf("name = %s\n", name);
    
    
}

可以给枚举类型起别名

// 1.先定义枚举类型, 再给枚举类型起别名
/*
 enum Gender
 {
 kGenderMale,
 kGenderFemale
 };
 typedef enum Gender SEX;
 */

// 2.定义枚举类型的同时给枚举类型起别名
/*
 typedef enum Gender
 {
 kGenderMale,
 kGenderFemale
 } SEX;
 */

// 3.定义枚举类型的同时给枚举类型起别名, 并且省略枚举原有类型名称
typedef enum
{
    kGenderMale,
    kGenderFemale
} SEX;

给结构体类型起别名

// 1.先定义结构体类型, 再给类型起别名
/*
 struct Person
 {
 int age;
 double height;
 char *name;
 };
 // SPerson == struct Person
 typedef struct Person SPerson;
 */

// 2.定义结构体类型的同时, 给结构体类型起别名
/*
 typedef struct Person
 {
 int age;
 double height;
 char *name;
 } SPerson;
 */

// 3.定义结构体类型的同时, 给结构体类型起别名, 并且省略掉原有类型的名称

 typedef struct
 {
 int age;
 double height;
 char *name;
 } SPerson;

给基本数据类型起别名

// Integer == int
typedef int Integer;
typedef Integer myInt;
// int float doulbe char
void test1()
{
    int num = 10;
    printf("num = %i\n", num);
    
    Integer age = 30;
    printf("age = %i\n", age);
    
    myInt score = 99;
    printf("score = %i\n", score);
}

typedef和宏定义的区别

一般情况下如果要给数据类型起一个名称建议用typedef, 不要用define

typedef int myInt;
#define Integer int

typedef char * String;
#define MY_STRING char *

const关键字

const修饰的变量内容不可改变,变量变为常量。

    int const num = 10;
    printf("num = %i\n", num);
    //num = 55;//编译报错,不能改变变量的值
    printf("num = %i\n", num);

const修饰的指针变量,指针变量指向的地址的内容可以改变,但是指针保存的地址不能变。

    char const *  name = "lnj";
    printf("name = %s\n", name);
    name = "lk";
    printf("name = %s\n", name);

const可以写在char这个数据类型的左右都可,习惯写在最前面。

终于学完了基础部分,以后多加练习,有些重要的知识点要回头反复敲代码,不然记不住,同时进度继续,下一步学oc。

猜你喜欢

转载自blog.csdn.net/jinxin70/article/details/83421382
今日推荐