C语言编程常见问题解答之编译预处理(转载)

C语言中的.c 和.h 文件区别 本文的大部分内容来自网上,经过自己的理解和总结整理而来,希望对和自己有同样的困惑的人予以参考,感谢那些网上的大牛们,是你们的无私,让我学到了很多的东西。          “予人玫瑰,手留余香”。         文章转载请注明出处!         首先我们先看一下这个例子:(由于我也不知道到底那个才是真正的原作者,所以在此我也没办法说明,故而在此冒昧的引用一下,希望大家以后转载的时候 阅读详情
 本章集中讨论与预处理程序有关的问题。在编译程序对程序进行通常的编译之前,要先运行预处理程序。可能你以前没有见过这个程序,因为它通常在幕后运行,程序员是看不见它的,然而,这个程序非常有用。
    预处理程序将根据源代码中的预处理指令来修改你的程序。预处理指令(如#define)为预处理程序提供特定的指令,告诉它应该如何修改你的源代码。预处理程序读入所有包含的文件和待编译的源代码,经过处理生成源代码的预处理版本。在该版本中,宏和常量标识符已用相应的代码和值代替。如果源代码中包含条件预处理指令(如#if),预处理程序将先判断条件,然后相应地修改源代码。
    预处理程序有许多非常有用的功能,例如宏定义,条件编译,在源代码中插入预定义的环境变量,打开或关闭某个编译选项,等等。对专业程序员来说,深入了解预处理程序的各种特征,是创建快速和高效的程序的关键之一。
    在阅读本章时,请记住本章采用的一些技术(以及所提到的一些常见陷阱),以便更好地利用预处理程序的各种功能。

    5.1  什么是宏(macro)?怎样使用宏?
    宏是一种预处理指令,它提供了一种机制,可以用来替换源代码中的字符串,宏是用“#define"语句定义的,下面是一个宏定义的例子:
    #define VERSION—STAMP "1.02"

    上例中所定义的这种形式的宏通常被称为标识符。在上例中,标识符VERSION_STAMP即代表字符串"1.02"——在编译预处理时,源代码中的每个VERSION_STAMP标识符都将被字符串“1.02”替换掉。
    以下是另一个宏定义的例子:
    #define CUBE(x)((x),(x)*(x))
    上例中定义了一个名为CUBE的宏,它有一个参数x。CUBE宏有自己的宏体,即((x)*(x)*(x))——在编译预处理时,源代码中的每个CUBE(x)宏都将被((x)*(x)*(x))替换掉。
    使用宏有以下几点好处;
    (1)在输入源代码时,可省去许多键入操作。   
    (2)因为宏只需定义一次,但可以多次使用,所以使用宏能增强程序的易读性和可靠性。
    (3)使用宏不需要额外的开销,因为宏所代表的代码只在宏出现的地方展开,因此不会引起程序中的跳转。
    (4)宏的参数对类型不敏感,因此你不必考虑将何种数据类型传递给宏。
    需要注意的是,在宏名和括起参数的括号之间绝对不能有空格。此外,为了避免在翻译宏时产生歧义,宏体也应该用括号括起来。例如,象下例中这样定义CUBE宏是不正确的:
denne CUBE(x)   x * x * x
  对传递给宏的参数也要小心,例如,一种常见的错误就是将自增变量传递给宏,请看下例:
#include <stdio. h>
#include CUBE(x) (x * x * x)
void main (void);
void main (void)
{
    int x, y;
    x = 5;
    y = CUBE( + +x);
    printfC'y is %d/n" . y);
}

    在上例中,y究竟等于多少呢?实际上,y既不等于125(5的立方),也不等于336(6* 7*8),而是等于512。因为变量x被作为参数传递给宏时进行了自增运算,所以上例中的CUBE宏实际上是按以下形式展开的:
    y = ((++x) * (++x) * (++x));
    这样,每次引用x时,x都要自增,所以你得到的结果与你预期的结果相差很远,在上例中,由于x被引用了3次,而且又使用了自增运算符,因此,在展开宏的代码时,x实际上为8,你将得到8的立方,而不5的立方。
    上述错误是比较常见的,作者曾亲眼见过有多年C语言编程经验的人犯这种错误。因为在程序中检查这种错误是非常费劲的,所以你要给予充分的注意。你最好试一下上面的例子,亲眼看一下那个令人惊讶的结果值(512)。
    宏也可使用一些特殊的运算符,例如字符串化运算符“#”和。连接运算符“##”。“#”运算符能将宏的参数转换为带双引号的字符串,请看下例:
    define DEBUG_VALUE(v)  printf(#v"is equal to %d./n",v)
    你可以在程序中用DEBUG_VALUE宏检查变量的值,请看下例:
    int x=20;
    DEBUG_VALUE(x);
    上述语句将在屏幕上打印"x is equal to 20"。这个例子说明,宏所使用的“#”运算符是一种非常方便的调试工具。
    “##”运算符的作用是将两个独立的字符串连接成一个字符串,详见5.16。

    请参见:
    5.10 使用宏更好,还是使用函数更好?

  5.16 连接运算符“##”有什么作用?
    5.17 怎样建立对类型不敏感的宏?
    5.18 什么是标准预定义宏?
    5.31 怎样取消一个已定义的宏?

    5.2  预处理程序(preprocessor)有什么作用?   
    C语言预处理程序的作用是根据源代码中的预处理指令修改你的源代码。预处理指令是一种命令语句(如#define),它指示预处理程序如何修改源代码。在对程序进行通常的编译处理之前,编译程序会自动运行预处理程序,对程序进行编译预处理,这部分工作对程序员来说是不可见的。
    预处理程序读入所有包含的文件以及待编译的源代码,然后生成源代码的预处理版本。在预处理版本中,宏和常量标识符已全部被相应的代码和值替换掉了。如果源代码中包含条件预处理指令(如#if),那么预处理程序将先判断条件,再相应地修改源代码。
    下面的例子中使用了多种预处理指令:
# include <stdio. h>
# define TRUE 1
# define FALSE (!TRUE)
# define GREATER (a, b) ((a) > (b) ? (TRUE) : (FALSE))
# define PIG-LATIN FALSE
void main (void);
void main (void)
{
    int x, y;
# if PIG_LATIN
    printf("Easeplay enternay ethay aluevay orfay xnay:") ;
    scanf("%d", &x) ;
    printf("Easeplay enternay ethay aluevay orfay ynay:");
    scanf("%d", &y);
#else
    printf(" Please enter the value for x: ");
    scanf("%d", &x);
    printf("Please enter the value for y: ");
    scanf("%d", &y);
# endif
    if (GREATER(x, y) = = TRUE)
    {
# if PIG- LATIN
    printf("xnay islay eatergray anthay ynay!/n");
#else
    printf {" x is greater than y! /n" ) ;
# endif
    }
    else
    {
# if PIG_LATIN
    printf ("xnay islay otnay eatergray anthay ynay!/n");
#else
    printf ("x is not greater than y!/n");
# endif
    }
}

   上例通过预处理指令定义了3个标识符常量(即TRUE,FALSE和PIG_LATIN)和一个宏(即GREATER(a,b)),并使用了一组条件编译指令。当预处理程序处理上例中的源代码时,它首先读入stdio.h头文件,并解释其中的预处理指令,然后把所有标识符常量和宏用相应的值和代码替换掉,最后判断PIG_LATIN是否为TRUE,并由此决定是使用拉丁文还是
使用英文。
    如果PIG_LATIN为FALSE,则上例的预处理版本将如下所示:
/ * Here is where all the include files
would be expanded. * /
void main (void)
{
    int x, y;
    printf("Please enter the value for X: ");
    scanf("%d", &x);
    printf("Please enter the value for y: ");
    scanf("%d", &y),
    if (((x) > (y) ? (1) : (!1)) == 1)
    {
        printf("x is greater than y!/n");
    }
    else
    {
        printf{"x is not greater than y!/n");
    }
}

    多数编译程序都提供了一个命令行选项,或者有一个独立的预处理程序,可以让你只启动预处理程序并将源代码的预处理版本保存到一个文件中。你可以用这种方法查看源代码的预处理版本,这对调试与宏或其它预处理指令有关的错误是比较有用的。

    请参见:   
    5.3  怎样避免多次包含同一个头文件?
    5.4  可以用#include指令包含类型名不是".h"的文件吗?
    5.12 #include <file>和#include"file"有什么不同?
    5.22 预处理指令#pragma有什么作用?
    5.23 #line有什么作用?

    5.3  怎样避免多次包含同一个头文件? 
    通过#ifndef和#define指令,你可以避免多次包含同一个头文件。在创建一个头文件时,你可以用#define指令为它定义一个唯一的标识符名称。你可以通过#ifndef指令检查这个标识符名称是否已被定义,如果已被定义,则说明该头文件已经被包含了,就不要再次包含该头文件;反之,则定义这个标识符名称,以避免以后再次包含该头文件。下述头文件就使用了这种技术:
# ifndef _FILENAME_H
#define _FILENAME_H

#define VER_NUM " 1. 00. 00"
#define REL_DATE "08/01/94"

#if _WINDOWS_
# define OS_VER     "WINDOWS"
#else
#define OS_VER      "DOS" 
# endif

# endif
 
    当预处理程序处理上述头文件时,它首先检查标识符名称_FILENAME_H是否已被定义——如果没有被定义,预处理程序就对此后的语句进行预处理,直到最后一个#endif语句;反之,预处理程序就不再对此后的语句进行预处理。

    请参见:  
    5.4  可以用#include指令包含类型名不是".h"的文件吗?
    5,12 #include <file>和#include“file”有什么不同?
    5.14 包含文件可以嵌套吗?
    5.15 包含文件最多可以嵌套几层?

    5. 4  可以用#include指令包含类型名不是".h"的文件吗?
    预处理程序将包含用#include指令指定的任意一个文件。例如,如果程序中有下面这样一条语句,那么预处理程序就会包含macros.inc文件。
    #include <macros.inc>

    不过,最好不要用#include指令包含类型名不是".h"的文件,因为这样不容易区分哪些文件是用于编译预处理的。例如,修改或调试你的程序的人可能不知道查看macros.inc文件中的宏定义,而在类型名为".h"的文件中,他却找不到在macros.inc文件中定义的宏。如果将macros.inc文件改名为macros.h,就可以避免发生这种问题。

    请参见:
    5.3怎样避免多次包含同一个头文件?
    5.12#include<file>和#include“file”有什么不同?
    5,14包含文件可以嵌套吗?
    5.15包含文件最多可以嵌套几层?

    5.5  用#define指令说明常量有什么好处?
    如果用#define指令说明常量,常量只需说明一次,就可多次在程序中使用,而且维护程序时只需修改#define语句,不必一一修改常量的所有实例。例如,如果在程序中要多次使用PI(约3.14159),就可以象下面这样说明一个常量:   
    #define PI 3.14159
    如果想提高PI的精度,只需修改在#define语句中定义的PI值,就不必在程序中到处修改了。通常,最好将#define语句放在一个头文件中,这样多个模块就可以使用同一个常量了。
    用#define指令说明常量的另一个好处是占用的内存最少,因为以这种方式定义的常量将直接进入源代码,不需要再在内存中分配变量空间。
    但是,这种方法也有缺点,即大多数调试程序无法检查用#define说明的常量。
    用#define指令说明的常量可以用#under指令取消。这意味着,如果原来定义的标识符(如NULL)不符合你的要求,你可以先取消原来的定义,然后重新按自己的要求定义一个标识符,详见5.31。

    请参见:
    5.6用enum关键字说明常量有什么好处?
    5.7与用#define指令说明常量相比,用enum关键字说明常量有什么好处?
    5.31怎样取消一个已定义的宏?

    5.6  用enum关键字说明常量有什么好处?
    用enum关键字说明常量(即说明枚举常量)有三点好处:
    (1)用enum关键字说明的常量由编译程序自动生成,程序员不需要用手工对常量一一赋值。
    (2)用enum关键字说明常量使程序更清晰易读,因为在定义enum常量的同时也定义了一个枚举类型标识符。
    (3)在调试程序时通常可以检查枚举常量,这一点是非常有用的,尤其在不得不手工检查头文件中的常量值时。
    不过,用enum关键字说明常量比用#define指令说明常量要占用更多的内存,因为前者需要分配内存来存储常量。
    以下是一个在检测程序错误时使用的枚举常量的例子:
    enum Error_Code
    {
        OUT_OF_MEMORY,
        INSUFFICIENT_DISK_SPACE,   
        LOGIC_ERROR,   
        FILE_NOT_FOUND   
    }  ;
    与用#define说明常量相比,用enum说明常量还有其它好处,这一点将在5.7中作更详细的介绍。

    请参见:
    5.5 用#define指令说明常量有什么好处?
    5.7 与用#denne指令说明常量相比,用enum关键字说明常量有什么好处?

    5.7  与用#define指令说明常量相比,用enum关键字说明常量有什么好处?
    与用#define指令说明常量(即说明标识符常量)相比,用enum关键字说明常量(即说明枚举常量)有以下几点好处:
    (1) 使程序更容易维护,因为枚举常量是由编译程序自动生成的,而标识符常量必须由程序员手工赋值。例如,你可以定义一组枚举常量,作为程序中可能发生的错误的错误号,请看下例:
    enum Error_Code   
    {
       OUT_OF_MEMORY,   
       INSUFFICIENT_DISK_SPACE,   
       LOGIC_ERROR,
       FILE+NOT_FOUND
    }  ;  
    在上例中,OUT_OF_MEMORY等枚举常量依次被编译程序自动赋值为0,1,2和3。
    同样,你也可以用#define指令说明类似的一组常量,请看下例:
    #define OUT_OF_MEMORY             0   
    #define INSUFFICIENT_DISK_SPACE    1
    #define LOGIC_ERROR                2
    #define FILE_NOT_FOUND             3
    上述两例的结果是相同的。
    假设你要增加两个新的常量,例如DRIVE_NOT_READY和CORRUPT_FILE。如果常量原来是用enum关键字说明的,你可以在原来的常量中的任意一个位置插入这两个常量,因为编译程序会自动赋给每一个枚举常量一个唯一的值;如果常量原来是用#define指令说明的,你就不得不手工为新的常量赋值。在上面的例子中,你并不关心常量的实际值,而只关心常量的值是否唯一,因此,用enum关键字说明常量使程序更容易维护,并且能防止给不同的常量赋予相同的值。
    (2)使程序更易读,这样别人修改你的程序时就比较方便。请看下例:
    void copy_file(char*  source_file_name,  char *  dest_file_name)
    {   
        ......
        Error_Code,err;
        ......
        if(drive_ready()!=TRUE)
        err=DRIVE_NOT_READY;
        ......
    }   
    在上例中,从变量err的定义就可以看出;赋予err的值只能是枚举类型Error_Code中的数值。因此,当另一个程序员想修改或增加上例的功能时,他只要检查一下Error_Code的定义,就能知道赋给err的有效值都有哪些。
 注意:将变量定义为枚举类型后,并不能保证赋予该变量的值就是枚举类型中的有效值。
    在上例中,编译程序并不要求赋予err的值只能是Error—Code类型中的有效值,因此,程序员自己必须保证程序能实现这一点。
    相反,如果常量原来是用#define指令说明的,那么上例就可能如下所示:
    void copy_file(char *source *file,char *dest_file)
    { 
        ...... 
        int err;
        ......
        if(drive_ready()!=TRUE)
            err=DRIVE_NOT_READY;
        ......
    }
    当另一个程序员想修改或增加上例的功能时,他就无法立即知道变量err的有效值都有哪些,他必须先检查头文件中的#defineDRIVE_NOT_READY语句,并且寄希望于所有相关的常量都在同一个头文件中定义。
    (3)使程序调试起来更方便,因为某些标识符调试程序能打印枚举常量的值。这一点在调试程序时是非常用的,因为如果你的程序在使用枚举常量的一行语句中停住了,你就能马上检查出这个常量的值;反之,绝大多数调试程序无法打印标识符常量的值,因此你不得不在头文件中手工检查该常量的值。

    请参见:
    5.5 用#dehne指令说明常量有什么好处?
    5.6 用enum关键字说明常量有什么好处?

    5.8  如何使部分程序在演示版中失效?
    如果你在为你的程序制作一个演示版,你可以通过预处理指令使你的程序的一部分生效或失效。以下是一个用#if和#endif指令实现上述功能的例子:
    int save_document(char * doc_name)
    {
    #if DEMO_VERSION
        printf("Sorry!  You can't save documents using the DEMO version Of
        ->this program!/n");
        return(0);
    #endif   
    }
    在编写演示版程序的源代码时,如果插入了#define DEMO_VERSION这行语句,预处理程序就会将上述save_document()函数中符合编译条件的代码包含进来,这样,使用演示版的用户就无法保存他们的文件。更好的方法是,在编译选项中定义DEMO_VERSION,这样就不必修改程序的源代码了。
    上述技巧在许多不同的情况下都很有用。例如,如果你编写的程序可能要在多种操作系统或操作环境下使用,你就可以定义一些象WINDOWS_VER,UNIX_VER和DOS_VER这样的宏,通过它们指示预处理程序如何根据具体条件将相应的代码包含到你的程序中去。

    请参见:
    5.32怎样检查一个符号是否已被定义?

    5.9  什么时候应该用宏代替函数?   
    见5.1-0。

    请参见:
    5.1什么是宏(macro)?怎样使用宏?
    5.1-0使用宏更好,还是使用函数更好?
    5.17怎样建立对类型不敏感的宏?

    5.10  使用宏更好,还是使用函数更好?
    这取决于你的代码是为哪种情况编写的。宏与函数相比有一个明显的优势,即它比函数效率更高(并且更快),因为宏可以直接在源代码中展开,而调用函数还需要额外的开销。但是,宏一般比较小,无法处理大的、复杂的代码结构,而函数可以。此外,宏需要逐行展开,因此宏每出现一次,宏的代码就要复制一次,这样你的程序就会变大,而使用函数不会使程序变大。
    一般来说,应该用宏去替换小的、可重复的代码段,这样可以使程序运行速度更快;当任务比较复杂,需要多行代码才能实现时,或者要求程序越小越好时,就应该使用函数。

    请参见:
    5.1  什么是宏(macro)?怎样使用宏?
    5.17 怎样建立对类型不敏感的宏?

    5.11 在程序中加入注释的最好方法是什么?
    大部分C编译程序为在程序中加注释提供了以下两种方法:
    (1)分别是用符号“/*”和“*/”标出注释的开始和结束,在符号“/*”和“*/”之间的任何内容都将被编译程序当作注释来处理。这种方法是在程序中加入注释的最好方法。
    例如,你可以在程序中加入下述注释:
    /*
    This portion Of the program contains
    a comment that is several lines long
    and is not included in the compiled
    Version Of the program.
    */
    (2)用符号“// ”标出注释行,从符号“// ”到当前行末尾之间的任何内容都将被编译程序当作注释来处理。当要加入一行独立的注释时,使用符号“//”是最方便的。但是,对于上面的例子,由于一段独立的注释中有4行内容,因此使用符号“//”是不合适的,请看下例:
    // This portion Of the program contains   
    // a  comment  that  is  several  lines  long
    // and is not included in the compiled   
    // Version Ofthe program.
    需要注意的是,用符号"// "加入注释的方法与ANSI标准是不兼容的,许多版本较早的编译程序不支持这种方法。

    请参见:
    5.8 如何使部分程序在演示版中失效?

    5.12  #include <file>和#include“file”有什么不同?
    在C程序中包含文件有以下两种方法:
    (1)用符号“<”和“>”将要包含的文件的文件名括起来。这种方法指示预处理程序到预定义的缺省路径下寻找文件。预定义的缺省路径通常是在INCLUDE环境变量中指定的,请看下例:   
    INCLUDE=C:/COMPILER/INCLUDE;S:/SOURCE/HEADERS;
    对于上述INCLUDE环境变量,如果用#include<file>语句包含文件,编译程序将首先到C:/COMPILER/INCLUDE目录下寻找文件;如果未找到,则到S:/SOURCE/HEADERS
  目录下继续寻找;如果还未找到,则到当前目录下继续寻找。   
    (2)用双引号将要包含的文件的文件名括起来。这种方法指示预处理程序先到当前目录下寻找文件,再到预定义的缺省路径下寻找文件。   
    对于上例中的INCLUDE环境变量,如果用#include“file”语句包含文件,编译程序将首先到当前目录下寻找文件;如果未找到,则到C:/COMPILER/INCLUDE目录下继续寻找;如果还未找到,则到S:/SOURCE/HEADERS目录下继续寻找。
    #include<file>语句一般用来包含标准头文件(例如stdio.h或stdlib.h),因为这些头文件极少被修改,并且它们总是存放在编译程序的标准包含文件目录下。#include“file”语句一般用来包含非标准头文件,因为这些头文件一般存放在当前目录下,你可以经常修改它们,并且要求编译程序总是使用这些头文件的最新版本。

    请参见:
    5.3  怎样避免多次包含同一个头文件?
    5. 4  可以用#include指令包含类型名不是“.h”的文件吗?
    5.14 包含文件可以嵌套吗?
    5.15 包含文件最多可以嵌套几层?

    5.13  你能指定在编译时包含哪一个头文件吗?   
    你可以通过#if,#else和#endif这组指令实现这一点。例如,头文件alloc.h和malloc.h的作用和内容基本相同,但前者供BorlandC++编译程序使用,后者供MicrosoftC++编译程序使用。如果你在编写一个既支持BorlandC++又支持MicrosoftC++的程序,你就应该指定在编译时是包含alloc.h头文件还是包含malloc.h头文件,请看下例:
    #ifdef __BORLANDC__
    #include<alloc.h>
    #else
    #include<malloc.h>
    #endif
    当用BorlandC++编译程序处理上例时,编译程序会自动定义__BORLANDC__标识符名称,因此alloc.h头文件将被包含进来;当用microsoftC++编译程序处理上例时,由于编译程序检查到__BORLANDC__标识符名称没有被定义,因此malloc.h头文件将被包含进来。

    请参见:
    5.21怎样判断一个程序是用C编译程序还是用C++编译程序编译的?
    5.32怎样检查一个符号是否已被定义?

    5.14  包含文件可以嵌套吗?
    包含文件可以嵌套,但你应该避免多次包含同一个文件(见5.3)。
    过去,人们认为头文件嵌套是一种不可取的编程方法,因为它增加了MAKE程序中的依赖跟踪函数(dependencytrackingfunction)的工作负担,从而降低了编译速度。现在,通过引入预编译头文件(precompiledheaders,即所有头文件和相关的依赖文件都以一种已被预编译的状态存储起来)这样一种技术,许多流行的编译程序已经解决了上述问题。
    许多程序员都喜欢建立一个自己的头文件,使其包含每个模块所需的每个头文件。这是一个头文件。

    包含文件可以嵌套,但你应该避免多次包含同一个文件(见5.3)。
    过去,人们认为头文件嵌套是一种不可取的编程方法,因为它增加了MAKE程序中的依赖跟踪函数(dependencytrackingfunction)的工作负担,从而降低了编译速度。现在,通过引入预编译头文件(precompiledheaders,即所有头文件和相关的依赖文件都以一种已被预编译的状态存储起来)这样一种技术,许多流行的编译程序已经解决了上述问题。
    许多程序员都喜欢建立一个自己的头文件,使其包含每个模块所需的每个头文件。这是一个头文件。

    请参见:
    5.3  怎样避免多次包含同一个头文件?
    5.4  可以用#include指令包含类型名不是“.h”的文件吗?
    5.12 #include<file~和#include“file”有什么不同?
    5.15 包含文件最多可以嵌套几层?

    5.15 包含文件最多可以嵌套几层?
    尽管理论上包含文件可以嵌套任意多层,但是,如果嵌套层数太多,编译程序就会用光它的堆栈空间。因此,实际的嵌套层数是有限的,它一般取决于你的硬件设置和编译程序的版本。
    在编写程序时,你应该避免过多的嵌套。一般来说,只有在确实需要时才嵌套包含文件,例如建立一个头文件,使其包含每个模块所需的每个头文件。

    请参见:
    5.3  怎样避免多次包含同一个头文件?
    5.4  可以用#include指令包含类型名不是“.h”的文件吗?
    5.12 #include<file>和#include“file”有什么不同?
    5.14 包含文件可以嵌套吗?

    5.16 连接运算符“##”有什么作用?
    连接运算符“##”可以把两个独立的字符串连接成一个字符串。在C的宏中,经常要用到“##”运算符,请看下例:
    #include<stdio.h>
    #define SORT(X)  sort_function # # X
    void main(vOid);
    void main(vOid)
    {
        char *array;
        int  elements,element_size;.
        SORT(3) (array,elements,element_size);
    }
    在上例中,宏SORT利用“##”运算符把字符串sort_function和经参数x传递过来的字符串连接起来,这意味着语句
    SORT(3)(array,elemnts,element_size);
    将被预处理程序转换为语句
    sort_function3(array,elements,element_size);
    从宏SORT的用法中你可以看出,如果在运行时才能确定要调用哪个函数,你可以利用“##”运算符动态地构造要调用的函数的名称。

    请参见:
    5.1什么是宏?怎样使用宏?
    5.17怎样建立对类型敏感的宏?

工业质检实战:用海康VisionMaster的BLOB分析搞定PCB焊点缺陷检测 本文详细介绍了如何利用海康VisionMaster平台的BLOB分析技术,构建一套针对PCB焊点缺陷的自动化视觉检测方案。文章从核心原理出发,重点阐述了如何通过动态阈值分割和模板匹配定位,克服工业现场光照不均、位置偏移等挑战,实现虚焊、漏焊等缺陷的精准、稳定检测,并提供了实战调优与系统集成的关键思路。 阅读详情

相关推荐

AHB2eflash 控制器设计笔记

① SRAM② DRAM③ SSD④ SATA⑤ U 盘⑥ 移动硬盘⑦ 机械磁盘eflash。

weixin_46220498的博客 3078

如何使用Macros Library

右键新建一个Macros Library,可以以其他脚本为父类,看个人 加几个方法,Category就是相当于分类,更有层次感 在其他脚本中使用 测试运行

weixin_42145757的博客 311

MuJoCo 相机图片怎么拿?视角调整获取物体图片及实时显示(附代码)

在Mujoco的仿真中,对于识别抓取场景来说,如何获得添加的body等物体的实时图像很重要,今天就分享下如何添加相机视角及可视化小方块(举例)的实时图片。复制一个scene.xml为scene_withcamera.xml,在xml中添加camera和小方块如下。设置xml文件中添加的相机body id。需要创建一个离屏渲染器。

darcsdn 2128

编译预处理和宏:带参数的宏

像函数的宏 #define cube(x) ((x)(x)(x)) 宏可以带参数 #include&lt;stdio.h&gt; #define cube(x) ((x)*(x)*(x)) int main(){ int i; scanf("%d".&amp;i); printf("%d\n",cube(i));//替换成 printf("%d\n",((i...

Qingzhu 582

信捷CODESYS平台伺服电机转矩控制模式应用与切换

信捷CODESYS二次开发软件:XS Studio 在运动控制中的转矩控制案例

weixin_64614866的博客 1万+

从PCIe 3.0到6.0:为什么“阻抗一致性”比“绝对值”更重要?聊聊连接器与板材的选型影响

本文探讨了PCIe从3.0到6.0演进过程中,阻抗一致性相比绝对值的重要性,分析了连接器与板材选型对高速信号完整性的影响。随着速率提升至64GT/s,系统级阻抗控制和验证成为关键,文章提供了选型指南、验证方法论及工程实践建议,帮助工程师应对高速设计挑战。

weixin_42556197的博客 384

常见路径规划算法介绍

本文介绍了算法书上常见的路径规划算法和工业界目前比较流行的高效分层路径规划算法。 目录 1. 经典路径规划算法 1.1Dijkstra 1.2 Bellman-Ford 1.3 SPFA 1.4A* 1.5Bidirectional Search(双向搜索) 2.分层规划算法 2.1CH算法(Contraction Hierarchies) 2.1.1CH预处理 2.1.2CH查询 2.2CCH算法(Customizable Contra...

bitkevin的博客 1万+

农业杂草检测——棉花杂草数据集

然后将本层13×13的特征图谱经过×2上采样扩充到26×26,并与上层中26×26的特征图谱拼接,得到新的26×26的特征图谱,在该特征尺度上进行第二次预测,此时得到的特征图谱是相对输入图像16倍下采样,具有中尺度感受野,适合检测中等尺度对象;通过目标检测,能够及时清除杂草,减少病虫害的滋生和传播,保障棉花的健康生长,并有助于降低生产成本。对于地膜覆盖的棉田,由于膜内耕作层土温高、底墒好,杂草出苗早而集中,一般覆膜后5 - 7天杂草陆续出土,在墒情好的情况下,15天左右即形成出土高峰。

qq_38220914的博客 1247

C语言预处理命令详解

  一  前言      预处理(或称预编译)是指在进行编译的第一遍扫描(词法扫描和语法分析)之前所作的工作。预处理指令指示在程序正式编译前就由编译器进行的操作,可放在程序中任何位置。      预处理是C语言的一个重要功能,它由预处理程序负责完成。当对一个源文件进行编译时,系统将自动引用预处理程序对源程序中的预处理部分作处理,处理完毕自动进入对源程序的编译。      C语言提供多种预处...

weixin_33743248的博客 933

RAG面试内容整理-8. 文档分块策略(滑动窗口、语义切分)

RAG系统通常面对长文档或整篇知识库资料的检索需求,但大模型一次处理的文本长度有限,向量索引也需固定大小的文本片段。因此,将长文档拆分成合适片段是必不可少的步骤,被称为文档分块(chunking)策略。

不务正业的猿 691

使用Visual Studio 2022创建CUDA编程项目

本文介绍了在Visual Studio 2022中配置CUDA开发环境的完整流程。首先需要安装VS2022并勾选C++开发组件,然后安装CUDA Toolkit和兼容的NVIDIA驱动。创建项目时选择CUDA模板,VS会自动生成数组相加的示例代码。项目配置包含CUDA头文件依赖和链接器设置(cudart_static.lib)。文章详细说明了从环境搭建到代码编写、调试的完整步骤,帮助开发者快速上手CUDA编程。

新兴IT民工的专栏 1万+

C语言进阶——4.宏定义

C语言进阶——3.宏定义

Sunrise的博客 954

YOLOv8科研探索:HAttention激活更多特征点,真心助你提升细粒度检测精度

如果你在集成中遇到问题,不妨从特征点激活阈值、多尺度融合的逻辑入手,一点点打磨——科研的突破,往往就藏在这些细致的尝试里。想象一下,普通模型可能只关注目标的“大块区域”,而HAttention能让模型同时捕捉到目标的“边缘”“纹理”等细粒度特征点,就像给模型装上了“显微镜”,再小的细节也逃不过它的“眼睛”。比如,小目标的细粒度特征点(如昆虫的触角)和大目标的整体特征点(如昆虫的身体)会被同时考虑,通过注意力权重的分配,让小细节融入全局理解,避免模型“只见树木不见森林”。HAttention通过。

FJN110的博客 80

C语言编程常见问题解答之数组

(隔段时间不看就会在实际使用中忘记掉---c语言数组、异或指针的使用),转载一下,忘时供查询  C语言处理数组的方式是它广受欢迎的原因之一。C语言对数组的处理是非常有效的,其原因有以下三点:     第一,除少数翻译器出于谨慎会作一些繁琐的规定外,C语言的数组下标是在一个很低的层次上处理的。但这个优点也有一个反作用,即在程序运行时你无法知道一个数组到底有多大,或者一个数组下标是否有效。ANSI

漂飘渔人 3637

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

c语言编辑编译链接,C语言的“编译、链接”

我们写的代码,仅仅是文本文件(txt),不管后缀怎么变,终究是文本文件,计算机只能显示,不能做事(执行)。文本文件通过“编译、链接”,成为可执行文件(windows下是exe):编译:生成.o文件,也被称为“对象文件”、“目标文件”。链接:将有关的目标文件连接起来,使所有的.o文件成为一个可执行的整体。为什么要生成“.o文件”?这不是多此一举嘛?——大型程序往往需要分文件写,每个源文件会生成对应的...

weixin_27038261的博客 595

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

嵌入式面试知识点总结 -- C语言篇

之前有写过 日常生活 – 嵌入式面试 ,讲了面试大部分都会问哪些问题。 也有自己总结了一些面试题: C语言再学习 – 详解C++/C 面试题 1 C语言再学习 – 详解C++/C 面试题 2 但是每次我都要翻好几篇文章按着看知识点,这就很烦了。现在将所用到的知识点在这篇文章内加以总结。 ...

不积跬步,无以至千里 7406

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

[转载]C/C++/MFC 编译运行错误解决方法收集error

[转载]C/C++/MFC 编译运行错误解决方法收集error 2011年12月19日   编译时提示error C2065: “IDD_DIALOG1” : 未声明的标识符 错误的可能原因及解决方法如下:   1.出错文件中没有包含资源文件ID声明的resource.h文件。在出错文件中加入#include “resource.h”语句。   2.工程附件包含目录的路径下没有r...

gzg844cz 607

C 语言常见问题集

从 http://c-faq-chn.sourceforge.net/ccfaq/index.html 转载过来,学习C的好助手。 目录 1. 前言 2. 声明和初始化 2.1 我如何决定使用那种整数类型? 2.2 64位机上的64位类型是什么样的? 2.3 怎样定义和声明全局变量和函数最好? 2.4 extern在函数声明中是什么意思? 2.5 关键字auto到底有什么用途? 2....

xpp02的专栏 305

【C语言】题集 of ①

🍪第一题→给两个正整数 a 和 b,输出 a 与 b 的和(自定义函数也行)🍪 🍪第二题→定义符号常量#define,用这个来打印自己的出生日期🍪 🍪第三题→输入三个数字,找出其中最大值并输出🍪 🍪第四题→判断是不是闰年,是的话就YES,不是就NO🍪 🍪第五题→输入多组数据,求a+b的和🍪

"謓泽"正在路上朝着"攻城狮"方向"前进四"! 2316

【转】嵌入式程序员C语言笔试题目及参考答案-东软集团

转载自:http://yinrenjieone.blog.163.com/blog/static/548995200911139141220/ 预处理(Preprocessor) 1 . 用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题) #define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL

自用博客 2775

16道嵌入式C语言面试题(转载)

转自:http://blog.chinaunix.net/uid-26495963-id-3165021.html预处理器(Preprocessor)1 . 用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题) #define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL 我在这想看到几件事情: 1) #defin

zxh1592000的博客 679

查看结构大小c语言,C语言中结构体大小计算

1.普通结构体struct student{char sex;char a;char b;int age;char name[100];};该结构体大小为108解答:1.先算struct的对齐大小,对齐的大小也是取决于struct成员中字节对齐最大的那个;在给的题目中就是int类型,也就是4byte。如果结构体成员小于4byte,需要补齐填满4byte2、三个char类型虽然只有3byte,但是为...

weixin_39761481的博客 1079

[转载]linux C经典面试题五

<br />原文地址:linux C经典面试题五作者:忧伤岁月<br />华为笔试题 <br />1.写出判断ABCD四个表达式的是否正确, 若正确, 写出经过表达式中 a的值(3分)<br />int a = 4;<br />(A)a += (a++); (B) a += (++a) ;(C) (a++) += a;(D) (++a) += (a++);<br />a = ?<br />答:C错误,左侧不是一个有效变量,不能赋值,可改为(++a) += a;<br />改后答案依次为9,10,10,11<

Napoleon的专栏 542

【整理】2天带你学完C语言,使用Chatgpt进行深度个人学习

声明:本文章系列基于chatgpt3.5模型算法导出,个人将结合自己所学进行系统的整理和修改,全文约3.56万字。声明:本文章系列基于chatgpt3.5模型算法导出,个人将结合自己所学进行系统的整理和修改,全文约3.56万字。

Phofomy的博客 433

16道嵌入式C语言面试题(经典)

转载于 http://blog.chinaunix.net/u1/34474/ 预处理器(Preprocessor) 1 . 用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)          #define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL 我在这想看到几件事情: 1) #define 语法的基本知识(例如:不能...

qq_15063463的博客 508

mooc哈尔滨c语言作业答案,哈尔滨工业大学C语言2016年MOOC在线测试答案.doc

哈尔滨工业大学C语言2016年MOOC在线测试答案本答案仅供参考,请勿用于商业用途或进行未经同意的转载第3周1计算两个数的平方和(3分)题目内容:从键盘读入两个实数,编程计算并输出它们的平方和,要求使用数学函数pow(x,y)计算平方值,输出结果保留2位小数。提示:使用数学函数需要在程序中加入编译预处理命令 #include 以下为程序的运行结果示例:please input x and y:1....

weixin_39755853的博客 751
上一篇: 引用小结
下一篇: 二叉树遍历非递归算法
metasearch
博客等级 码龄21年 158粉丝 258原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值