Predefined Macros

Refer to https://msdn.microsoft.com/en-us/library/b0084kay.aspx

_MSC_VER Evaluates to an integer literal that encodes the major and minor number components of the compiler’s version number. The major number is the first component of the period-delimited version number and the minor number is the second component.

For example, if the version number of the Visual C++ compiler is 17.00.51106.1, the _MSC_VER macro evaluates to 1700. Type cl /? at the command line to view the compiler’s version number.

 

在 Visual Studio 调试器中指定符号 (.pdb) 和源文件

来源:https://technet.microsoft.com/zh-cn/magazine/ms241613.aspx

发布时间: 2016年4月

查找并指定符号文件和源文件;指定符号加载行为、使用符号和源服务器;自动或按需加载符号。

内容

查找符号 (.pdb) 文件

查找源文件

查找符号 (.pdb) 文件

System_CAPS_note注意
在 VS 2012 之前的 Visual Studio 版本中,在远程计算机上调试托管代码需要符号文件也位于远程计算机上。 现在,这已经不成问题了。 所有符号文件必须位于本地计算机上或 Visual Studio“选项”对话框的“调试”/“符号”页中指定的位置。 请参阅“Microsoft 应用程序生命周期管理”博客上的 Visual Studio 2012 和 2013 中的 .NET 远程符号加载更改
  • 调试器搜索 .pdb 文件的位置 | 为什么符号文件需要与可执行文件完全匹配? | 指定符号位置和加载行为 | 使用符号服务器查找不在你的本地计算机上的符号文件 | 调试时查找并加载符号 | 为符号文件设置编译器选项

程序数据库 (.pdb) 文件(也称为符号文件)将你在类、方法和其他代码的源文件中创建的标识符映射到在项目的已编译可执行文件中使用的标识符。 .pdb 文件还可以将源代码中的语句映射到可执行文件中的执行指令。 调试器使用此信息确定两个关键信息:显示在 Visual Studio IDE 中的源文件和行号,以及可执行文件中在设置断点时要停止的位置。 符号文件还包含源文件的原始位置以及(可选)源服务器的位置(可从中检索源文件)。

在 Visual Studio IDE 中调试项目时,调试器知道查找代码的 .pdb 和源文件的确切位置。 如果要在项目源代码之外调试代码(如项目调用的 Windows 或第三方代码),则你必须指定 .pdb(也可以是外部代码的源文件)的位置,这些文件需要与可执行文件完全匹配。

调试器搜索 .pdb 文件的位置

  1. 在 DLL 或可执行文件中指定的位置。

    (默认情况下,如果你在计算机上已生成 DLL 或可执行文件,则链接器会将关联的 .pdb 文件的完整路径和文件名放入 DLL 或可执行文件中。 调试器首先会检查在 DLL 或可执行文件内指定的位置中是否存在符号文件。 这很有帮助,因为你的计算机上始终有可供已编译代码使用的符号。)

  2. 可存在于与 DLL 或可执行文件相同文件夹中的 .pdb 文件。
  3. 所有本地符号缓存文件夹。
  4. 在 Microsoft 符号服务器(如果启用)等上指定的任何网络、Internet 或本地符号服务器和位置。

返回页首 查找符号 (.pdb) 文件

返回页首 内容

为什么符号文件需要与可执行文件完全匹配?

调试器只会为可执行文件加载与该可执行文件生成之时所创建的 .pdb 文件完全匹配的 .pdb 文件(即该 .pdb 文件必须是原始 .pdb 文件或其副本)。 由于除了创建正确且高效的代码的主要任务之外,编译器的编译速度也得到了优化,因此可执行文件的实际布局可更改,即使代码本身未更改也是如此。 有关详细信息,请参阅 MSDN 博客文章为什么 Visual Studio 要求调试器符号文件须与同时生成的二进制文件完全匹配?

返回页首 查找符号 (.pdb) 文件

返回页首 内容

指定符号位置和加载行为

在 VS IDE 中调试项目时,调试器将自动加载位于项目目录中的符号文件。 你可在 VS“选项”对话框的“调试”/“符号”页中指定 Microsoft、Windows 或第三方组件的可选搜索路径和符号服务器。 “符号”页上,你还可指定希望调试器自动加载其符号的特定模块。 之后,你可以在主动进行调试时手动更改这些设置。

打开“调试”/“符号”页

  1. “调试”菜单上,选择“选项”
  2. “选项”对话框中,选择“调试”节点下的“符号”
工具 - 选项 - 调试 - 符号页

