C的隐藏功能

时间:2008-09-25 09:02:07

标签: c hidden-features

我知道所有C编译器实现背后都有一个标准,所以应该没有隐藏的功能。尽管如此,我确信所有C开发人员都有他们一直使用的隐藏/秘密技巧。

56 个答案:

答案 0 :(得分:116)

GCC编译器的更多技巧,但你可以给编译器提供分支指示提示(在Linux内核中很常见)

#define likely(x)       __builtin_expect((x),1)
#define unlikely(x)     __builtin_expect((x),0)

请参阅:http://kerneltrap.org/node/4705

我喜欢这个,它也为某些功能增添了一些表现力。

void foo(int arg)
{
     if (unlikely(arg == 0)) {
           do_this();
           return;
     }
     do_that();
     ...
}

答案 1 :(得分:77)

int8_t
int16_t
int32_t
uint8_t
uint16_t
uint32_t

这些是标准中的可选项,但它必须是隐藏的功能,因为人们不断重新定义它们。我曾经使用过的一个代码库(目前仍在使用)有多个重定义,所有重定义都有不同的标识符。大部分时间都是使用预处理器宏:

#define INT16 short
#define INT32  long

等等。这让我想把头发拉出来。 只需使用怪异的标准整数typedef!

答案 2 :(得分:73)

逗号运算符未被广泛使用。它当然可以被滥用,但它也非常有用。这种用法是最常见的:

for (int i=0; i<10; i++, doSomethingElse())
{
  /* whatever */
}

但您可以在任何地方使用此运算符。观察:

int j = (printf("Assigning variable j\n"), getValueFromSomewhere());

评估每个语句,但表达式的值将是最后一个语句的值。

答案 3 :(得分:63)

将结构初始化为零

struct mystruct a = {0};

这将使所有结构元素归零。

答案 4 :(得分:62)

函数指针。您可以使用函数指针表来实现,例如,快速间接线程代码解释器(FORTH)或字节码调度程序,或模拟类似OO的虚拟方法。

