C中的预编译宏定义

2023-11-07

 

C中的预编译宏定义

作者: infobillows   发表日期: 2007-09-15 21:34  点击数: 1507


在将一个C源程序转换为可执行程序的过程中, 编译预处理是最初的步骤. 这一步骤是由预处理器(preprocessor)来完成的. 在源流程序被编译器处理之前, 预处理器首先对源程序中的"宏(macro)"进行处理.

C初学者可能对预处理器没什么概念, 这是情有可原的: 一般的C编译器都将预处理, 汇编, 编译, 连接过程集成到一起了. 编译预处理往往在后台运行. 在有的C编译器中, 这些过程统统由一个单独的程序来完成, 编译的不同阶段实现这些不同的功能. 可以指定相应的命令选项来执行这些功能. 有的C编译器使用分别的程序来完成这些步骤. 可单独调用这些程序来完成. 在gcc中, 进行编译预处理的程序被称为CPP, 它的可执行文件名为cpp.

编译预处理命令的语法与C语言的语法是完全独立的. 比如: 你可以将一个宏扩展为与C语法格格不入的内容, 但该内容与后面的语句结合在一个若能生成合法的C语句, 也是可以正确编译的.


(一) 预处理命令简介

预处理命令由#(hash字符)开头, 它独占一行, #之前只能是空白符. 以#开头的语句就是预处理命令, 不以#开头的语句为C中的代码行. 常用的预处理命令如下:

#define              定义一个预处理宏
#undef               取消宏的定义

#include            包含文件命令
#include_next   与#include相似, 但它有着特殊的用途

#if                      编译预处理中的条件命令, 相当于C语法中的if语句
#ifdef                判断某个宏是否被定义, 若已定义, 执行随后的语句
#ifndef             与#ifdef相反, 判断某个宏是否未被定义
#elif                  若#if, #ifdef, #ifndef或前面的#elif条件不满足, 则执行#elif之后的语句, 相当于C语法中的else-if
#else                与#if, #ifdef, #ifndef对应, 若这些条件不满足, 则执行#else之后的语句, 相当于C语法中的else
#endif              #if, #ifdef, #ifndef这些条件命令的结束标志.
defined            与#if, #elif配合使用, 判断某个宏是否被定义

#line                标志该语句所在的行号
#                      将宏参数替代为以参数值为内容的字符窜常量
##                   将两个相邻的标记(token)连接为一个单独的标记
#pragma        说明编译器信息

#warning       显示编译警告信息
#error            显示编译错误信息


(二) 预处理的文法

预处理并不分析整个源代码文件, 它只是将源代码分割成一些标记(token), 识别语句中哪些是C语句, 哪些是预处理语句. 预处理器能够识别C标记, 文件名, 空白符, 文件结尾标志.

预处理语句格式:    #command name(...) token(s)

1, command预处理命令的名称, 它之前以#开头, #之后紧随预处理命令, 标准C允许#两边可以有空白符, 但比较老的编译器可能不允许这样. 若某行中只包含#(以及空白符), 那么在标准C中该行被理解为空白. 整个预处理语句之后只能有空白符或者注释, 不能有其它内容.
2, name代表宏名称, 它可带参数. 参数可以是可变参数列表(C99).
3, 语句中可以利用"/"来换行.

e.g.
#  define  ONE 1 /* ONE == 1 */
等价于: #define ONE 1

#define err(flag, msg) if(flag) /
    printf(msg)
等价于: #define err(flag, msg) if(flag) printf(msg)


(三) 预处理命令详述

1, #define
#define命令定义一个宏:
#define MACRO_NAME(args) tokens(opt)
之后出现的MACRO_NAME将被替代为所定义的标记(tokens). 宏可带参数, 而后面的标记也是可选的.

对象宏
不带参数的宏被称为"对象宏(objectlike macro)"

#define经常用来定义常量, 此时的宏名称一般为大写的字符串. 这样利于修改这些常量.
e.g.
#define MAX 100
int a[MAX];

