C入门(下)

项目基础运用

一、文件链接

对于c提供的文件,可以直接使用eg:#include<stdio.h>的方式进行链接,对于自己定义的h文件,若c与h文件位于同一文件夹,可直接使用eg:#include "yourselffile.h"进行链接,如果不在同一文件夹,则以相对c文件的位置引用eg:#include "../h_file/test.h

二、重定向

#define debug
#ifdef debug
#define ok 1
#else
#define ok 2
#endif

明显的ok=1,于是可以

#define debug

#ifdef debug
#define Debug(info) printf(info)
#else
#define Debug(info) //相当于将Debug(info)视为空null
#endif

你会发现将printf()函数定义给了Debug(),当注释掉第一行时,Debug()定义为空,则可以在工程中广泛使用Debug()函数,使其在调试中可以输出,在调试结束后$$,仅需要注释掉上面的一行即可。但过程中会发现其中Debug只能为字符串,不像原生printf一样可以使用%d,%s,%c等的扩展,此时就要使用C 可变参数,可将原例修改为

#ifdef debug
#define Debug(...) printf(__VA_ARGS__)
#else
#define Debug(...) 
#endif

这样Debug的功能就和printf一样了。

进阶(重定向printf)

重定向过程中,由于重定向语句为Debug(format,...) printf(format,##__VA_ARGS__),该语句实际对应关系为format->format,…->##VA_ARGS;由于printf在单字符串时能够直接输出printf(str),于是对于重定向,会是使用##来取消掉参数前的逗号,也就构成##__VA_ARGS__,于是Debug的单参数输出效果就与printf类似,多参数也不受影响。问题来了,要如何个性化重定向输出,以下为两种解决方式。

1. 字符串常量化运算符

由于Debug中format参数对应printf中的format,在定义过程中,不能改变format的前后关系,始终要让format位于printf的第一个参数中,又因要个性化输出,所以只能将两个连接在一起,c中没有"str"+"str"这样可以直接连接的语法,要使参数和自定义str连接在一起,最高效的方式就是使用#,将format直接常量化
eg
#define Debug(format,...) printf("FILE: "__FILE__", LINE:%d"#format"\r\n",__LINE__,##__VA_ARGS__)
可以看到,常量化后的参数可以直接放在字符串后面,输出也正常

Debug("%s","chen");

FILE: c_file/test.c, LINE:6"chen"

format输出多了一个引号包围,但不影响效果.

2. 函数集

对于重定向的使用,往往涉及到宏定义的操作,事实上,宏定义除了可以为特定函数指定重定向的函数之外,还可以重定向为一个函数集,只要将该函数集用括号包裹起来即可,用法和脚本类似。
eg

#define debug
#ifdef debug
#define Debug(...) (printf("FILE: "__FILE__", LINE:%d",__LINE__),printf(__VA_ARGS__),printf("\r\n"))
#else
#define Debug(...)
#endif

使用效果

Debug("%s","chen");

FILE: c_file/test.c, LINE:6chen

其中的__FILE__和__LINE__是便于调试的原生指令,前者显示运行时的文件,后者显示行数,便于纠错。该代码中,重定向的目标为三个printf的输出集,这样就能最大化地个人定制输出格式。
其他原生指令包括

__LINE__:在源代码中插入当前源代码行号;

__FILE__:在源文件中插入当前源文件名;

__DATE__:在源文件中插入当前的编译日期

__TIME__:在源文件中插入当前编译时间;

__STDC__:当要求程序严格遵循ANSI C标准时该标识被赋值为1;

参考
printf函数 重定义printf函数。
怎样写参数个数可变的宏

三、打印输出

c中可以使用的输出函数为

printf("%d",i);//常用输出

char test[10];
sprintf(test,"%d",3);//相比printf,需要指定一个输出的缓存(test),相当于为字符串赋值

FILE * f;
f=fopen("test","w+");
if(f!=NULL){
            fprintf(f,"%s","this is come from fprintf");//输出字符到指定文件
}

需要注意的是,fprintf输出的输出流可以是命令行,就和普通的printf输出效果一样,此时可以提出

fprintf(stdout,"hello file1");           
fprintf(stderr,"hello file2\r\n");     

输出
hello file2
hello file1 

stdout:
行缓冲输出,当有新输出才会输出,专门用于输出到指定文件
stderr:
直接输出,常用于输出异常等错误
此外,对于上面的代码,如果使用命令行运行

./outfile >test 

命令行结果:hello file2
同时test文件结果:hello file1

参考
stderr和stdout详细解说

四、include和extern

在C/C++环境中

在C++文件中,当多个文件连接编译时,对于引用.h文件和使用extern使用全局变量的区别如下

1.引入.h文件

首先对于变量的声明,最好是在c/c++文件中定义,然后在.h文件中声明,这样就避免了混合链接时导致重复链接的问题,c++文件中对于重复的定义是直接报错输出的。举例

a.cpp

#include <stdio.h>
int num=2;
void test2()
{
    printf("here is test2\r\n");
}

a.h
#ifndef __test2_h
#define __test2_h
void test2();
extern int num;
#endif 

可见对num的定义是位于.cpp中,而在对应.h文件中则使用extern 方式声明了该变量。以下为相应引用

b.cpp
#include<stdio.h>
#include"a.h"
void test3(){
    test2();
    printf("here is test3,and num is %d\r\n",num);
}

b.h
void test3();

c.cpp
#include <stdio.h>
#include "b.h"
#include "a.h"
int main()
{
   printf("this is test1,and num is %d\r\n",num);
   test2();
   test3();
}

由于b.cpp和c.cpp都对a.h进行引用,在连接编译时,由于分别与a.h编译后结果才编译在一起,当在a.h中使用extern int num;声明时,由于声明不受限制,所以不会报错,但如果在a.h中使用的是int num=2;直接定义该值,并进行连接编译时,则会导致multiple definition重复定义的问题,同理,如果将上面a.cpp文件中的test2()在.h文件中定义,也会导致multiple definition of `test2()';的问题,所以对于cpp文件,不要在.h头文件中定义,.h文件一律使用声明是最稳当的,定义在相应的.cpp文件中实现。

2.使用extern

相比于引入.h文件,extern调用全局变量的方式就自由得多,在a.cpp中定义方法test2()和变量num,甚至都不用在a.h中声明,在b.cpp和c.cpp文件中只需要使用

extern int num;
extern int test2();

就可以正常使用a.cpp中实现和定义的方法和变量。但对于该方法和变量的定义,在整个工程中也只能出现一次,如果在不同的cpp文件中定义了相同的变量或方法,则会multiple definition of `test2()',此外,对于全局变量,可以通过不同的文件修改它的值,例如在b.cpp中修改a.cpp中定义的全局变量(设为num)的值,则当运行到b.cpp对应代码时,num的值就会全局的变化。由此,全局变量实际可以用作系统全局通知来使用,这在使用.h文件引入的模式中也是同样有效的。

通过上面,会发现,如果只是想要使用其他文件中的某个元素(函数或者变量),使用extern来获取会更方便,反之,如果大量使用其他文件中的元素,使用引入.h文件的方式会更好

参考
头文件为什么要加#ifndef #define #endif
extern 与头文件(*.h)的区别和联系

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
《C语言入门到精通》是一本针对C语言学习的教程。本书以系统完整的方式介绍了C语言的基本概念、语法和应用,并通过大量的示例和练习帮助读者快速掌握C语言编程技巧。 书籍的第一部分主要介绍了C语言的基础知识。首先,它介绍了C语言的发展历程以及与其他编程语言的区别。然后,书中详细解释了C语言的数据类型、运算符、流程控制语句和函数等基本概念,帮助读者建立起对C语言编程的基础理论知识。 第二部分是关于C语言的高级应用和技巧。本书详细介绍了C语言的数组、指针、结构体和文件操作等高级特性。读者可以通过学习这些知识,掌握更加灵活、高效的编程方法,提高自己的程序设计能力。 此外,本书还包含了一些用的C语言库函数和相关工具的介绍,如字符串处理函数、内存管理函数和调试工具等。这些内容帮助读者更好地理解和使用C语言的相关资源,提高自己的编程效率。 《C语言入门到精通》不仅提供了理论知识的讲解,还提供了大量的实战编程示例和练习题。这些示例和练习题既可以帮助读者巩固所学知识,又可以提供编程实践的机会,培养读者的编程思维和解决问题的能力。 总的来说,这本书适合初学者作为C语言学习的入门教材,也适合有一定编程基础的读者作为C语言进阶学习的参考书。无论是新手还是专业人士,通过学习这本书,都可以逐步掌握C语言的基本概念和高级应用,从入门到精通。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值