宏和函数的区别

本文探讨了C语言中宏定义和函数的区别,详细介绍了宏定义的使用及其潜在的问题,并通过实例对比了宏与函数在实现相同功能时的不同表现。

1.宏

宏定义又称宏代替,宏替换,其声明方式为:#define name(parameter-list) stuff。其中,parameter-list(参数列表)是一个有逗号分隔的符号列表,它们可能出现在stuff中。必须注意的是,参数列表的左括号必须与name紧密相连,如果两者之间有任何空格存在,则参数列表会被解释为stuff的一部分。#define机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏或定义宏。此外,还要注意,在使用宏的时候,一定不不要吝啬“()”,否则,宏替换不会根据你所期待的那样替换,如下所示。

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

#define MUL(x) x*x

int main()
{
    printf("%d\n", MUL(5 + 2));
    //这里也许我们期待的是7*7,打印值49,然而,结果却是17,因为MUL(5+2)被替换为:5+2*5+2
    //所以我们最好这样定义宏:#define MUL(x) (x)*(x),但是,这样依然有问题,如打印如下值

    printf("%d\n", 3 * MUL(5 + 2)); 
    //这里也许我们期待的是3*7*7,打印值147,然而,结果是27,因为3*MUL(5+2)被替换为:3*5+2*5+2
    //所以,在上面的基础上,我们继续加上外层括号,将宏定义写为:#define MUL(x) ((x)*(x))

    system("pause");
    return 0;
}

2. 函数

函数的定义就是函数体的实现。函数体就是一个代码块,他在函数被调用的时候执行。函数定义的语法如下:
类型
函数名(形参)
代码块
对函数的调用规定为“单向值传递”,即只能将实参的值传递给形参,不能奢求形参值的改变从而使实参的值随之改变,因为形参只是实参的一份临时拷贝,函数调用结束后,函数代码块这片内存随之消失。同时,必须牢记不能返回栈指针。如下,例1,实现两个数的交换,例2,实现字符串拷贝。

//例1,实现两个数的交换,错误的函数用法
#include<stdio.h>
#include<stdlib.h>

void Change(int a, int b)
{
    int tmp = 0;

    tmp = a;
    a = b;
    b = tmp;

    printf("函数代码块中:a=%d,b=%d\n",a,b);
}
int main()
{
    int a = 2;
    int b = 5;

    Change(a, b);

    printf("调用结束后:a=%d,b=%d\n",a,b);

    system("pause");
    return 0;
}
//例2,实现字符串拷贝,函数的错误用法,企图返回栈指针
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>

char * CreateMemory(int n)
{

    char *ret = NULL;
    ret = (char *)malloc(n*sizeof(char));

    return &ret;
}

int main()
{
    char *ret = NULL;

    ret=CreateMemory(10);

    strcpy(ret, "hello");

    printf("%s\n", ret);

    free(ret);

    system("pause");
    return 0;
}

3.宏和函数区别

1.宏只占编译时间,函数调用则占用运行时间(分配单元,保存现场,值传递,返回),每次执行都要载入,所以执行相对宏会较慢。

2.使用宏次数多时,宏展开后源程序很长,因为每展开一次都使程序增长,但是执行起来比较快一点(这也不是绝对的,当有很多宏展开,目标文件很大,执行的时候运行时系统换页频繁,效率就会低下)。而函数调用不使源程序变长。
3.宏定义很容易产生二意性,比如上述的例子。
4.函数的参数必须申明为一种特定的类型,所以它只能在类型合适的表达式上使用。然而,比如比较两个数的大小并返回较大,可以这样定义:#define MAX(a,b) ((a)>(b)?(a):(b)) ,这个宏以用于任何可以用<、>操作符比较大小的类型。换句话说,宏是与类型无关的。
5:调用函数只可得到一个返回值,且有返回类型,而宏没有返回值和返回类型,但是用宏可以设法得到几个结果。
6:函数体内有Bug,可以在函数体内打断点调试。如果宏体内有Bug,那么在执行的时候是不能对宏调试的,即不能深入到宏内部。

### 代码替换与参数传递机制 带参数的函数在编程中有着本质的区别定义是通过预处理器在编译前进行字符串替换,而函数则是通过调用机制在运行时进行参数传递执行。的参数替换仅仅是字符串的直接替换,不会对表达式进行计算,而函数的参数传递则是计算过后的值传递,并且参数是有数据类型的[^2]。 ### 运行时期与内存占用 的展开是在编译之前进行的,这意味着它不会在运行时占用额外的内存空间,因为它只是在源代码中进行替换。而函数调用则是在编译之后执行时才调用,这需要在运行时分配内存来存储参数局部变量,以及保存调用上下文[^2]。 ### 类型安全与调试 定义中的参数没有类型的概念,它们只是简单的文本替换,因此在使用时需要注意操作符优先级括号的使用以避免错误。相比之下,函数参数具有明确的数据类型,并且在调用时会进行类型检查,这使得函数更加安全。此外,定义通常难以调试,因为它们在编译前就已经被替换了,而函数可以使用调试工具逐行执行[^1]。 ### 代码膨胀与性能 由于定义在每次使用时都会被完整地插入到代码中,这会导致最终生成的程序体积增大,特别是当被频繁使用时。然而,这种插入方式也意味着的执行速度可能更快,因为没有函数调用返回的开销。相反,函数调用虽然会带来一些运行时开销,但它们的代码只存在于一处,有助于减少程序的整体大小[^1]。 ### 示例代码 下面是一个简单的定义函数实现比较的例子: ```c #include <stdio.h> // 定义 #define MAX_MACRO(a, b) ((a) > (b) ? (a) : (b)) // 函数定义 int max_function(int a, int b) { return (a > b) ? a : b; } int main() { int x = 10, y = 20; int max_macro = MAX_MACRO(x, y); int max_func = max_function(x, y); printf("Max via macro: %d\n", max_macro); printf("Max via function: %d\n", max_func); return 0; } ``` 在这个例子中,`MAX_MACRO` 是一个定义,它在预处理阶段被替换为相应的表达式。而 `max_function` 是一个普通的函数,它在运行时被调用并返回结果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值