指定符号服务器或搜索位置

  1. 选择文件夹 工具/ 选项/ 调试/符号文件夹图标 图标。 “符号文件(.pdb)位置”框中将显示可编辑的文本。
  2. 键入符号服务器或符号位置的 URL 或目录路径。 语句结束有助于找到正确的格式。
  3. 若要改进符号加载性能,请在路径中键入符号可由“在此目录下缓存符号”框中的符号服务器复制的本地目录,或可将符号复制到其中的本地目录。
    System_CAPS_note注意
    不要将符号缓存放入受保护文件夹(例如,C:\Windows 文件夹或其子文件夹之一)。 而应使用可读写的文件夹。

指定符号加载行为

你可指定开始调试时要从“符号文件(.pdb)位置”框位置自动加载的文件。 始终加载项目目录中的符号文件。

  1. 选择“除排除模块之外的所有模块”来为所有模块(除了你在选择“指定排除的模块”链接时指定的模块之外)加载所有符号。
  2. 选择“仅指定的模块”选项,然后选择“指定模块”来列出要自动加载其符号文件的模块。 其他模块的符号文件被忽略。

指定其他符号选项

你还可在 VS“选项”对话框的“调试”/“常规”页上设置以下选项:

启动时若无符号则发出警告(仅限本机)

选定后,如果尝试调试在调试器中没有对应符号信息的程序,系统将显示警告对话框。

加载 DLL 导出

选定后,加载 DLL 导出表。 处理 Windows 消息、Windows 过程 (WindowProc)、COM 对象、封送或不具有其符号的任何 DLL 时,DLL 导出表中的符号信息将很有用。 读取 DLL 导出信息会占用一些系统开销。 因此,默认情况下此功能被禁用。

若要查看 DLL 导出表中的可用符号,请使用dumpbin /exports符号可用于任何 32 位系统 DLL。 dumpbin /exports输出中,可以查看到精确的函数名,包括非字母数字字符。 这对于在函数上设置断点很有用。 DLL 导出表中的函数名在调试器的其他位置似乎被截断了。 调用将按调用顺序列出,当前函数(嵌套最深的函数)位于顶端。 有关详细信息,请参阅 dumpbin /exports

返回页首 内容 | 查找符号 (.pdb) 文件

使用符号服务器查找不在你的本地计算机上的符号文件

Visual Studio 可从实现 symsrv 协议的符号服务器下载调试符号文件。 Visual Studio Team Foundation ServerWindows 调试工具是可实现符号服务器的两个工具。 在 VS“选项”对话框中指定要使用的符号服务器。

可供使用的符号服务器包括:

Microsoft 公共符号服务器

若要调试在调用系统 DLL 或第三方库时出现的故障,通常需要使用系统 .pdb 文件,这些文件包含表示 Windows DLL、EXE 以及设备驱动程序的符号。 你可从 Microsoft 公共符号服务器获取这些符号。 除了 MDAC、IIS、ISA 和 .NET Framework 之外,Microsoft 公共符号服务器为 Windows 操作系统提供符号。

若要使用 Microsoft 符号服务器,请选择“调试”菜单上的“选项和设置”,然后选择“符号”选择“Microsoft 符号服务器”

内部网络或本地计算机上的符号服务器

你的团队或公司可为你自己的产品创建符号服务器,并作为外部源符号的缓存。 你自己的计算机上可能具有符号服务器。 你可在 VS“选项”对话框的“调试”/“符号”页上输入符号服务器的位置作为 URL 或路径。

第三方符号服务器

Windows 应用程序和库的第三方提供程序可提供对 Internet 上的符号服务器的访问。 你还可在“调试”/“符号”页上输入这些符号服务器的 URL。

System_CAPS_note注意
如果使用 Microsoft 公共符号服务器以外的符号服务器,请确保该符号服务器及其路径是可信任的。 由于符号文件可以包含任意可执行代码,因此你可能面临安全威胁。

返回页首 内容 | 查找符号 (.pdb) 文件

调试时查找并加载符号

只要调试器处于中断模式,你就可以为之前被调试器选项排除的或编译器无法找到的模块加载符号。 可以从调用堆栈窗口、模块窗口、局部变量窗口、自动窗口和所有监视窗口的快捷菜单中加载符号。 如果调试器在没有可用符号或源文件的代码中中断,则将显示一个文档窗口。 在此可以找到所缺文件的相关信息,并采取相应措施来查找并加载它们。

使用未加载任何符号的文档页查找符号

调试器可通过多种方式中断没有可用符号的代码:

  1. 单步执行代码。
  2. 通过断点或异常中断代码。
  3. 切换到其他线程。
  4. 通过在“调用堆栈”窗口中双击帧来更改堆栈帧。

出现上述事件之一时,调试器将显示“未加载任何符号”页来帮助你查找和加载必需的符号。