#ifndef __FILE_H__
#define __FILE_H__
#include "file.h"
#endif
#define __FILE_H__ 中的宏就不带任何参数, 也不扩展为任何标记. 这经常用于包含头文件.

要调用该宏, 只需在代码中指定宏名称, 该宏将被替代为它被定义的内容.

函数宏
带参数的宏也被称为"函数宏". 利用宏可以提高代码的运行效率: 子程序的调用需要压栈出栈, 这一过程如果过于频繁会耗费掉大量的CPU运算资源. 所以一些代码量小但运行频繁的代码如果采用带参数宏来实现会提高代码的运行效率.

函数宏的参数是固定的情况

函数宏的定义采用这样的方式: #define name( args ) tokens
其中的args和tokens都是可选的. 它和对象宏定义上的区别在于宏名称之后不带括号.

注意, name之后的左括号(必须紧跟name, 之间不能有空格, 否则这就定义了一个对象宏, 它将被替换为 以(开始的字符串. 但在调用函数宏时, name与(之间可以有空格.

e.g.
#define mul(x,y) ((x)*(y))

注意, 函数宏之后的参数要用括号括起来, 看看这个例子:
e.g.
#define mul(x,y) x*y
"mul(1, 2+2);" 将被扩展为: 1*2 + 2
同样, 整个标记串也应该用括号引用起来:
e.g.
#define mul(x,y) (x)*(y)
sizeof mul(1,2.0) 将被扩展为 sizeof 1 * 2.0

调用函数宏时候, 传递给它的参数可以是函数的返回值, 也可以是任何有意义的语句:
e.g.
mul (f(a,b), g(c,d));

e.g.
#define insert(stmt) stmt
insert ( a=1; b=2;)  相当于在代码中加入 a=1; b=2 .
insert ( a=1, b=2;)  就有问题了: 预处理器会提示出错: 函数宏的参数个数不匹配. 预处理器把","视为参数间的分隔符.  
insert ((a=1, b=2;)) 可解决上述问题.

在定义和调用函数宏时候, 要注意一些问题:
1, 我们经常用{}来引用函数宏被定义的内容, 这就要注意调用这个函数宏时的";"问题.
example_3.7:
#define swap(x,y) { unsigned long _temp=x; x=y; y=_tmp}
如果这样调用它: "swap(1,2);" 将被扩展为: { unsigned long _temp=1; 1=2; 2=_tmp};
明显后面的;是多余的, 我们应该这样调用: swap(1,2)
虽然这样的调用是正确的, 但它和C语法相悖, 可采用下面的方法来处理被{}括起来的内容:

#define swap(x,y) /
    do { unsigned long _temp=x; x=y; y=_tmp} while (0)
swap(1,2); 将被替换为:
do { unsigned long _temp=1; 1=2; 2=_tmp} while (0);
在Linux内核源代码中对这种do-while(0)语句有这广泛的应用.

2, 有的函数宏是无法用do-while(0)来实现的, 所以在调用时不能带上";", 最好在调用后添加注释说明.
eg_3.8:
#define incr(v, low, high) /
    for ((v) = (low),; (v) <= (high); (v)++)
只能以这样的形式被调用: incr(a, 1, 10)  /* increase a form 1 to 10 */

函数宏中的参数包括可变参数列表的情况
C99标准中新增了可变参数列表的内容. 不光是函数, 函数宏中也可以使用可变参数列表.

#define name(args, ...) tokens
#define name(...) tokens
"..."代表可变参数列表, 如果它不是仅有的参数, 那么它只能出现在参数列表的最后. 调用这样的函数宏时, 传递给它的参数个数要不少于参数列表中参数的个数(多余的参数被丢弃).
通过__VA_ARGS__来替换函数宏中的可变参数列表. 注意__VA_ARGS__只能用于函数宏中参数中包含有"..."的情况.

e.g.
#ifdef DEBUG
#define my_printf(...) fprintf(stderr, __VA_ARGS__)
#else
#define my_printf(...) printf(__VA_ARGS__)
#endif

tokens中的__VA_ARGS__被替换为函数宏定义中的"..."可变参数列表.

注意在使用#define时候的一些常见错误:
#define MAX = 100
#define MAX 100;
=, ; 的使用要值得注意. 再就是调用函数宏是要注意, 不要多给出";".

注意: 函数宏对参数类型是不敏感的, 你不必考虑将何种数据类型传递给宏. 那么, 如何构建对参数类型敏感的宏呢? 参考本章的第九部分, 关于"##"的介绍.

关于定义宏的另外一些问题
(1) 宏可以被多次定义, 前提是这些定义必须是相同的. 这里的"相同"要求先后定义中空白符出现的位置相同, 但具体的空白符类型或数量可不同, 比如原先的空格可替换为多个其他类型的空白符: 可为tab, 注释...
e.g.
#define NULL 0
#define NULL /* null pointer */     0
上面的重定义是相同的, 但下面的重定义不同:
#define fun(x) x+1
#define fun(x) x + 1 或: #define fun(y) y+1
如果多次定义时, 再次定义的宏内容是不同的, gcc会给出"NAME redefined"警告信息.

应该避免重新定义函数宏, 不管是在预处理命令中还是C语句中, 最好对某个对象只有单一的定义. 在gcc中, 若宏出现了重定义, gcc会给出警告.

(2) 在gcc中, 可在命令行中指定对象宏的定义:
e.g.
$ gcc -Wall -DMAX=100 -o tmp tmp.c
相当于在tmp.c中添加" #define MAX 100".

那么, 如果原先tmp.c中含有MAX宏的定义, 那么再在gcc调用命令中使用-DMAX, 会出现什么情况呢?
---若-DMAX=1, 则正确编译.
---若-DMAX的值被指定为不为1的值, 那么gcc会给出MAX宏被重定义的警告, MAX的值仍为1.

注意: 若在调用gcc的命令行中不显示地给出对象宏的值, 那么gcc赋予该宏默认值(1), 如: -DVAL == -DVAL=1

(3) #define所定义的宏的作用域
宏在定义之后才生效, 若宏定义被#undef取消, 则#undef之后该宏无效. 并且字符串中的宏不会被识别
e.g.
#define ONE 1
sum = ONE + TWO    /* sum = 1 + TWO  */
#define TWO 2
sum = ONE + TWO    /* sum = 1 + 2    */  
#undef ONE
sum = ONE + TWO    /* sum = ONE + 2  */

char c[] = "TWO"   /* c[] = "TWO", NOT "2"! */

(4) 宏的替换可以是递归的, 所以可以嵌套定义宏.
e.g.
# define ONE NUMBER_1
# define NUMBER_1 1
int a = ONE  /* a = 1 */

2, #undef
#undef用来取消宏定义, 它与#define对立:
#undef name
如够被取消的宏实际上没有被#define所定义, 针对它的#undef并不会产生错误.
当一个宏定义被取消后, 可以再度定义它.

3, #if, #elif, #else, #endif
#if, #elif, #else, #endif用于条件编译:

#if 常量表达式1
    语句...
#elif 常量表达式2
    语句...
#elif 常量表达式3
    语句...
...
#else
    语句...
#endif

#if和#else分别相当于C语句中的if, else. 它们根据常量表达式的值来判别是否执行后面的语句. #elif相当于C中的else-if. 使用这些条件编译命令可以方便地实现对源代码内容的控制.
else之后不带常量表达式, 但若包含了常量表达式, gcc只是给出警告信息.

使用它们可以提升代码的可移植性---针对不同的平台使用执行不同的语句. 也经常用于大段代码注释.
e.g.
#if 0
{
    一大段代码;
}
#endif

常量表达式可以是包含宏, 算术运算, 逻辑运算等等的合法C常量表达式, 如果常量表达式为一个未定义的宏, 那么它的值被视为0.
#if MACRO_NON_DEFINED  == #if 0
在判断某个宏是否被定义时, 应当避免使用#if, 因为该宏的值可能就是被定义为0. 而应当使用下面介绍的#ifdef或#ifndef.

注意: #if, #elif, #else之后的宏只能是对象宏. 如果name为名的宏未定义, 或者该宏是函数宏. 那么在gcc中使用"-Wundef"选项会显示宏未定义的警告信息.

4, #ifdef, #ifndef, defined.
#ifdef, #ifndef, defined用来测试某个宏是否被定义
#ifdef name  或 #ifndef name

它们经常用于避免头文件的重复引用:
#ifndef __FILE_H__
#define __FILE_H__
#include "file.h"
#endif

defined(name): 若宏被定义,则返回1, 否则返回0.
它与#if, #elif, #else结合使用来判断宏是否被定义, 乍一看好像它显得多余, 因为已经有了#ifdef和#ifndef. defined用于在一条判断语句中声明多个判别条件:

#if defined(VAX) && defined(UNIX) && !defined(DEBUG)

和#if, #elif, #else不同, #indef, #ifndef, defined测试的宏可以是对象宏, 也可以是函数宏. 在gcc中使用"-Wundef"选项不会显示宏未定义的警告信息.

5, #include , #include_next
#include用于文件包含. 在#include 命令所在的行不能含有除注释和空白符之外的其他任何内容.
#include "headfile"
#include <headfile>
#include 预处理标记
前面两种形式大家都很熟悉, "#include 预处理标记"中, 预处理标记会被预处理器进行替换, 替换的结果必须符合前两种形式中的某一种.

实际上, 真正被添加的头文件并不一定就是#include中所指定的文件. #include"headfile"包含的头文件当然是同一个文件, 但#include <headfile>包包含的"系统头文件"可能是另外的文件. 但这不值得被注意. 感兴趣的话可以查看宏扩展后到底引入了哪些系统头文件.

关于#include "headfile"和#include <headfile>的区别以及如何在gcc中包含头文件的详细信息, 参考本blog的GCC笔记.

相对于#include, 我们对#include_next不太熟悉. #include_next仅用于特殊的场合. 它被用于头文件中(#include既可用于头文件中, 又可用于.c文件中)来包含其他的头文件. 而且包含头文件的路径比较特殊: 从当前头文件所在目录之后的目录来搜索头文件.
比如: 头文件的搜索路径一次为A,B,C,D,E. #include_next所在的当前头文件位于B目录, 那么#include_next使得预处理器从C,D,E目录来搜索#include_next所指定的头文件.

可参考
cpp手册 进一步了解#include_next

6, 预定义宏
标准C中定义了一些对象宏, 这些宏的名称以"__"开头和结尾, 并且都是大写字符. 这些预定义宏可以被#undef, 也可以被重定义.

下面列出一些标准C中常见的预定义对象宏(其中也包含gcc自己定义的一些预定义宏:
__LINE__             当前语句所在的行号, 以10进制整数标注.
__FILE__             当前源文件的文件名, 以字符串常量标注.
__DATE__            程序被编译的日期, 以"Mmm dd yyyy"格式的字符串标注.
__TIME__            程序被编译的时间, 以"hh:mm:ss"格式的字符串标注, 该时间由asctime返回.

__STDC__            如果当前编译器符合ISO标准, 那么该宏的值为1
__STDC_VERSION__    如果当前编译器符合C89, 那么它被定义为199409L, 如果符合C99, 那么被定义为199901L.
                    我用gcc, 如果不指定-std=c99, 其他情况都给出__STDC_VERSION__未定义的错误信息, 咋回事呢?
__STDC_HOSTED__        如果当前系统是"本地系统(hosted)", 那么它被定义为1. 本地系统表示当前系统拥有完整的标准C库.


gcc定义的预定义宏:
__OPTMIZE__            如果编译过程中使用了优化, 那么该宏被定义为1.
__OPTMIZE_SIZE__    同上, 但仅在优化是针对代码大小而非速度时才被定义为1.
__VERSION__            显示所用gcc的版本号.
可参考"GCC the complete reference".
要想看到gcc所定义的所有预定义宏, 可以运行: $ cpp -dM /dev/null

7, #line
#line用来修改__LINE__和__FILE__.
e.g.
  printf("line: %d, file: %s/n", __LINE__, __FILE__);
#line 100 "haha"
  printf("line: %d, file: %s/n", __LINE__, __FILE__);
  printf("line: %d, file: %s/n", __LINE__, __FILE__);

显示:
line: 34, file: 1.c
line: 100, file: haha
line: 101, file: haha

8, #pragma, _Pragma
#pragma用编译器用来添加新的预处理功能或者显示一些编译信息. #pragma的格式是各编译器特定的, gcc的如下:
#pragma GCC name token(s)

#pragma之后有两个部分: GCC和特定的pragma name. 下面分别介绍gcc中常用的.

(1) #pragma GCC dependency
dependency测试当前文件(既该语句所在的程序代码)与指定文件(既#pragma语句最后列出的文件)的时间戳. 如果指定文件比当前文件新, 则给出警告信息.
e.g.
在demo.c中给出这样一句:
#pragma GCC dependency "temp-file"
然后在demo.c所在的目录新建一个更新的文件: $ touch temp-file, 编译: $ gcc demo.c 会给出这样的警告信息:  warning: current file is older than temp-file
如果当前文件比指定的文件新, 则不给出任何警告信息.

还可以在在#pragma中给添加自定义的警告信息.
e.g.
#pragma GCC dependency "temp-file" "demo.c needs to be updated!"
1.c:27:38: warning: extra tokens at end of #pragma directive

1.c:27:38: warning: current file is older than temp-file
注意: 后面新增的警告信息要用""引用起来, 否则gcc将给出警告信息.

(2) #pragma GCC poison token(s)
若源代码中出现了#pragma中给出的token(s), 则编译时显示警告信息. 它一般用于在调用你不想使用的函数时候给出出错信息.
e.g.
#pragma GCC poison scanf
scanf("%d", &a);
warning: extra tokens at end of #pragma directive
error: attempt to use poisoned "scanf"
注意, 如果调用了poison中给出的标记, 那么编译器会给出的是出错信息. 关于第一条警告, 我还不知道怎么避免, 用""将token(s)引用起来也不行.

(3) #pragma GCC system_header
从#pragma GCC system_header直到文件结束之间的代码会被编译器视为系统头文件之中的代码. 系统头文件中的代码往往不能完全遵循C标准, 所以头文件之中的警告信息往往不显示. (除非用 #warning显式指明).
(这条#pragma语句还没发现用什么大的用处)

由于#pragma不能用于宏扩展, 所以gcc还提供了_Pragma:
e.g.
#define PRAGMA_DEP #pragma GCC dependency "temp-file"
由于预处理之进行一次宏扩展, 采用上面的方法会在编译时引发错误, 要将#pragma语句定义成一个宏扩展, 应该使用下面的_Pragma语句:
#define PRAGMA_DEP _Pragma("GCC dependency /"temp-file/"")
注意, ()中包含的""引用之前引该加上/转义字符.

9, #, ##
#和##用于对字符串的预处理操作, 所以他们也经常用于printf, puts之类的字符串显示函数中.
#用于在宏扩展之后将tokens转换为以tokens为内容的字符串常量.
e.g.
#define TEST(a,b) printf( #a "<" #b "=%d/n", (a)<(b));
注意: #只针对紧随其后的token有效!
##用于将它前后的两个token组合在一起转换成以这两个token为内容的字符串常量. 注意##前后必须要有token.
e.g.
#define TYPE(type, n) type n

之后调用: 
TYPE(int, a) = 1;
TYPE(long, b) = 1999;
将被替换为:
int a = 1;
long b = 1999;

(10) #warning, #error
#warning, #error分别用于在编译时显示警告和错误信息, 格式如下:
#warning tokens
#error tokens
e.g.
#warning "some warning"
注意, #error和#warning后的token要用""引用起来!
(在gcc中, 如果给出了warning, 编译继续进行, 但若给出了error, 则编译停止. 若在命令行中指定了 -Werror, 即使只有警告信息, 也不编译.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C中的预编译宏定义 的相关文章

  • Linux 使用 boost asio 拒绝套接字绑定权限

    我在绑定套接字时遇到问题 并且以用户身份运行程序时权限被拒绝 这行代码会产生错误 acceptor new boost asio ip tcp acceptor io boost asio ip tcp endpoint boost asi
  • 如何启动异步任务对象

    我想开始收集Task同时处理对象并等待所有对象完成 下面的代码显示了我想要的行为 public class Program class TaskTest private Task createPauseTask int ms works w
  • 使用预编译头减少 clang 编译时间

    我正在开发一个数据库项目 该项目将查询 以某种高级语言表示 编译为 C 代码 这段代码由数据库编译并执行 那部分工作得很好 现在 我正在尝试减少 C 查询代码的编译时间 我想知道是否可以使用预编译头来提高性能 该查询被转换为一个名为 Que
  • Confuser .NET 混淆器。安全吗? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我目前正在开发一个应用程序 其中阻止用户反编译代码非常重要 现在 我意识到 如果由经验丰富的程序员执行 大多数 exe 都是可反编译的
  • C++:将模板参数的模板类型成员添加为好友的正确语法?

    我有一个带有模板类型参数 tTRAIT 的类 我想加一个模板为好友type member aliastTRAIT 但我无法弄清楚语法 这可能吗 template
  • 在不使用 ncurses 的情况下用 C/C++ 编写“真正的”交互式终端程序,例如 vim、htop...

    不 我不想使用ncurses 因为我想了解如何 终端可以工作 并且我自己编程也很有趣 没有 必须是可移植的 它必须只能在基于 linux xterm 的终端仿真器上工作 我想做的是编写一个交互式终端应用程序 例如 htop 和 vim 我的
  • 在 C++ 中使用表达式模板进行符号微分

    如何在 C 中使用表达式模板实现符号微分 一般来说 您需要一种表示符号的方法 即编码的表达式模板 例如3 x x 42 以及一个可以计算导数的元函数 希望您对 C 中的元编程足够熟悉 知道这意味着什么和需要什么 但可以给您一个想法 This
  • 是否有一种算法可以在线性时间内计算数组反转?

    我知道有多少倒转 en wikipedia org wiki Inversion 28discrete mathematics 29 in an n 元素数组可以在 O n log n 操作使用增强型归并排序 http www geeksf
  • C++:初始化静态字符串成员

    我在 C 中初始化静态字符串成员时遇到一些问题 我有几个类 每个类都包含几个表示 id 的静态字符串成员 当我通过调用静态函数初始化变量时 一切都很好 但是 当我想为一个变量分配另一个变量的值时 它仍然保留空字符串 这段代码有什么问题 st
  • 本地主机上的 .net HTTP_X_FORWARDED_FOR NULL

    抱歉 如果其他地方已经回答了这个问题 我找不到它 如果没有 我会尝试查找访问过该站点的机器的原始 IP 根据我的基本理解 变量HTTP X FORWARDED FOR无论代理和其他过滤器如何 都会显示用户的 IP 如果这是真的 我正在尝试对
  • 函数参数评估顺序[重复]

    这个问题在这里已经有答案了 在 C 和 C 中 函数参数的求值是否有固定的顺序 我的意思是 标准怎么说 是吗left to right or right to left 我从书中得到的信息令人困惑 是否有必要function call应该使
  • 如何强制用户仅使用“new”创建从我派生的类的对象?

    为了实现引用计数 我们使用IUnknown http msdn microsoft com en us library ms680509 VS 85 aspx类接口和智能指针模板类 该接口具有所有引用计数方法的实现 包括Release vo
  • 将一个整数从 C 客户端发送到 Java 服务器

    我使用此代码将一个整数从我的 Java 客户端发送到我的 Java 服务器 int n rand nextInt 50 1 DataOutputStream dos new DataOutputStream socket getOutput
  • 按值返回的函数的返回语句中的初始化

    我的问题源于深入研究std move in return语句 例如以下示例 struct A A std cout lt lt Constructed lt lt this lt lt std endl A A noexcept std c
  • TreeView:仅在子节点中存在复选框

    我需要一个树视图控件 根节点没有复选框 只有图像 所有子节点都有一个复选框 图像 C net 2 0 winforms 不是 wpf WinForms树视图默认不支持混合复选框 非复选框节点 您可以在树视图上全局启用复选框 并使用以下命令在
  • 如何解决内存碎片

    我们偶尔会遇到这样的问题 长时间运行的服务器进程 在 Windows Server 2003 上运行 由于内存分配失败而引发异常 我们怀疑这些分配由于内存碎片而失败 因此 我们一直在寻找一些可能对我们有帮助的替代内存分配机制 我希望有人能告
  • 参数数量在编译时确定的 Lambda 函数

    我想声明一个带有 N 个参数的 lambda 函数 其中 N 是模板参数 就像是 template
  • 如何在给定点停止线程?

    我试图停止一些线程 阅读一些有关优雅地执行此操作的正确方法的内容 但我一定做错了什么 因为它根本不起作用 起初我尝试不使用lock with IsRunning不稳定 然后尝试使用锁 这是我所拥有的 private volatile boo
  • 如何在 Winform DataGridView 中创建不同的单元格格式

    我有一个 DataGridView 我将其绑定到 DataTable DataTable 是一个全数字值 要求 DataGridView 中的每 n 行都包含文本 而不是数值 以便在视觉上为用户分隔部分 我很高兴在绑定后将此文本数据放入 D
  • C# amo 获取角色完整

    我正在开发一个 SSAS 项目 其中除其他事项外 我需要获取 C 中表格多维数据集的完整用户列表 目前我让它以这样的方式工作 我可以获得角色 但数据不完整 当我调用 Server Database Roles 为了便于阅读而简化 属性并枚举

随机推荐

  • Element-ui使用Select多选框如何给value属性绑定对象类型

    如何给select多选下拉框的value属性绑定对象类型时怎么做 现在select标签处有一个value key属性绑定值必须是为option标签中v for遍历的数组中的元素 该元素值必须具有唯一性 在option属性key处绑定值为数组
  • 分类算法及其应用场景

    单一的分类方法主要包括 LR逻辑回归 SVM支持向量机 DT决策树 NB朴素贝叶斯 NN人工神经网络 K 近邻 集成学习算法 基于Bagging和Boosting算法思想 RF随机森林 GBDT Adaboost XGboost
  • 使用vim编写并编译运行C++程序并添加代码补全等功能

    听说使用vim写程序效率非常高 今天满怀一腔热血准备把vim整利索了 写篇笔记记录一下防止以后忘了 vim功能很多 我也只能是解决我现在的需求 以后遇到更多的需求再扩充 如果读者有什么建议和问题欢迎留言讨论 vim 编写一个C 程序并保存
  • Java中实现文件上传下载的三种解决方案(推荐)

    前言 文件上传是一个老生常谈的话题了 在文件相对比较小的情况下 可以直接把文件转化为字节流上传到服务器 但在文件比较大的情况下 用普通的方式进行上传 这可不是一个好的办法 毕竟很少有人会忍受 当文件上传到一半中断后 继续上传却只能重头开始上
  • 我的 2020 总结:跌宕起伏

    文章目录 复盘与展望 复盘与展望 2020总结 2021计划 个人 生理健康 55kg前半年熬夜较多眼睛干涩 眼睑有障碍 经常热敷毛巾 蒸汽眼罩滴眼药水 坚持锻炼 俯卧撑 开合跳 心理健康 6月份左右申请过劳动仲裁 迟迟拿不到钱比较着急 找
  • 初步学习MapReduce编程——编程实现文件合并和去重操作

    对于两个输入文件 即文件A和文件B 编写MapReduce程序 对两个文件进行合并 并剔除其中重复的内容 得到一个新的输出文件C 数据放TXT文件时 不能多出数据外的空行光标 不然运行程序时会显示错误 For input string 直接
  • From System Services Freezing to System Server Shutdown in Android: All You Need ....阅读报告

    From System Services Freezing to System Server Shutdown in Android All You Need Is a Loop in an App 阅读笔记 概述 这篇文章从安卓系统进程S
  • angular指令和管道_Angular单元测试开发人员指南—第2部分(服务,管道,指令)

    angular指令和管道 Explore how to write test cases for shared services Http services pipes and attribute directives in Angular
  • SpringBoot常用注解

    实体类常用注解 Data 注在类上 提供类的get set equals hashCode canEqual toString方法 AllArgsConstructor 注在类上 提供类的全参构造 NoArgsConstructor 注在类
  • Linux基础命令-date设置时间

    Linux基础命令 history历史记录 文章目录 前言 一 date命令的介绍 二 语法及参数 2 1 用help或man查看语法 2 2 常用参数 三 参考实例 3 1 以默认格式输出系统当前的时间和日期 3 2 按照 年 月 日 的
  • 深入理解Java中的自增

    引言 i 和 i的区别在学习程序设计的时候应该就已经学过 一个是用完再加 一个是加完再用 那么考虑一下下面的代码 int i 0 for int j 0 j lt 100 j i i 这个运行完 i的值应该是多少呢 深入理解自增 上面的代码
  • Java基础语法教学视频,MySql知识体系总结(SQL优化篇

    备注 因为mysql优化器的缘故 与索引顺序不一致 也会触发索引 但实际项目中尽量顺序一致 5 联合索引 但其中一个条件是 gt 6 联合索引 order by where和order by一起使用时 不要跨索引列使用 三 单表sql优化
  • LDO低压差线性稳压器

    LDO 转自 http baike baidu com view 1042146 htm 百科名片 LDO是low dropout regulator 意为低压差线性稳压器 是相对于传统的线性稳压器来说的 传统的线性稳压器 如78xx系列的
  • 元宇宙产业委一届二次全会召开 同步举办共享大会和全球元宇宙大会

    2022全球元宇宙大会 上海站将于8月18 19日在沪召开 央链直播快讯 2022年8月18 19日 由中国移动通信联合会主办 上海市通信管理局联合主办 众视Tech 中移联会展部 中国移动通信联合会元宇宙产业委员会承办 BIC EURON
  • Aop反射机制实现某个参数值 修改

    项目中使用敏感字段的加解密 但是有的是直接在url中拼接的 所以我就想根据一个自定义注解的方式做匹配 修改值 利用反射的机制实现值的修改 以下是具体的代码 依赖aop
  • VSCode顶端文件名多行显示

    VScode默认顶端一行显示所有打开的文件名 1 直接在设置中搜索 多行 在 Workbench Editor Wrap Tabs 前选中即可 2 也可用快捷键 Ctrl P 查看最近浏览文件记录
  • 2023华为od机试 Python 实现【德州扑克】

    前言 本题使用Python解答 如果需要Java代码 请参考 链接 题目 我们可以选择五张牌 它们的范围是 每张牌的大小在2 10之间 或者字母J Q K A 牌花色为红桃 黑桃 梅花 方块四种花色之一 现在一共有6种牌型 牌型1 同花顺
  • 运维体系框架标准化模型简介

    为什么要做标准化 标准化的过程实际上就是对运维对象的识别和建模过程 形成统一的对象模型后 各方在统一的认识下展开有效协作 然后针对不同的运维对象 再抽取出它们所对应的运维场景 接下来才是运维场景的自动化实现 这有点像我们学的面向对象编程的思
  • Fabric.js

    Fabric js是什么 Fabric js 是一个简化HTML5 Canvas开发的Javascript库 Fabric js提供了HTML5 Canvas本身缺失的对象模型 交互层 SVG解析器以及其他一整套工具 它是一个完全开源的项目
  • C中的预编译宏定义

    C中的预编译宏定义 作者 infobillows 发表日期 2007 09 15 21 34 点击数 1507 在将一个C源程序转换为可执行程序的过程中 编译预处理是最初的步骤 这一步骤是由预处理器 preprocessor 来完成的 在源