关于宏定义的常见问题

1.怎样避免重复定义头文件?

#ifdef
#include <stdio.h>
....
#endif

2.请用宏定义实现swap函数

法一:

#define swap1(a,b)    (a=(a)+(b),b=(a)-(b),a=(a)-(b))

法二:

#define swap2(x, y) \
	(y) = (x) + (y); \
	(x) = (y) - (x); \
	(y) = (y) - (x);

法三:

#define swap3(x, y)\
	x = x^y;\
	y = x^y;\
	x = x^y;

3.请用宏定义实现min()、max()

#define max(a,b) ((a) > (b) ? (a) : (b))
#define min(a,b) ((a) < (b) ? (a) : (b))

使用宏定义写max()、min()时需要注意:(借鉴了以下博客链接内容)

有时候强类型语言对于实现相对简单的函数似乎是个障碍,例如虽下面
的函数 min()的算法很简单,但是强类型语言要求我们为所有希望比较的
类型都实现一个实例

int min( int a, int b ) {
    return a < b ? a : b;
}
double min( double a, double b ) {
    return a < b ? a : b;
}

有一种方法可替代这种为每个 min()实例都显式定义一个函数的方法,
这种方法很有吸引力,但是也很危险,那就是用预处理器的宏扩展设
施例如

 #define min(a,b) ((a) < (b) ? (a) : (b))

虽然该定义对于简单的 min()调用都能正常工作,如

min(10,20);
min(10.0,20.0);

但是在复杂调用下它的行为是不可预期的,这是因为它的机制并不像函数
调用那样工作,只是简单地提供参数的替换,结果是它的两个参数值都被
计算两次,一次是在a和b的测试中,另一次是在宏的返回值被计算期间,
例如

#include <iostream>
#define min(a,b) ((a) < (b) ? (a) : (b))
const int size = 10;
int ia[size];
int main() {
    int elem_cnt = 0;
    int *p = &ia[0];
    // 计数数组元素的个数
    while ( min(p++,&ia[size]) != &ia[size] )
        ++elem_cnt;
    cout << "elem_cnt : " << elem_cnt
    << "\texpecting: " << size << endl;
    return 0;
}

这个程序给出了计算整型数组ia的元素个数的一种明显绕弯的的方法。
min()的宏扩展在这种情况下会失败,因为应用在指针实参p上的后置
递增操作随每次扩展而被应用了两次,执行该程序的结果是下面不正
确的计算结果
elem_cnt:5 expecting:10
————————————————
版权声明:本文为CSDN博主「Sleepp」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Sleepp/article/details/81876502

其中

它的两个参数值都被计算两次,一次是在a和b的测试中,另一次是在宏的返回值被计算期间。

解释了原因。参数值会计算两次,如果递归函数在min与max的define宏定义函数下调用了自己是非常可怕的,它会增加指数级别的时间复杂度。define宏定义因为不会真正调用函数的特性在一定情况下确实能增加速度,然而如果min与max的define宏定义函数的“实参”(其实它并不能叫做实参)中出现了一个复杂的计算的话,它会进行两次计算,这大大拖慢了程序的速度。所以我建议如果在使用define宏定义函数时,如果传值中出现了一个会进行时间较长的计算的函数的话,应该这样使用:

int t=calc();  //假如calc()是一个需要经过大量计算的函数
ans=min(t,ans);

这样会大大加快速度 或者除非卡常时否则干脆别用了 。
 

4.怎样在C++程序中调用C程序?反之呢?

C++和C是两种完全不同的编译链接处理方式,如果直接在C++里面调用C函数,会找不到函数体,报链接错误。要解决这个问题,就要在 C++文件里面显示声明一下哪些函数是C写的,要用C的方式来处理。
1.引用头文件前需要加上 extern “C”,如果引用多个,那么就如下所示
extern “C”
{
#include “ s.h”
#include “t.h”
#include “g.h”
#include “j.h”
};
然后在调用这些函数之前,需要将函数也全部声明一遍。
2.C++调用C函数的方法,将用到的函数全部重新声明一遍
extern “C”
{
extern void A_app(int);
extern void B_app(int);
extern void C_app(int);
extern void D_app(int);

}

C++程序中调用被c编译器编译后的函数,为什么要加extern “C”?

C++语言支持函数重载,C语言不支持函数重载。函数被C++编译后在库中的名字与C语言的不同。假设某个C 函数的声明如下:
void foo(int x, int y);
该函数被C 编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字用来支持函数重载和类型安全连接。由于编译后的名字不同,C++程序不能直接调用C 函数。C++提供了一个C 连接交换指定符号extern“C”来解决这个问题。例如:
extern “C”
{
void foo(int x, int y);
// 其它函数
}
或者写成
extern “C”
{
#include “myheader.h”
// 其它C 头文件
}
这就告诉C++编译译器,函数 foo 是个C 连接,应该到库中找名字_foo 而不是找_foo_int_int。C++编译器开发商已经对C 标准库的头文件作了extern“C”处理,所以可用#include 直接引用这些头文件。

5.#include “x.h”与#include <x.h>的区别?

C中可以通过#include <header.h>和#include "header.h"来加载文件,两者的区别主要是在include加载的搜索路径上存在不同。

区别是:
采用#include <header.h>,链接器会按照系统指定目录(如Windows下给INCLUDE环境变量指定的路径顺序)去查找头文件<header.h>。

采用#include "header.h"方式,链接器会先到当前目录查找头文件header.h,如果没找到,才会在到系统指定目录查找。

所以在Windows下如果想使用#include <own_iostream>需要将own_iostream文件拷贝到环境变量INCLUDE的路径下,如我的电脑中采用的INCLUDE=“xxx\VS2010\VC\include”,则需要将相应文件拷贝到该路径下。如果是自定义的库文件,一般建议是采用相对路径`#include “own_iostream”在当前目录下搜索。

在Linux系统下,使用gcc编译时查找头文件,按照以下路径顺序查找:
1. gcc编译时,可以设置-I选项以指定头文件的搜索路径,如果指定多个路径,则按照顺序依次查找。比如:gcc -I /usr/local/include/node a.c
2. gcc会查找环境变量C_INCLUDE_PATH,CPLUS_INCLUDE_PATH中指定的路径。
3. 系统默认的路径,分别是/usr/include,/usr/local/include,/usr/lib/gcc-lib/i386-linux/2.95.2/include(gcc库文件的路径,各个系统不一致)。同时,include也可以采用相对路径,比如,a.c需要包含/usr/local/include/ownLib/header.h,由于/usr/local/include是系统的默认搜索路径,所以在a.c中可以用相对路径包含,#include <ownLib/header.h>
(该题答案原文链接:https://blog.csdn.net/roger_ranger/article/details/78535551)

(该总结部分转载,侵删)

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值