“未加载任何符号”页
  • 若要更改搜索路径,请选择未选定的路径或选择“新建”,然后输入新路径。 选择“加载”以再次搜索路径,并在找到符号文件时加载符号文件。
  • 选择浏览并查找可执行文件名称 来重写任何符号选项并重试搜索路径。 如果找到符号文件,或显示了文件资源管理器供你手动选择符号文件,则加载符号文件。
  • 选择“更改符号设置…”可显示 VS“选项”对话框的“调试”/“符号”页。
  • 选择“查看反汇编”可在新窗口中显示一次反汇编。
  • 若要在未找到源文件或符号文件的情况下始终显示反汇编,请选择“选项”对话框链接,然后选择“启用地址级调试”“源代码不可用时显示反汇编”
    “选项”/“调试”/“常规反汇编”选项

从快捷菜单更改符号选项

当你处于中断模式时,可查找并加载调用堆栈窗口、模块窗口、局部变量窗口、自动窗口和所有监视窗口中显示的项的符号。 在窗口中选择一个项,打开快捷菜单,然后选择下列选项之一:

选项 描述
加载符号 尝试从“选项”对话框的“调试”/“符号”页上指定的位置加载符号。 如果无法找到符号文件,则将启动文件资源管理器,以便你能够指定要搜索的新位置。
符号加载信息 显示已加载符号文件的位置或调试器无法查找文件时已搜索位置的信息。
符号设置… 打开 VS“选项”对话框的“调试”/“符号”页。
始终自动加载 将符号文件添加到由调试器自动加载的文件列表中。

返回页首 内容 | 查找符号 (.pdb) 文件

为符号文件设置编译器选项

当你从 VS IDE 生成项目并使用标准“调试”生成配置时,C++ 和托管编译器将为你的代码创建相应的符号文件。 也可在命令行上设置编译器选项以创建符号文件。

C++ 选项

程序数据库 (.pdb) 文件保存调试和项目状态信息,使用这些信息可以对程序的调试配置进行增量链接。 使用 /ZI 或 /Zi(适用于 C/C++)生成时,将创建 .pdb 文件。

在 Visual C++ 中, /Fd 选项命名由编译器创建的 .pdb 文件。 使用向导在 Visual Studio 中创建项目时,/Fd 选项将设置为创建一个名为 project.pdb 的 .pdb 文件。

如果使用生成文件生成 C/C++ 应用程序,并指定 /ZI/Zi 而不指定 /Fd,则最终将生成两个 .pdb 文件:

  • VCx.pdb,其中 x 表示 Visual C++ 的版本,例如 VC11.pdb。 该文件存储各个 OBJ 文件的所有调试信息并与项目生成文件驻留在同一个目录中。
  • project.pdb   该文件存储 .exe 文件的所有调试信息。 对于 C/C++,它驻留在 \debug 子目录中。

每当创建 OBJ 文件时,C/C++ 编译器都会将调试信息合并到 VCx.pdb 中。 插入的信息包括类型信息,但不包括函数定义等符号信息。 因此,即使每个源文件都包含公共头文件(如 <windows.h>),这些头中的 typedef 也仅存储一次,而不是存储在每个 OBJ 文件中。

链接器将创建 project.pdb,它包含项目的 EXE 文件的调试信息。 project.pdb 文件包含完整的调试信息(包括函数原型),而不仅仅是在 VCx.pdb 中找到的类型信息。 这两个 .pdb 文件都允许增量更新。 链接器还在其创建的 .exe 或 .dll 文件中嵌入 .pdb 文件的路径。

Visual Studio 调试器使用 EXE 或 DLL 文件中的 .pdb 文件的路径查找 project.pdb 文件。 如果调试器在该位置无法找到 .pdb 文件或者路径无效(例如,如果项目已移至另一台计算机),则调试器将搜索包含 EXE 的路径以及在“选项”对话框(“调试”文件夹,“符号”节点)中指定的符号路径。 调试器将不会加载与所调试的可执行文件不匹配的 .pdb 文件。如果调试器无法找到 .pdb 文件,则将显示“查找符号”对话框,这将允许你搜索符号或向搜索路径添加其他位置。

.NET Framework 选项

