宏定义中的困惑

#define swap(t,x,y) {t temp=x;x=y;y=temp;}
在定义宏swap(t,x,y)中t是代指什么
09-11-18  c语言初学者2009 发布
2个回答
时间
投票
  • 0

    7x7xn31

    技术实现 如何逐步实现动态库的加载,类型的匹配,动态链接库函数导出的定义,参考下面宏定义即可:
    #define LIBEXPORT_API extern C __declspec(dllexport) 第一步,我先从简单的调用出发,定义了一个简单的函数,该函数仅仅实现一个整数加法求和: LIBEXPORT_API int mySum(int a,int b){ return a+b;}
    C# 导入定义: public class RefComm
    {
    [DllImport(LibEncrypt.dll,
    EntryPoint= mySum ,
    CharSet=CharSet.Auto,CallingConvention=CallingConvention.StdCall)]
    public static extern int mySum (int a,int b);
    }
    在C#中调用测试: int iSum = RefComm.mySum(,); 运行查看结果iSum为5,调用正确。第一步试验完成,说明在C#中能够调用自定义的动态链接库函数。 第二步,我定义了字符串操作的函数(简单起见,还是采用前面的函数名),返回结果为字符串: LIBEXPORT_API char *mySum(char *a,char *b){sprintf(b,%s,a); return a;}
    C# 导入定义: public class RefComm
    {
    [DllImport(LibEncrypt.dll,
    EntryPoint= mySum ,
    CharSet=CharSet.Auto,
    CallingConvention=CallingConvention.StdCall)]
    public static extern string mySum (string a, string b);
    }
    在C#中调用测试: string strDest=;
    string strTmp= RefComm.mySum(45, strDest); 运行查看结果 strTmp 为45,但是strDest为空。我修改动态链接库实现,返回结果为串b: LIBEXPORT_API char *mySum(char *a,char *b){sprintf(b,%s,a) return b;}
    修改 C# 导入定义,将串b修改为ref方式: public class RefComm
    {
    [DllImport(LibEncrypt.dll,
    EntryPoint= mySum ,
    CharSet=CharSet.Auto,CallingConvention=CallingConvention.StdCall)]
    public static extern string mySum (string a, ref string b);
    }
    在C#中再调用测试: string strDest=;
    string strTmp= RefComm.mySum(45, ref strDest);
    运行查看结果 strTmp 和 strDest 均不对,含不可见字符。再修改 C# 导入定义,将CharSet从Auto修改为Ansi: public class RefComm
    {
    [DllImport(LibEncrypt.dll,
    EntryPoint= mySum ,
    CharSet=CharSet.Ansi,CallingConvention=CallingConvention.StdCall)]
    public static extern string mySum (string a, string b);
    }
    在C#中再调用测试: string strDest=;
    string strTmp= RefComm. mySum(45, ref strDest);
    运行查看结果 strTmp 为45,但是串 strDest 没有赋值。第二步实现函数返回串,但是在函数出口参数中没能进行输出。再次修改 C# 导入定义,将串b修改为引用(ref): public class RefComm
    {
    [DllImport(LibEncrypt.dll,
    EntryPoint= mySum ,
    CharSet=CharSet.Ansi,CallingConvention=CallingConvention.StdCall)]
    public static extern string mySum (string a, ref string b);
    } 运行时调用失败,不能继续执行。 第三步,修改动态链接库实现,将b修改为双重指针: LIBEXPORT_API char *mySum(char *a,char **b){sprintf((*b),%s,a); return *b;}
    C#导入定义: public class RefComm
    {
    [DllImport(LibEncrypt.dll,
    EntryPoint= mySum ,
    CharSet=CharSet.Ansi,CallingConvention=CallingConvention.StdCall)]
    public static extern string mySum (string a, ref string b);
    }
    在C#中调用测试: string strDest=;
    string strTmp= RefComm. mySum(45, ref strDest); 运行查看结果 strTmp 和 strDest 均为45,调用正确。第三步实现了函数出口参数正确输出结果。 第四步,修改动态链接库实现,实现整数参数的输出: LIBEXPORT_API int mySum(int a,int b,int *c){ *c=a+b; return *c;}
    C#导入的定义: public class RefComm
    {
    [DllImport(LibEncrypt.dll,
    EntryPoint= mySum ,
    CharSet=CharSet.Ansi,CallingConvention=CallingConvention.StdCall)]
    public static extern int mySum (int a, int b,ref int c);
    }
    在C#中调用测试: int c=0;
    int iSum= RefComm. mySum(,, ref c); 运行查看结果iSum 和c均为5,调用正确。 经过以上几个步骤的试验,基本掌握了如何定义动态库函数以及如何在 C# 定义导入,有此基础,很快我实现了变长加密函数在 C# 中的调用,至此目标实现。 三、结论 在 C# 中调用 C++ 编写的动态链接库函数,如果需要出口参数输出,则需要使用指针,对于字符串,则需要使用双重指针,对于 C# 的导入定义,则需要使用引用(ref)定义。 对于函数返回值,C# 导入定义和 C++ 动态库函数声明定义需要保持一致,否则会出现函数调用失败。定义导入时,一定注意 CharSet 和 CallingConvention 参数,否则导致调用失败或结果异常。运行时,动态链接库放在 C# 程序的目录下即可,我这里是一个 C# 的动态链接库,两个动态链接库就在同一个目录下运行

    09-11-18 | 添加评论 | 打赏

    评论读取中....

  • 0

    po138ol96

    这个问题可以参看Effective c++ 的条款1


    这个条款最好称为:“尽量用编译器而不用预处理”,因为#define经常被认为好象不是语言本身的一部分。这是问题之一。再看下面的语句:
    #define ASPECT_RATIO 1.653
    编译器会永远也看不到ASPECT_RATIO这个符号名,因为在源码进入编译器之前,它会被预处理程序去掉,于是ASPECT_RATIO不会加入到符号列表中。如果涉及到这个常量的代码在编译时报错,就会很令人费解,因为报错信息指的是1.653,而不是ASPECT_RATIO。如果ASPECT_RATIO不是在你自己写的头文件中定义的,你就会奇怪1.653是从哪里来的,甚至会花时间跟踪下去。这个问题也会出现在符号调试器中,因为同样地,你所写的符号名不会出现在符号列表中。
    解决这个问题的方案很简单:不用预处理宏,定义一个常量:

    const double ASPECT_RATIO = 1.653;

    这种方法很有效。但有两个特殊情况要注意。
    首先,定义指针常量时会有点不同。因为常量定义一般是放在头文件中(许多源文件会包含它),除了指针所指的类型要定义成const外,重要的是指针也经常要定义成const。例如,要在头文件中定义一个基于char*的字符串常量,你要写两次const:

    const char * const authorName = "Scott Meyers";

    另外,定义某个类(class)的常量一般也很方便,只有一点点不同。要把常量限制在类中,首先要使它成为类的成员;为了保证常量最多只有一份拷贝,还要把它定义为静态成员:


    class GamePlayer {
    private:
    static const int NUM_TURNS = 5; // constant eclaration
    int scores[NUM_TURNS]; // use of constant
    ...
    };

    还有一点,正如你看到的,上面的语句是NUM_TURNS的声明,而不是定义,所以你还必须在类的实现代码文件中定义类的静态成员:

    const int GamePlayer::NUM_TURNS; // mandatory definition;
    // goes in class impl.file

    你不必过于担心这种小事。如果你忘了定义,链接器会提醒你。

    旧一点的编译器会不接受这种语法,因为它认为类的静态成员在声明时定义初始值是非法的;而且,类内只允许初始化整数类型(如:int, bool, char 等),还只能是常量。
    在上面的语法不能使用的情况下,可以在定义时赋初值:


    class EngineeringConstants { // this goes in the class
    private: // header file
    static const double FUDGE_FACTOR;
    ...
    };
    // this goes in the class implementation file
    const double EngineeringConstants::FUDGE_FACTOR = 1.35;

    大多数情况下你只要做这么多。唯一例外的是当你的类在编译时需要用到这个类的常量的情况,例如上面GamePlayer::scores数组的声明(编译过程中编译器一定要知道数组的大小)。所以,为了弥补那些(不正确地)禁止类内进行整型类常量初始化的编译器的不足,可以采用称之为“借用enum”的方法来解决。这种技术很好地利用了当需要int类型时可以使用枚举类型的原则,所以GamePlayer也可以象这样来定义:


    class GamePlayer {
    private:
    enum { NUM_TURNS = 5 } // "the enum hack" — makes
    // NUM_TURNS a symbolic name
    // for 5
    int scores[NUM_TURNS];// fine
    };

    除非你正在用老的编译器(即写于1995年之前),你不必借用enum。当然,知道有这种方法还是值得的,因为这种可以追溯到很久以前的时代的代码可是不常见的哟。

    回到预处理的话题上来。另一个普遍的#define指令的用法是用它来实现那些看起来象函数而又不会导致函数调用的宏。典型的例子是计算两个对象的最大值:


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

    这个语句有很多缺陷,光想想都让人头疼,甚至比在高峰时间到高速公路去开车还让人痛苦。
    无论什么时候你写了象这样的宏,你必须记住在写宏体时对每个参数都要加上括号;否则,别人调用你的宏时如果用了表达式就会造成很大的麻烦。但是即使你象这样做了,还会有象下面这样奇怪的事发生:

    int a = 5, b = 0;
    max(++a, b);// a 的值增加了2次
    max(++a, b+10); // a 的值只增加了1次

    这种情况下,max内部发生些什么取决于它比较的是什么值!
    幸运的是你不必再忍受这样愚笨的语句了。你可以用普通函数实现宏的效率,再加上可预计的行为和类型安全,这就是内联函数
    inline int max(int a, int b) { return a > b ? a : b; }
    不过这和上面的宏不大一样,因为这个版本的max只能处理int类型。但模板可以很轻巧地解决这个问题:


    template<class T>
    inline const T& max(const T& a, const T& b)
    { return a > b ? a : b; }

    这个模板产生了一整套函数,每个函数拿两个可以转换成同种类型的对象进行比较然后返回较大的(常量)对象的引用。因为不知道T的类型,返回时传递引用可以提高效率
    有了const和inline,你对预处理的需要减少了,但也不能完全没有它。抛弃#include的日子还很远,#ifdef/#ifndef在控制编译的过程中还扮演重要角色。预处理还不能退休,但你一定要计划给它经常放长假。

    09-11-18 | 添加评论 | 打赏

    评论读取中....

精华知识
更多  
意见反馈 帮助