然后标准库中有隐藏的宝石,例如qsort(),bsearch(),strpbrk(),strcspn()[后两个对于实现strtok()替换很有用。

C的错误特征是带符号的算术溢出是未定义的行为(UB)。所以无论何时你看到像x + y这样的表达式都是有符号的整数,它可能会溢出并导致UB。

答案 5 :(得分:52)

多字符常量:

int x = 'ABCD';

这会将x设置为0x41424344(或0x44434241,具体取决于架构。)

编辑:此技术不可移植,尤其是在序列化int时。 但是,创建自记录枚举功能非常有用。 e.g。

enum state {
    stopped = 'STOP',
    running = 'RUN!',
    waiting = 'WAIT',
};

如果您正在查看原始内存转储并且需要确定枚举的值而不必查找它,这会使它变得更加简单。

答案 6 :(得分:44)

我从未使用bit fields但是对于超低级别的东西来说它们听起来很酷。

struct cat {
    unsigned int legs:3;  // 3 bits for legs (0-4 fit in 3 bits)
    unsigned int lives:4; // 4 bits for lives (0-9 fit in 4 bits)
    // ...
};

cat make_cat()
{
    cat kitty;
    kitty.legs = 4;
    kitty.lives = 9;
    return kitty;
}

这意味着sizeof(cat)可以与sizeof(char)一样小。


Aaronleppie合并评论,谢谢你们。

答案 7 :(得分:37)

隔行扫描结构,如Duff's Device

strncpy(to, from, count)
char *to, *from;
int count;
{
    int n = (count + 7) / 8;
    switch (count % 8) {
    case 0: do { *to = *from++;
    case 7:      *to = *from++;
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n > 0);
    }
}

答案 8 :(得分:37)

C有一个标准但并非所有C编译器都完全兼容(我还没有看到任何完全兼容的C99编译器!)。

也就是说,我喜欢的技巧是那些非常明显且可以跨平台移植的技巧,因为它们依赖于C语义。它们通常是关于宏或位算术的。

例如:在不使用临时变量的情况下交换两个无符号整数:

...
a ^= b ; b ^= a; a ^=b;
...

或“扩展C”代表有限状态机,如:

FSM {
  STATE(x) {
    ...
    NEXTSTATE(y);
  }

  STATE(y) {
    ...
    if (x == 0) 
      NEXTSTATE(y);
    else 
      NEXTSTATE(x);
  }
}

可以通过以下宏实现:

#define FSM
#define STATE(x)      s_##x :
#define NEXTSTATE(x)  goto s_##x

一般来说,我不喜欢聪明的技巧但是让代码不必要地复杂化(作为交换示例)而且我喜欢使代码更清晰并直接传达意图的那些(如FSM示例)。

答案 9 :(得分:33)

我非常喜欢指定的初始化程序,在C99中添加(并且在gcc中支持了很长时间):

#define FOO 16
#define BAR 3

myStructType_t myStuff[] = {
    [FOO] = { foo1, foo2, foo3 },
    [BAR] = { bar1, bar2, bar3 },
    ...

数组初始化不再依赖于位置。如果更改FOO或BAR的值,则数组初始化将自动对应于其新值。

答案 10 :(得分:28)

C99有一些很棒的任意顺序结构初始化。

struct foo{
  int x;
  int y;
  char* name;
};

void main(){
  struct foo f = { .y = 23, .name = "awesome", .x = -38 };
}

struct foo{ int x; int y; char* name; }; void main(){ struct foo f = { .y = 23, .name = "awesome", .x = -38 }; }

答案 11 :(得分:27)

匿名结构和数组是我最喜欢的。 (参见http://www.run.montefiore.ulg.ac.be/~martin/resources/kung-f00.html

setsockopt(yourSocket, SOL_SOCKET, SO_REUSEADDR, (int[]){1}, sizeof(int));

void myFunction(type* values) {
    while(*values) x=*values++;
}
myFunction((type[]){val1,val2,val3,val4,0});

它甚至可以用于实现链接列表...

答案 12 :(得分:24)

嗯......我认为C语言的一个优点是它的可移植性和标准性,所以每当我在我正在使用的实现中找到一些“隐藏技巧”时,我尽量不使用它,因为我尝试保持我的C代码尽可能标准和便携。

答案 13 :(得分:24)

gcc有很多我喜欢的C语言扩展,可以找到here。我最喜欢的一些是function attributes。一个非常有用的例子是format属性。如果您定义一个采用printf格式字符串的自定义函数,则可以使用此方法。如果启用此函数属性,gcc将对您的参数进行检查,以确保您的格式字符串和参数匹配,并在适当时生成警告或错误。

int my_printf (void *my_object, const char *my_format, ...)
            __attribute__ ((format (printf, 2, 3)));

答案 14 :(得分:24)

当我第一次看到“震惊”我的(隐藏)功能是关于printf。此功能允许您使用变量格式化格式说明符本身。寻找代码,你会看到更好的:

#include <stdio.h>

int main() {
    int a = 3;
    float b = 6.412355;
    printf("%.*f\n",a,b);
    return 0;
}

*角色达到了这个效果。

答案 15 :(得分:19)

编译时断言,为already discussed here

//--- size of static_assertion array is negative if condition is not met
#define STATIC_ASSERT(condition) \
    typedef struct { \
        char static_assertion[condition ? 1 : -1]; \
    } static_assertion_t

//--- ensure structure fits in 
STATIC_ASSERT(sizeof(mystruct_t) <= 4096);

答案 16 :(得分:16)

常量字符串连接

我很惊讶没有在答案中看到它,因为我所知道的所有编译器都支持它,但许多程序员似乎忽略了它。有时它非常方便,而且不仅仅是在编写宏时。

我目前的代码中使用的用例: 我在配置文件中有#define PATH "/some/path/"(实际上它是由makefile设置的)。现在我想构建包含文件名的完整路径来打开资源。它只是:

fd = open(PATH "/file", flags);

而不是可怕的,但很常见的:

char buffer[256];
snprintf(buffer, 256, "%s/file", PATH);
fd = open(buffer, flags);

请注意,常见的可怕解决方案是:

  • 三倍
  • 更不容易阅读
  • 慢得多
  • 设置为任意缓冲区大小限制的强大功能(但你必须使用更长的代码来避免没有常量字符串连接)。
  • 使用更多堆栈空间

答案 17 :(得分:15)

好吧,我从来没有用过它,而且我不确定我是否会向任何人推荐它,但我觉得如果不提及Simon Tatham的co-routine trick.

答案 18 :(得分:12)

初始化数组或枚举时,可以在初始化列表中的最后一项之后放置逗号。 e.g:

int x[] = { 1, 2, 3, };

enum foo { bar, baz, boom, };

这样做是为了让你自动生成代码,你不必担心删除最后一个逗号。

答案 19 :(得分:12)

结构分配很酷。许多人似乎并没有意识到结构也是值,并且可以被分配,没有必要使用memcpy(),当一个简单的赋值就可以了。

例如,考虑一些虚构的2D图形库,它可能会定义一个表示(整数)屏幕坐标的类型:

typedef struct {
   int x;
   int y;
} Point;

现在,你做的事情可能看起来“错误”,比如编写一个创建从函数参数初始化的点的函数,并返回它,如下所示:

Point point_new(int x, int y)
{
  Point p;
  p.x = x;
  p.y = y;
  return p;
}

这是安全的,当然很长(因为)使用结构赋值按值复制返回值:

Point origin;
origin = point_new(0, 0);

通过这种方式,您可以编写非常干净且面向对象的代码,所有代码都采用简单的标准C.

答案 20 :(得分:10)

奇怪的矢量索引:

int v[100]; int index = 10; 
/* v[index] it's the same thing as index[v] */

答案 21 :(得分:9)

使用sscanf时,您可以使用%n找出您应该继续阅读的位置:

sscanf ( string, "%d%n", &number, &length );
string += length;

显然,你无法添加另一个答案,所以我会在这里添加第二个答案,你可以使用“&amp;&amp;”和“||”作为条件:

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

int main()
{
   1 || puts("Hello\n");
   0 || puts("Hi\n");
   1 && puts("ROFL\n");
   0 && puts("LOL\n");

   exit( 0 );
}

此代码将输出:

Hi
ROFL

答案 22 :(得分:9)

C编译器实现了几个标准之一。但是,拥有标准并不意味着语言的所有方面都已定义。例如,Duff's device是一个受欢迎的“隐藏”功能,它已经变得如此受欢迎,以至于现代编译器具有特殊用途的识别代码,以确保优化技术不会破坏这种常用模式的预期效果。

一般情况下,当您在编译器使用的任何C标准的剃刀边缘上运行时,不鼓励隐藏功能或语言技巧。许多这样的技巧从一个编译器到另一个编译器不起作用,并且这些类型的功能通常会从给定制造商的一个版本的编译器套件失败到另一个版本。

破坏C代码的各种技巧包括:

  1. 依赖编译器如何在内存中布局结构。
  2. 对整数/浮点数的 endianness 的假设。
  3. 关于功能ABI的假设。
  4. 关于堆栈帧增长方向的假设。
  5. 关于声明内执行顺序的假设。
  6. 关于函数参数中语句执行顺序的假设。
  7. 关于short,int,long,float和double类型的位大小或精度的假设。
  8. 当程序员对大多数C标准中指定为“编译器相关”行为的执行模型做出假设时出现的其他问题和问题。

答案 23 :(得分:8)

我最近发现了0个位域。

struct {
  int    a:3;
  int    b:2;
  int     :0;
  int    c:4;
  int    d:3;
};

将给出

的布局
000aaabb 0ccccddd

而不是没有:0;

0000aaab bccccddd

0宽度字段表示应在下一个原子实体(char

上设置以下位域

答案 24 :(得分:8)

Gcc(c)有一些有趣的功能,你可以启用它们,比如嵌套的函数声明,以及?:b形式的?:运算符,如果a不是false,则返回一个。

答案 25 :(得分:8)

使用INT(3)在代码中设置断点是我最喜欢的

答案 26 :(得分:8)

使用枚举进行编译时假设检查: 愚蠢的例子,但对于具有编译时可配置常量的库非常有用。

#define D 1
#define DD 2

enum CompileTimeCheck
{
    MAKE_SURE_DD_IS_TWICE_D = 1/(2*(D) == (DD)),
    MAKE_SURE_DD_IS_POW2    = 1/((((DD) - 1) & (DD)) == 0)
};

答案 27 :(得分:8)

我最喜欢的C“隐藏”功能是在printf中使用%n来回写堆栈。通常,printf会根据格式字符串从堆栈中弹出参数值,但%n可以将它们写回。

查看第3.4.2节here。可能会导致许多令人讨厌的漏洞。

答案 28 :(得分:7)

C99风格的可变参数宏,又名

#define ERR(name, fmt, ...)   fprintf(stderr, "ERROR " #name ": " fmt "\n", \
                                  __VAR_ARGS__)

将像

一样使用
ERR(errCantOpen, "File %s cannot be opened", filename);

这里我还使用了stringize运算符和字符串常量连接,以及我非常喜欢的其他功能。

答案 29 :(得分:6)

在某些情况下,可变大小的自动变量也很有用。这些是在nC99中添加的,并且已经在gcc中支持了很长时间。

void foo(uint32_t extraPadding) {
    uint8_t commBuffer[sizeof(myProtocol_t) + extraPadding];

您最终在堆栈上有一个缓冲区,其中包含固定大小的协议标头和可变大小数据的空间。使用alloca()可以获得相同的效果,但这种语法更紧凑。

在调用此例程之前,您必须确保extraPadding是一个合理的值,否则您最终会破坏堆栈。在调用malloc或任何其他内存分配技术之前,你必须仔细检查参数,所以这并不是很不寻常。

答案 30 :(得分:5)

我喜欢你可以制作的可变大小的结构:

typedef struct {
    unsigned int size;
    char buffer[1];
} tSizedBuffer;

tSizedBuffer *buff = (tSizedBuffer*)(malloc(sizeof(tSizedBuffer) + 99));

// can now refer to buff->buffer[0..99].

也就是现在在ANSI C中的offsetof,但是在我第一次看到它的时候却是一个魔法。它基本上使用address-of运算符(&amp;)作为空指针重铸作为结构变量。

答案 31 :(得分:5)

我喜欢__LINE____FILE__。见这里:http://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html

答案 32 :(得分:5)

GCC中的Lambda(例如匿名函数):

#define lambda(return_type, function_body) \
    ({ return_type fn function_body fn })

这可以用作:

lambda (int, (int x, int y) { return x > y; })(1, 2)

扩展为:

({ int fn (int x, int y) { return x > y } fn; })(1, 2)

答案 33 :(得分:4)

要清除输入缓冲区,您无法使用fflush(stdin)。正确的方法如下:scanf("%*[^\n]%*c") 这将丢弃输入缓冲区中的所有内容。

答案 34 :(得分:3)

我在C编程超过15年后才发现这一点:

struct SomeStruct
{
   unsigned a : 5;
   unsigned b : 1;
   unsigned c : 7;
};

位字段!冒号后面的数字是成员所需的位数,成员打包到指定的类型中,因此如果unsigned是16位,则上面的内容如下所示:

xxxc cccc ccba aaaa

Skizz

答案 35 :(得分:3)

使用不寻常的类型转换来转换类型。虽然不是隐藏的功能,但它非常棘手。

示例:

如果你需要知道编译器存储如何浮动,那么试试这个:

uint32_t Int;
float flt = 10.5; // say

Int = *(uint32_t *)&flt;

printf ("Float 10.5 is stored internally as %8X\n", Int);

float flt = 10.5; // say

printf ("Float 10.5 is stored internally as %8X\n", *(uint32_t *)&flt);

注意巧妙地使用类型转换。将变量的地址(此处为&amp; flt)转换为所需类型(此处为(uint32_t *))并提取其内容(应用'*')。

这也适用于表达的另一面:

*(float *)&Int = flt;

这也可以使用union来实现:

typedef union
{
  uint32_t Int;
  float    flt;

} FloatInt_type;

答案 36 :(得分:3)

我在一段代码中看到了这一点,并询问它做了什么:


hexDigit = "0123456789abcdef"[someNybble];

另一个最喜欢的是:


unsigned char bar[100];
unsigned char *foo = bar;
unsigned char blah = 42[foo];

答案 37 :(得分:3)

早期版本的gcc尝试在源代码中遇到“#pragma”时运行游戏。另请参阅here

答案 38 :(得分:2)

将变量与文字进行比较时,最好将文字放在==运算符的上,以确保编译器在您错误使用时出错而是赋值算子。

if (0 == count) {
    ...
}

乍一看可能看起来很奇怪,但它可以省去一些头痛(比如你碰巧错误地输入了if (count = 0))。

答案 39 :(得分:2)

intptr_t用于声明类型指针的变量。 C99具体并在stdint.h中声明

答案 40 :(得分:2)

这不是一个隐藏的功能,但它看起来像伏都教,我第一次看到这样的东西:


void callback(const char *msg, void *data)
{
    // do something with msg, e.g.
    printf("%s\n", msg);

    return;
    data = NULL;
}

这种构造的原因是,如果使用-Wextra编译它并且没有“data = NULL;” - 行,gcc将发出关于未使用参数的警告。但是对于这条无用的线路,你不会收到警告。

编辑:我知道还有其他(更好的)方法来阻止这些警告。我第一次看到它时,看起来很奇怪。

答案 41 :(得分:2)

Steve Webb已指出__LINE____FILE__宏。它让我想起在我上一份工作中我是如何破解它们进行内存中记录的。

我正在开发一种设备,其中没有可用的端口将日志信息从设备传递到用于调试的PC。可以使用断点来使用调试器来暂停和了解程序的状态,但是没有关于系统跟踪的信息。

由于对调试日志的所有调用实际上都是单个全局宏,因此我们将该宏更改为将文件名和行号转储到全局数组。此数组包含一系列文件名和行号,显示调用了哪些调试调用,给出了执行跟踪的公平概念(尽管不是实际的日志消息)。可以通过调试器暂停执行,将这些字节转储到本地文件,然后使用脚本将此信息映射到代码库。这是因为我们有严格的编码指南,所以我们可以在一个文件中对记录机制进行更改。

答案 42 :(得分:1)

函数指针的大小不是标准的。至少不在K&amp; R书中。尽管它讨论了其他类型指针的大小但是(我认为)函数指针的sizeof是未定义的行为。

同样sizeof是编译时运算符,我看到很多人都在询问sizeof是否是在线论坛中的函数或运算符。

我看到的一个错误如下(简化示例):

int j;
int i;
j = sizeof(i++)

i上的增量将不会执行,因为在编译时评估sizeof。程序员打算在一个语句中破解这两个操作,增加i和计算sizeof

C中的运算符优先级管理关联顺序而不是评估顺序。例如,如果您有三个函数fgh,每个函数都返回一个int,它们的表达式如下:

f() + g() * h()

C标准没有给出关于这些功能的评估顺序的规则。在添加g的结果之前,hf的结果将成倍增加。如果函数共享状态并且计算取决于这些函数的评估顺序,则这可能导致错误。这可能导致可移植性问题。

答案 43 :(得分:1)

<强>摘录

  

在此页面中,您将找到一个列表   有趣的C编程   问题/谜题,这些程序   列出的是我所拥有的   收到我的电子邮件转发   朋友,我在一些书中读到的一些,a   很少来自互联网,还有一些来自互联网   我在C中的编码经验。

http://www.gowrikumar.com/c/index.html

答案 44 :(得分:1)

这是gcc中的三个不错的选择:

__FILE__ 
__FUNCTION__
__LINE__

答案 45 :(得分:1)

假设您有一个包含相同类型成员的结构:

struct Point {
    float x;
    float y;
    float z;
};

您可以将它的实例转换为浮点指针并使用数组索引:

Point a;
int sum = 0, i = 0;
for( ; i < 3; i++)
    sum += ((float*)a)[i];

非常基础,但在编写简洁代码时很有用。

答案 46 :(得分:1)

寄存器变量

我曾经使用 register 关键字声明一些变量,以帮助加快速度。这将提示C编译器使用CPU寄存器作为本地存储。由于现代C编译器会自动执行此操作,因此很可能不再需要这样做。

答案 47 :(得分:0)

使用NaN进行链式计算/错误返回:

//#include&lt; stdint.h&gt;
static uint64_t iNaN = 0xFFF8000000000000;
const double NaN = *(double *)&amp; iNaN; //安静的NaN

内部函数可以将NaN作为错误标志返回:它可以安全地用于任何计算,结果将始终为NaN。

注意:测试NaN很棘手,因为NaN!= NaN ...使用isnan(x),或滚动你自己。 x!= x在数学上是正确的,如果x是NaN,但往往会被某些编译器优化出来

答案 48 :(得分:0)

我喜欢typeof()运算符。它的工作方式与sizeof()类似,它在编译时解析。它不返回字节数,而是返回类型。当你需要将变量声明为与其他变量相同的类型时,这是有用的,无论它是什么类型。

typeof(foo) copy_of_foo; //declare bar to be a variable of the same type as foo
copy_of_foo = foo; //now copy_of_foo has a backup of foo, for any type

这可能只是一个gcc扩展,我不确定。

答案 49 :(得分:0)

面向对象的C宏: 你需要一个构造函数(init),一个析构函数(dispose),一个相等的(相等的),一个复制器(副本),以及一些用于实例化的原型(原型)。

通过声明,您需要声明一个常量原型来复制和派生。然后你可以做C_OO_NEW。如果需要,我可以发布更多示例。 LibPurple是一个面向对象的大型C代码库,带有一个回调系统(如果你想看到一个正在使用中)

#define C_copy(to, from) to->copy(to, from)

#define true 1
#define false 0
#define C_OO_PROTOTYPE(type)\
void type##_init (struct type##_struct *my);\
void type##_dispose (struct type##_struct *my);\
char type##_equal (struct type##_struct *my, struct type##_struct *yours); \
struct type##_struct * type##_copy (struct type##_struct *my, struct type##_struct *from); \
const type type##__prototype = {type##_init, type##_dispose, type##_equal, type##_copy

#define C_OO_OVERHEAD(type)\
        void (*init) (struct type##_struct *my);\
        void (*dispose) (struct type##_struct *my);\
        char (*equal) (struct type##_struct *my, struct type##_struct *yours); \
        struct type##_struct *(*copy) (struct type##_struct *my, struct type##_struct *from); 

#define C_OO_IN(ret, type, function, ...)       ret (* function ) (struct type##_struct *my, __VA_ARGS__);
#define C_OO_OUT(ret, type, function, ...)      ret type##_##function (struct type##_struct *my, __VA_ARGS__);

#define C_OO_PNEW(type, instance)\
        instance = ( type *) malloc(sizeof( type ));\
        memcpy(instance, & type##__prototype, sizeof( type ));

#define C_OO_NEW(type, instance)\
        type instance;\
        memcpy(&instance, & type ## __prototype, sizeof(type));

#define C_OO_DELETE(instance)\
        instance->dispose(instance);\
        free(instance);

#define C_OO_INIT(type)         void type##_init (struct type##_struct *my){return;}
#define C_OO_DISPOSE(type)      void type##_dispose (struct type##_struct *my){return;}
#define C_OO_EQUAL(type)        char type##_equal (struct type##_struct *my, struct type##_struct *yours){return 0;}
#define C_OO_COPY(type)         struct type##_struct * type##_copy (struct type##_struct *my, struct type##_struct *from){return 0;}

答案 50 :(得分:0)

我刚读过这个article。它有一些C和其他几种语言的“隐藏功能”。

答案 51 :(得分:0)

像这样包裹malloc和realloc:

#ifdef _DEBUG
#define mmalloc(bytes)                  malloc(bytes);printf("malloc: %d\t<%s@%d>\n", bytes, __FILE__, __LINE__);
#define mrealloc(pointer, bytes)        realloc(pointer, bytes);printf("realloc: %d\t<%s@%d>\n", bytes, __FILE__, __LINE__);
#else //_DEBUG
#define mmalloc(bytes)                  malloc(bytes)
#define mrealloc(pointer, bytes)        realloc(pointer, bytes)

事实上,这是我的全部砷(BailIfNot适用于OO c):

#ifdef _DEBUG
#define mmalloc(bytes)                  malloc(bytes);printf("malloc: %d\t<%s@%d>\n", bytes, __FILE__, __LINE__);
#define mrealloc(pointer, bytes)        realloc(pointer, bytes);printf("realloc: %d\t<%s@%d>\n", bytes, __FILE__, __LINE__);
#define BAILIFNOT(Node, Check)  if(Node->type != Check) return 0;
#define NULLCHECK(var)          if(var == NULL) setError(__FILE__, __LINE__, "Null exception", " var ", FATAL);
#define ASSERT(n)               if( ! ( n ) ) { printf("<ASSERT FAILURE@%s:%d>", __FILE__, __LINE__); fflush(0); __asm("int $0x3"); }
#define TRACE(n)                printf("trace: %s <%s@%d>\n", n, __FILE__, __LINE__);fflush(0);
#else //_DEBUG
#define mmalloc(bytes)                  malloc(bytes)
#define mrealloc(pointer, bytes)        realloc(pointer, bytes)
#define BAILIFNOT(Node, Check)  {}
#define NULLCHECK(var)          {}
#define ASSERT(n)               {}
#define TRACE(n)                {}
#endif //_DEBUG

以下是一些示例输出:

malloc: 12      <hash.c@298>
trace: nodeCreate <hash.c@302>
malloc: 5       <hash.c@308>
malloc: 16      <hash.c@316>
malloc: 256     <hash.c@320>
trace: dataLoadHead <hash.c@441>
malloc: 270     <hash.c@463>
malloc: 262144  <hash.c@467>
trace: dataLoadRecursive <hash.c@404>

答案 52 :(得分:0)

可变大小的结构体,在常见的解析器库中可以看到。

struct foo
{
  int a;
  int b;
  char b[1]; // using [0] is no longer correct
             // must come at end
};

char *str = "abcdef";
int len = strlen(str);
struct foo *bar = malloc(sizeof(foo) + len);

strcpy(bar.b, str); // try and stop me!

答案 53 :(得分:0)

%n格式字符串中经常被遗忘的printf说明符有时非常实用。 %n返回printf格式化输出时使用的虚拟光标的当前位置。

int pos1, pos2;
 char *string_of_unknown_length = "we don't care about the length of this";

  printf("Write text of unknown %n(%s)%n text\n", &pos1, string_of_unknown_length, &pos2);
  printf("%*s\\%*s/\n", pos1, " ", pos2-pos1-2, " ");
  printf("%*s", pos1+1, " ");
  for(int i=pos1+1; i<pos2-1; i++)
    putc('-', stdout);
  putc('\n', stdout);

将有以下输出

Write text of unknown (we don't care about the length of this) text
                      \                                      /
                       --------------------------------------

有点做作,但在制作漂亮的报道时会有一些用处。

答案 54 :(得分:0)

如何在交换机内使用while(0),以便可以使用break这样的continue语句: - )

void sw(int s)
{
    switch (s) while (0) {
    case 0:
        printf("zero\n");
        continue;
    case 1:
        printf("one\n");
        continue;
    default:
        printf("something else\n");
        continue;
    }
}

答案 55 :(得分:-1)

在Visual Studio中,您可以突出显示自己定义的类型。

为此,在“Commom7 / IDE”文件夹中创建一个名为“usertype.dat”的文件。该文件的内容应为您要突出显示的类型。例如:

// usertype.dat的内容

int8_t
int16_t
int32_t
int64_t
uint8_t
uint16_t
uint32_t
uint64_t
float32_t
float64_t
char_t