vlambda博客
学习文章列表

C语言中一个分号的奇迹(预处理、指针、结构体、内存分配)——一段暗藏玄机的代码



C语言中一个分号的奇迹(预处理、指针、结构体、内存分配)——一段暗藏玄机的代码


✎ 编 者 悟 语

    

      “看热闹”只能是置身事外



文 章 导 读


    今天给大家拼凑一个超经典的程序实例,从这个实例中大家能体会到C语言的玄妙,小伙伴们一定要自己撸一撸代码好好替换哈,希望对大家有所帮助哈,C语言中一个分号的奇迹(预处理、指针、结构体、内存分配)——一段暗藏玄机的代码


1
你见过下面这样的C代码吗


    看看下面的代码?你见过吗?



#include <stdio.h>
#include <stdlib.h>


int main(int argc, char *argv[]) 
{
 
     ;
 
     ;
 
     ;
 
     ;
 
     printf("\n我运行到这啦哈!!!\n");
 
     return 0;
}


    说实在的上面的代码,你确实是见不到的,并不是它有什么问题,而是,你用得时候只有将形式影藏起来才能见到它的威力。


    上面代码的运行结果如下:


C语言中一个分号的奇迹(预处理、指针、结构体、内存分配)——一段暗藏玄机的代码


    从上面的结果中我们可以得出一个结论和两个推测。

结论:C语言中支持由单个分号构成的语句。

推测1:单独的分号语句,在编译的时候可能会被编译器优化掉,而不占用空间和运行速度。

推测2:单独的分号语句,没有被编译器优化掉,它可能占空间,也可能占运行速度。

    上面的推测没有经过验证,所以我在这里并不能下结论,如果感兴趣,小伙伴们可以去测试一下。


    我写上面那段代码的目的,不是为了唬人,而是想说,这个单独的分号语句,也是很有用处的,它结合预处理(这里主要用到定义+分支)、指针、结构体、内存分配会发挥意想不到的效果。


重要提醒:下面的代码不长,但能让你get到n多个具有深度的知识点。


2
代码功能说明

    

    大家在看我下面的实例代码的时候,要注意下面4个点,只有将这4个点看到心里有数,才算领会到了实例代码的精髓。


    1)宏开关的位置


    可以有一个开关,统一动作,也可以将一个开关放到多个位置。


    2)利用宏开关,批量开启关闭功能块


    可以将不同功能的模块或这说函数等放到一起,用宏开关开启关闭相应的功能。


    3)给结构体指针变量和结构体内部的指针变量分配空间


    凡是指针都要指向特定的位置,不然就是空指针,也容易造成野指针,造成程序崩溃,很多时候不分配空间直接使用都会报错的。


    4)利用宏替换,将函数调用失效


    用宏替换将函数名及括号参数替换为空格,函数就是失效了,函数调用不用时都不用注释。


    5)在主函数中只是调用,可不可以去掉宏分支


    当只是将函数失效,没有变量操作时,宏分支是可以去掉的,这个点大家自己去尝试吧!

3
实例代码


    代码分为三个函数,主要目的是模块化,名字起得比较随意大家多担待,看内容吧。

    

    1)struct_test.h



#ifndef STRUCT_TEST_H
#define STRUCT_TEST_H
#endif 

#define OPERATIN_ON

#ifdef OPERATIN_ON

// 定义两个结构 
typedef struct _with_para
{

     int p; 
     int w_val1;
     int w_val2;
    
}with_para_t;


typedef struct _without_para
{

     with_para_t *wp; 
     int stu_val1;
     int stu_val2;
    
}without_para_t;

// 定义结构体变量及指针 
without_para_t test_para;
with_para_t with_test;
without_para_t *pp;

// 函数声明 
int test_fun1(void);
int test_fun2(int val1,int val2);


#else  //将两个函数替换为空格,函数调用处只剩分号,即可取消函数调用             

#define test_fun1(void)          
#define test_fun2(val1,val2)

#endif


    2)struct_test.c


#include<stdio.h>
#include<stdlib.h>

#include"struct_test.h"

#ifdef OPERATIN_ON  //此宏没定义,下面的函数定义就失效了,不用注释定义和调用就可以用宏批量取消掉某种功能的函数 

// 此函数适合用于初始化,初始值用结构指定,并返回给结构中的指针变量 
int test_fun1(void)
{
     with_test.p = 3;
     with_test.w_val1 = 4;
     with_test.w_val2 = 5;
}


// 此函数适合带参初始化 
int test_fun2(int val1,int val2)
{
     pp->stu_val1 = val1;
     pp->stu_val2 = val2;
     pp->wp = &with_test;
}

#endif


    3)main.c


#include <stdio.h>
#include <stdlib.h>

#include"struct_test.h"


#ifdef OPERATIN_ON

// 声明外部结构变量 
extern without_para_t test_para;
extern with_para_t with_test;
extern without_para_t *pp;

#endif

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

#ifdef OPERATIN_ON  // 宏定义起作用的分支 
 
     printf("宏开启啦!\n"); 
     // 指向结构的指针需要分配空间,结构中的指针也要分配空间 
     test_para.wp = (with_para_t *)malloc(sizeof(with_para_t));
     pp = (without_para_t *)malloc(sizeof(without_para_t));
    
     // 函数起作用 
     test_fun1();
     test_fun2(1,2);

     // 打印信息
     printf("\n打印函数信息如下:\n\n"); 
     printf("pp->stu_val1 = %d\n",pp->stu_val1);
     printf("pp->stu_val2 = %d\n",pp->stu_val2);
     printf("pp->wp->p = %d\n",pp->wp->p);
    
#else              //将宏定义OPERATIN_ON注释掉,宏失效运行此分支, test_fun1();被替换为;,即不再调用这两个函数。 
    
     printf("宏关闭啦!\n"); 
     // 函数调用被替换为分号,函数失效 
     printf("\n函数调用被替换为分号,失去调用意义啦!\n");
     test_fun1();       
     test_fun2(1,2);
    
#endif

     printf("\n我运行到这啦哈!!!\n")
;
 
     return 0;
}

    当宏OPERATIN_ON打开(定义)时,运行结果如下:


C语言中一个分号的奇迹(预处理、指针、结构体、内存分配)——一段暗藏玄机的代码


    当宏OPERATIN_ON关闭(注释)时,运行结果如下:


C语言中一个分号的奇迹(预处理、指针、结构体、内存分配)——一段暗藏玄机的代码

提示:上面的代码,大家可以建立一个工程去试试,只是看是不太好体到它的应用价值的。

4
宏替换的延伸扩展


    宏替换不仅可以换函数,还可以换变量是否复制,相关内容请看我的另一篇文章——


总结

    今天这篇文章是时分实用的,这里只是抛砖引玉,并没有结合实际的应用,但小伙伴们还是会有不小的收获的,要自己把代码拿下来撸一撸哈,看热闹只能是置身事外哈,小伙伴们敬请期待哈,C语言中一个分号的奇迹(预处理、指针、结构体、内存分配)——一段暗藏玄机的代码




相关文章:














专辑推荐:












Game Over!