程序数据库 (.pdb) 文件保存调试和项目状态信息,使用这些信息可以对程序的调试配置进行增量链接。 在使用 /debug 进行生成时,将创建一个 .pdb 文件。 可以使用 /debug:full/debug:pdbonly 生成应用程序。 使用 /debug:full 生成将产生可调试的代码。 使用 /debug:pdbonly 进行生成将生成 .pdb 文件,但是不会生成通知 JIT 编译器调试信息可用的DebuggableAttribute如果你想为不希望其成为可调试的发布版本生成 .pdb 文件,请使用 /debug:pdbonly有关详细信息,请参阅 /debug (C# Compiler Options)/debug (Visual Basic)

Visual Studio 调试器使用 EXE 或 DLL 文件中的 .pdb 文件的路径查找 project.pdb 文件。 如果调试器无法在该位置找到.pdb 文件,或者该路径无效,调试器将先搜索包含 EXE 的路径,然后搜索“选项”对话框中指定的符号路径。 该路径通常是“符号”节点中的“调试”文件夹。 调试器将不会加载与所调试的可执行文件不匹配的 .pdb 文件。 如果调试器无法找到 .pdb 文件,则将显示“查找符号”对话框,这将允许你搜索符号或向搜索路径添加其他位置。

Web 应用程序

一定要把你的应用程序配置文件 (Web.config) 设为调试模式。 调试模式将导致 ASP.NET 为动态生成的文件生成符号,并允许调试器附加到 ASP.NET 应用程序。 如果项目是通过 Web 项目模板创建的,则 VS 会在你开始调试时自动完成此设置。

内容 | 查找符号 (.pdb) 文件

查找源文件

  • 调试器搜索源文件的位置 | 使用“无源”/“未加载任何符号”页查找并加载源文件 | 将源文件搜索路径添加到解决方案 | 使用源服务器

调试器搜索源文件的位置

调试器在下列位置查找源文件:

  1. 在启动调试器的 Visual Studio 实例的 IDE 中打开的文件。
  2. 在 VS 实例中打开的解决方案中的文件。
  3. 在解决方案的属性中的“公共属性”/“调试源文件”页中指定的目录。
  4. 模块的 .pdb 的源信息。 这可能是生成模块时源文件的位置,也可能是源服务器的命令。

返回页首 内容 | 查找源文件

使用“无源”/“未加载任何符号”页查找并加载源文件

当调试器在源文件不可用的位置中断执行时,它将显示“未加载任何源”“未加载任何符号”页,这些页可帮助你查找源文件。 当调试器无法找到可执行文件的符号 (.pdb) 文件来完成搜索时,将显示“未加载任何符号”“无符号”页将提供用于搜索文件的选项。 如果在执行选项之一后找到 .pdb,并且调试器可以使用符号文件中的信息检索源文件,则将显示源。 否则,将显示描述问题的“未加载任何源”页。 此页将显示选项链接,这些链接可执行可以解决问题的操作。

返回页首 内容 | 查找源文件

将源文件搜索路径添加到解决方案

你可指定网络或本地目录来搜索源文件。

  1. 在解决方案资源管理器中选择解决方案,然后从快捷菜单中选择“属性”
  2. “公共属性”节点下,选择“调试源文件”
  3. 单击文件夹 工具/ 选项/ 调试/符号文件夹图标 图标。 可编辑文本将显示在“包含源代码的目录”列表中。
  4. 添加要搜索的路径。

请注意,只搜索指定的目录。 你必须为要搜索的任何子目录添加项。

返回页首 内容 | 查找源文件

使用源服务器

如果本地计算机上没有源代码,或者 .pdb 文件与源代码不匹配,则可使用源服务器来帮助调试应用程序。 源服务器接受文件请求并返回实际的文件。 源服务器通过名为 srcsrv.dll 的 DLL 文件运行。 源服务器读取应用程序的 .pdb 文件,该文件包含指向源代码存储库的指针,以及用于从该存储库检索源代码的命令。 你可以限制允许从应用程序的 .pdb 文件执行的命令,方法是在名为 srcsrv.ini 的文件内列出允许的命令,该文件必须与 srcsrv.dll 和 devenv.exe 位于同一个目录中。

System_CAPS_security 安全性注意
任意命令都可嵌入应用程序的 .pdb 文件中,因此请确保在 srcsrv.ini 文件中仅放入要执行的命令。 任何尝试执行不在 srcsvr.ini 文件中的命令都将导致出现一个确认对话框。 有关详细信息,请参阅 安全警告:调试器必须执行不受信任的命令未对命令参数执行任何验证,因此请慎用受信任的命令。 例如,如果你信任 cmd.exe,恶意用户则可能会指定使该命令变得危险的参数。

启用源服务器的使用

  1. 确保你在编译时采用了上一节中介绍的安全措施。
  2. “工具”菜单上,选择“选项”

    这将显示“选项”对话框。

  3. “调试”节点下,选择“常规”
  4. 选择“启用源服务器支持”复选框。
    启用源服务器选项
  5. (可选)选择所需的子选项。

    请注意,“允许源服务器中的部分信任程序集(仅限托管)”“始终运行不受信任的源服务器命令并且不再提示”都会增大上述安全风险。

动态库和静态库的区别

来源:http://zhidao.baidu.com/link?url=J8CWhysxnBTWm63NM2A_e6L-qHNt24sJrtJj0i1Yh-cqIVfnXqsvyrGvsLzeoRUjhk0MTELkRjTO82r7VUlexli38RoCpkF2WkDWZK_2E0S

我们通常把一些公用函数制作成函数库,供其它程序使用。

函数库分为静态库和动态库两种。

静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。

动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在。

本文主要通过举例来说明在Linux中如何创建静态库和动态库,以及使用它们。

在创建函数库前,我们先来准备举例用的源程序,并将函数库的源程序编译成.o文件。

第1步:编辑得到举例的程序–hello.h、hello.c和main.c;

hello.h(见程序1)为该函数库的头文件。

hello.c(见程序2)是函数库的源程序,其中包含公用函数hello,该函数将在屏幕上输出”Hello XXX!”。

main.c(见程序3)为测试库文件的主程序,在主程序中调用了公用函数hello。

程序1: hello.h

#ifndef HELLO_H

#define HELLO_H

void hello(const char *name);

#endif //HELLO_H

程序2: hello.c

#include

void hello(const char *name)

{

printf(“Hello %s!/n”, name);

}

程序3: main.c

#include “hello.h”

int main()

{

hello(“everyone”);

return 0;

}

第2步:将hello.c编译成.o文件;

无论静态库,还是动态库,都是由.o文件创建的。因此,我们必须将源程序hello.c通过gcc先编译成.o文件。

在系统提示符下键入以下命令得到hello.o文件。

# gcc -c hello.c

#

(注1:本文不介绍各命令使用和其参数功能,若希望详细了解它们,请参考其他文档。)

(注2:首字符”#”是系统提示符,不需要键入,下文相同。)

我们运行ls命令看看是否生存了hello.o文件。

# ls

hello.c hello.h hello.o main.c

#

(注3:首字符不是”#”为系统运行结果,下文相同。)

在ls命令结果中,我们看到了hello.o文件,本步操作完成。

下面我们先来看看如何创建静态库,以及使用它。

第3步:由.o文件创建静态库;

静态库文件名的命名规范是以lib为前缀,紧接着跟静态库名,扩展名为.a。例如:我们将创建的静态库名为myhello,则静态库文件名就是libmyhello.a。在创建和使用静态库时,需要注意这点。创建静态库用ar命令。

在系统提示符下键入以下命令将创建静态库文件libmyhello.a。

# ar cr libmyhello.a hello.o

#

我们同样运行ls命令查看结果:

# ls

hello.c hello.h hello.o libmyhello.a main.c

#

ls命令结果中有libmyhello.a。

第4步:在程序中使用静态库;

静态库制作完了,如何使用它内部的函数呢?只需要在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后在用gcc命令生成目标文件时指明静态库名,gcc将会从静态库中将公用函数连接到目标文件中。注意,gcc会在静态库名前加上前缀lib,然后追加扩展名.a得到的静态库文件名来查找静态库文件。

在程序3:main.c中,我们包含了静态库的头文件hello.h,然后在主程序main中直接调用公用函数hello。下面先生成目标程序hello,然后运行hello程序看看结果如何。

# gcc -o hello main.c -L. -lmyhello

# ./hello

Hello everyone!

#

我们删除静态库文件试试公用函数hello是否真的连接到目标文件 hello中了。

# rm libmyhello.a

rm: remove regular file `libmyhello.a’? y

# ./hello

Hello everyone!

#

程序照常运行,静态库中的公用函数已经连接到目标文件中了。

我们继续看看如何在Linux中创建动态库。我们还是从.o文件开始。

第5步:由.o文件创建动态库文件;

动态库文件名命名规范和静态库文件名命名规范类似,也是在动态库名增加前缀lib,但其文件扩展名为.so。例如:我们将创建的动态库名为myhello,则动态库文件名就是libmyhello.so。用gcc来创建动态库。

在系统提示符下键入以下命令得到动态库文件libmyhello.so。

# gcc -shared -fPCI -o libmyhello.so hello.o

#

我们照样使用ls命令看看动态库文件是否生成。

# ls

hello.c hello.h hello.o libmyhello.so main.c

#

第6步:在程序中使用动态库;

在程序中使用动态库和使用静态库完全一样,也是在使用到这些公用函数的源程序中包含这些公用函数的原型声明,然后在用gcc命令生成目标文件时指明动态库名进行编译。我们先运行gcc命令生成目标文件,再运行它看看结果。

# gcc -o hello main.c -L. -lmyhello

# ./hello

./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory

#

哦!出错了。快看看错误提示,原来是找不到动态库文件libmyhello.so。程序在运行时,会在/usr/lib和/lib等目录中查找需要的动态库文件。若找到,则载入动态库,否则将提示类似上述错误而终止程序运行。我们将文件 libmyhello.so复制到目录/usr/lib中,再试试。

# mv libmyhello.so /usr/lib

# ./hello

Hello everyone!

#

成功了。这也进一步说明了动态库在程序运行时是需要的。

我们回过头看看,发现使用静态库和使用动态库编译成目标程序使用的gcc命令完全一样,那当静态库和动态库同名时,gcc命令会使用哪个库文件呢?抱着对问题必究到底的心情,来试试看。

先删除 除.c和.h外的 所有文件,恢复成我们刚刚编辑完举例程序状态。

# rm -f hello hello.o /usr/lib/libmyhello.so

# ls

hello.c hello.h main.c

#

在来创建静态库文件libmyhello.a和动态库文件libmyhello.so。

# gcc -c hello.c

# ar cr libmyhello.a hello.o

# gcc -shared -fPCI -o libmyhello.so hello.o

# ls

hello.c hello.h hello.o libmyhello.a libmyhello.so main.c

#

通过上述最后一条ls命令,可以发现静态库文件libmyhello.a和动态库文件libmyhello.so都已经生成,并都在当前目录中。然后,我们运行gcc命令来使用函数库myhello生成目标文件hello,并运行程序 hello。

# gcc -o hello main.c -L. -lmyhello

# ./hello

./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory

#

从程序hello运行的结果中很容易知道,当静态库和动态库同名时, gcc命令将优先使用动态库。

doxygen 使用简介(C,C++为代码作注释)

来自:http://www.cnblogs.com/wishma/archive/2008/07/24/1250339.html

doxygen注释块

doxygen注释块其实就是在C”C++注释块的基础添加一些额外标识, 使doxygen把它识别出来, 并将它组织到生成的文档中去。

在每个代码项中都可以有两类描述, 这两类描述将在文档中格式化在一起: 一种就是brief描述, 另一种就是detailed。 两种都是可选的,但不能同时没有。

顾名思义, 简述(brief)就是在一行内简述地描述。而详细描述(detailed description)则提供更长, 更详细的文档。

在doxygen中, 有多种方法将注释块标识成详细描述:

  1. 你可以使用JavaDoc风格,即在C风格注释块开始使用两个星号’*’, 就像这样:
  2. /**
  3.  * … 描述 …
  4.  */
  5. 或者你使用Qt风格代码注释,即在C风格注释块开始处添加一个叹号’!’:
  6. /*!
  7.  * … 描述 …
  8.  */

注释块中间的星号是可选, 所以将注释块写成:

/*!

… 描述 …

*/

同样也是有效的.

  1. 第三种方法就是使用连续两个以上C++注释行所组成的注释块,而每个注释行开始处要多写一个斜杠或写一个叹号。像下行这样:
  2. ///
  3. /// … 描述 …
  4. ///

或者

//!

//!… 描述 …

//!

  1. 一些人喜欢使他们的注释块在文档中显得更为显目,所以你也可以这么做:
  2. /////////////////////////////////////////////////
  3. /// … 描述 …
  4. /////////////////////////////////////////////////

简述同样也可以同种写法:

  1. 一种是在一个doxygen注释块中使用“brief . 这个命令只对当前一个文字段有效, 所以详细描述应该与之间隔一个空行.

像这样:

/*! “brief 这里是简要描述.

*         这里仍然是简要描述.

*

* 详细描述从这里开始.

*/

  1. 如果在doxygen配置文件将JAVADOC_AUTOBRIEF设置成YES。则JavaDoc风格将注释块中的第一个句子视为简要描述, 这个句子可以以句号’.’、空格或者空行来结束:
  2. /** 这里是简述, 并由句号结束. 详细描述从这里
  3.  * 开始.
  4.  */

这个功能同样多行C++行注释段中有效:

/// 这里是简述, 并由句号结束. 详细描述从这里

/// 开始..

  1. 第三种方法就是详细描述注释段紧跟在一行C++注释段后面,至多隔一个空行, 如下面两个例子:
  2. /// 简述.
  3. /** 详细描述. */

或者

//! 简述.

 

//! 详细描述

//! 从这里开始.

注意最后一个例子, 例子那个空行是用来分隔简述注释段和详细描述段的, 不能去掉.而且JAVADOC_AUTOBRIEF不应该被设置成YES.

看上去doxygen是相当富有弹性, 但下面的例子是非法的:

//! 这个简述因为是多行的

//! 会被认为是详细描述..

/*! 而这里则是另外的详细描述.

*/

因为doxygen只允许一个简要和一个详细描述。

此外, 如果在一个代码项的声明之前有简要描述而且在其定义之前也有简要描述, 那么doxygen只使用声明之前的描述。而如果详细描述出现了同样的情况, doxygen则会使用定义之前的描述, 另外的描述会被忽略掉。

这里有一个使用Qt风格的C++代码:

//! A test class.

/*!

A more elaborate class description.

*/

 

class Test

{

public:

 

//! An enum.

/*! More detailed enum description. */

enum TEnum {

TVal1, /*!< Enum value TVal1. */

TVal2, /*!< Enum value TVal2. */

TVal3 /*!< Enum value TVal3. */

}

//! Enum pointer.

/*! Details. */

*enumPtr,

//! Enum variable.

/*! Details. */

enumVar;

 

//! A constructor.

/*!

A more elaborate description of the constructor.

*/

Test();

 

//! A destructor.

/*!

A more elaborate description of the destructor.

*/

~Test();

 

//! A normal member taking two arguments and returning an integer value.

/*!

“param a an integer argument.

“param s a constant character pointer.

“return The test results

“sa Test(), ~Test(), testMeToo() and publicVar()

*/

int testMe(int a,const char *s);

 

//! A pure virtual member.

/*!

“sa testMe()

“param c1 the first argument.

“param c2 the second argument.

*/

virtual void testMeToo(char c1,char c2) = 0;

 

//! A public variable.

/*!

Details.

*/

int publicVar;

 

//! A function variable.

/*!

Details.

*/

int (*handler)(int a,int b);

};

 

点击这里查看doxygen产生的HTML文档.

只有一行的注释段包含的是简述, 而多行的注释段则包含的是详细描述.

简要描述可以被用来进行class、namespace或者文件的成员描述, 被以较小的斜体字型显示出来。(这种描述可以通过将BRIEF_MEMBER_DESC设置成NO来隐藏。)默认情况下,简要描述会被显示成详细描述的第一个句子。(这同样可以通过将REPEAT_BRIEF设置成NO来屏蔽。)在Qt风格中, 两种描述都是可选的。

默认情况下, JavaDoc风格的注释段和Qt风格的注释段的行为是相同的。但这样, 就不符合JavaDoc标准, 注释段的第一个句应该自动识别为简要描述。你应该设置JAVADOC_AUTOBRIEFyly YES, 来启用这个功能。如果你设置了这个选项并想在句子中间放置一个句号, 你应该在后面添加一个反斜杠和一个空格. 就像下面的例子一样:

/** Brief description (e.g.” using only a few words). Details follow. */

Here is the same piece of code as shown above, this time documented using the JavaDoc style and JAVADOC_AUTOBRIEF set to YES:

/**

* A test class. A more elaborate class description.

*/

 

class Test

{

public:

 

/**

* An enum.

* More detailed enum description.

*/

 

enum TEnum {

TVal1, /**< enum value TVal1. */

TVal2, /**< enum value TVal2. */

TVal3 /**< enum value TVal3. */

}

*enumPtr, /**< enum pointer. Details. */

enumVar; /**< enum variable. Details. */

 

/**

* A constructor.

* A more elaborate description of the constructor.

*/

Test();

 

/**

* A destructor.

* A more elaborate description of the destructor.

*/

~Test();

 

/**

* a normal member taking two arguments and returning an integer value.

* @param a an integer argument.

* @param s a constant character pointer.

* @see Test()

* @see ~Test()

* @see testMeToo()

* @see publicVar()

* @return The test results

*/

int testMe(int a,const char *s);

 

/**

* A pure virtual member.

* @see testMe()

* @param c1 the first argument.

* @param c2 the second argument.

*/

virtual void testMeToo(char c1,char c2) = 0;

 

/**

* a public variable.

* Details.

*/

int publicVar;

 

/**

* a function variable.

* Details.

*/

int (*handler)(int a,int b);

};

 

点击这里查看doxygen产生的HTML文档.

不像其它的文档系统 , doxygen允许在代码项的定义之前面放置成员(包括全局函数)的注释。这种方法可以在源文件中进行文档注释工作。这就是使得头文件可以比较简洁, 使代码的实现人员更容易阅读。比较折中的方案就是在声明的地方添加简要描述, 在实现的地方添加详细描述。

在成员后面放置文档

如果你想对文件、结构体、联合体、类或者枚举的成员进行文档注释的话, 并且要在成员中间添加注释, 而这些注释往往都是在每个成员后面。为此, 你可以使用在注释段中使用'<‘标识

Here are some examples:

int var; /*!< Detailed description after the member */

This block can be used to put a Qt style detailed documentation block after a member. Other ways to do the same are:

int var; /**< Detailed description after the member */

或者

int var; //!< Detailed description after the member

//!

或者

int var; ///< Detailed description after the member

///

Most often one only wants to put a brief description after a member. This is done as follows:

int var; //!< Brief description after the member

或者

int var; ///< Brief description after the member

Note that these blocks have the same structure and meaning as the special comment blocks in the previous section only the < indicates that the member is located in front of the block instead of after the block.

Here is an example of the use of these comment blocks:

/*! A test class */

 

class Test

{

public:

/** An enum type.

* The documentation block cannot be put after the enum!

*/

enum EnumType

{

int EVal1,     /**< enum value 1 */

int EVal2      /**< enum value 2 */

};

void member();   //!< a member function.

 

protected:

int value;       /*!< an integer value */

};

点击这里查看doxygen产生的HTML文档.

警告:

这种注释方法只能在成员参数中使用。它们不能用在描述文件、类、联合体、名字空间和枚举本身。此外, 在下一节提到的结构化命令(如”class)在这种注释段中是无效的。

在其它地方进行注释

虽然我们一般是在声明和定义的前面添加文档注释块, 但也许出于某种情况需要将文档放在其它什么位置上。比如我们需要编写一段实际上不存在的文件的描述。因此, Doxygen允许将文档放置在任何位置。(除了在函数体内或在一个普通的注释段)

为了达到这种目的, 你唯一要做的就是在注释段中使用结构化命令。

结构化命令(就像其它命令)由一个反斜杠(“), 或 JavaDoc风格中使用的@来做开头, 后面紧跟着命令名字和一些参数。例如, 你想注释一个名为Test的类,如下例:

/*! “class Test

“brief A test class.

 

A more detailed class description.

*/

命令”class被用来指示代码段中包含着关于Test类的文档.其它的结构化命令有:

  • “struct生成C结构的文档.
  • “union生成联合体的文档.
  • “enum生成枚举的文档.
  • “fn生成函数的文档.
  • “var生成变量或typedef或枚举值的文档.
  • “def生成#define定义的文档.
  • “file生成关于一个文件的文档.
  • “namespace生成名字空间的文档.
  • “package生成Java包的文档.
  • “interface生成IDL接口的文档.

参看特殊命令得到更多关于这些命令或其它命令的详细信息.

对C++类的成员进行文档注释时, 必须对类本身进行文档注释。这同样对名字空间有效。要对全局函数、typedef、枚举或宏定义进行文档注释, 则必须先对包含它们的文件进行文档(这通常是头文件).

再重复一下, 因为它经常被忘记: 如果要对全局项目(如:函数、typedefs、枚举或宏等), 你必须必须对定义它们的文件进行文档注释。换一句话, 你至少在文件写下

/*! “file */

/** @file */

这样的注释.

下面是一个名为structcmd.h头文件的例子, 里面包含了使用结构化命令的例子:

/*! “file structcmd.h

“brief A Documented file.

 

Details.

*/

 

/*! “def MAX(a,b)

“brief A macro that returns the maximum of “a a and “a b.

 

Details.

*/

 

/*! “var typedef unsigned int UINT32

“brief A type definition for a .

 

Details.

*/

 

/*! “var int errno

“brief Contains the last error code.

 

“warning Not thread safe!

*/

 

/*! “fn int open(const char *pathname,int flags)

“brief Opens a file descriptor.

 

“param pathname The name of the descriptor.

“param flags Opening flags.

*/

 

/*! “fn int close(int fd)

“brief Closes the file descriptor “a fd.

“param fd The descriptor to close.

*/

 

/*! “fn size_t write(int fd,const char *buf, size_t count)

“brief Writes “a count bytes from “a buf to the filedescriptor “a fd.

“param fd The descriptor to write to.

“param buf The data buffer to write.

“param count The number of bytes to write.

*/

 

/*! “fn int read(int fd,char *buf,size_t count)

“brief Read bytes from a file descriptor.

“param fd The descriptor to read from.

“param buf The buffer to read into.

“param count The number of bytes to read.

*/

 

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

typedef unsigned int UINT32;

int errno;

int open(const char *,int);

int close(int);

size_t write(int,const char *, size_t);

int read(int,char *,size_t);

点击这里查看doxygen产生的HTML文档.

因为例子中的每个注释都包含了结构化命令, 所以所有的注释块都可以移动到其它的地方, 而不会对生成的文档产生影响。这种方法的坏处是当原型发生了改变 , 你不得不改变结构化命令的参数。因此, 在使用它们之前, 应该认真考虑一下是否真正需要使用它们。

 

Doxygen – Generate documentation from source code

Refer to http://www.stack.nl/~dimitri/doxygen/

Doxygen is the de facto standard tool for generating documentation from annotated C++ sources, but it also supports other popular programming languages such as C, Objective-C, C#, PHP, Java, Python, IDL (Corba, Microsoft, and UNO/OpenOffice flavors), Fortran, VHDL, Tcl, and to some extent D.

Doxygen can help you in three ways:

  1. It can generate an on-line documentation browser (in HTML) and/or an off-line reference manual (in $\mbox{\LaTeX}$) from a set of documented source files. There is also support for generating output in RTF (MS-Word), PostScript, hyperlinked PDF, compressed HTML, and Unix man pages. The documentation is extracted directly from the sources, which makes it much easier to keep the documentation consistent with the source code.
  2. You can configure doxygen to extract the code structure from undocumented source files. This is very useful to quickly find your way in large source distributions. Doxygen can also visualize the relations between the various elements by means of include dependency graphs, inheritance diagrams, and collaboration diagrams, which are all generated automatically.
  3. You can also use doxygen for creating normal documentation (as I did for the doxygen user manual and web-site).

Doxygen is developed under Mac OS X and Linux, but is set-up to be highly portable. As a result, it runs on most other Unix flavors as well. Furthermore, executables for Windows are available.