例如,我最近在linux内核中遇到过这个问题:
/* Force a compilation error if condition is true */ #define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
所以,在你的代码中,如果你有一些结构必须是,比如8个字节的大小,可能是因为一些硬件限制,你可以这样做:
BUILD_BUG_ON((sizeof(struct mystruct) % 8) != 0);
除非struct mystruct的大小是8的倍数,否则它将不会编译,如果它是8的倍数,则根本不会生成运行时代码。
我知道的另一个技巧是“Graphics Gems”一书,它允许单个头文件在一个模块中声明和初始化变量,而在使用该模块的其他模块中,只是将它们声明为外部。
#ifdef DEFINE_MYHEADER_GLOBALS #define GLOBAL #define INIT(x, y) (x) = (y) #else #define GLOBAL extern #define INIT(x, y) #endif GLOBAL int INIT(x, 0); GLOBAL int somefunc(int a, int b);
有了这个,定义x和somefunc的代码就是:
#define DEFINE_MYHEADER_GLOBALS #include "the_above_header_file.h"
而仅使用x和somefunc()的代码执行:
#include "the_above_header_file.h"
所以你得到一个头文件,声明全局和函数原型的实例,以及相应的extern声明。
那么,你最喜欢的C编程技巧是什么?
答案 0 :(得分:81)
C99使用匿名数组提供了一些非常酷的东西:
删除无意义的变量
{
int yes=1;
setsockopt(yourSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
}
变为
setsockopt(yourSocket, SOL_SOCKET, SO_REUSEADDR, (int[]){1}, sizeof(int));
传递可变数量的参数
void func(type* values) {
while(*values) {
x = *values++;
/* do whatever with x */
}
}
func((type[]){val1,val2,val3,val4,0});
静态链接列表
int main() {
struct llist { int a; struct llist* next;};
#define cons(x,y) (struct llist[]){{x,y}}
struct llist *list=cons(1, cons(2, cons(3, cons(4, NULL))));
struct llist *p = list;
while(p != 0) {
printf("%d\n", p->a);
p = p->next;
}
}
任何我确定我还没有想到的许多其他很酷的技术。
答案 1 :(得分:68)
在阅读Quake 2源代码时,我提出了类似的内容:
double normals[][] = {
#include "normals.txt"
};
(或多或少,我现在没有代码检查它。)
从那时起,一个创造性地使用预处理器的新世界在我眼前展开。我不再只包含标题,而是包括整个代码块(它可以提高可重用性):-p
感谢John Carmack!的xD
答案 2 :(得分:50)
我喜欢使用= {0};
初始化结构而无需调用memset。
struct something X = {0};
这会将struct(或数组)的所有成员初始化为零(但不是任何填充字节 - 如果你需要将其归零,则使用memset。)
但你应该知道有some issues with this for large, dynamically allocated structures。
答案 3 :(得分:45)
如果我们谈论c技巧,我最喜欢的是Duff's Device循环展开!我只是在等待合适的机会来让我真正用它来生气......
答案 4 :(得分:42)
使用__FILE__
和__LINE__
进行调试
#define WHERE fprintf(stderr,"[LOG]%s:%d\n",__FILE__,__LINE__);
答案 5 :(得分:31)
在C99
typedef struct{
int value;
int otherValue;
} s;
s test = {.value = 15, .otherValue = 16};
/* or */
int a[100] = {1,2,[50]=3,4,5,[23]=6,7};
答案 6 :(得分:28)
一旦我的配偶和我重新定义了返回以找到一个棘手的堆栈损坏错误。
类似的东西:
#define return DoSomeStackCheckStuff, return
答案 7 :(得分:23)
我喜欢“struct hack”,因为它有一个动态大小的对象。 This site也很好地解释了它(虽然它们引用了C99版本,你可以在其中编写“str []”作为结构的最后一个成员)。你可以像这样创建一个字符串“object”:
struct X {
int len;
char str[1];
};
int n = strlen("hello world");
struct X *string = malloc(sizeof(struct X) + n);
strcpy(string->str, "hello world");
string->len = n;
这里,我们在堆上分配了一个类型为X的结构,其大小为int(对于len),加上“hello world”的长度加上1(因为包括str 1在sizeof(X)。
当您希望在同一块中的某些可变长度数据之前有一个“标题”时,它通常很有用。
答案 8 :(得分:17)
面向对象的代码,带有C,通过模拟类。
只需创建一个结构和一组函数,这些函数将指向该结构的指针作为第一个参数。
答案 9 :(得分:16)
而不是
printf("counter=%d\n",counter);
使用
#define print_dec(var) printf("%s=%d\n",#var,var);
print_dec(counter);
答案 10 :(得分:14)
使用愚蠢的宏技巧使记录定义更容易维护。
#define COLUMNS(S,E) [(E) - (S) + 1]
typedef struct
{
char studentNumber COLUMNS( 1, 9);
char firstName COLUMNS(10, 30);
char lastName COLUMNS(31, 51);
} StudentRecord;
答案 11 :(得分:11)
用于创建一个在所有模块中只读的变量,但声明的变量除外:
// Header1.h:
#ifndef SOURCE1_C
extern const int MyVar;
#endif
// Source1.c:
#define SOURCE1_C
#include Header1.h // MyVar isn't seen in the header
int MyVar; // Declared in this file, and is writeable
// Source2.c
#include Header1.h // MyVar is seen as a constant, declared elsewhere
答案 12 :(得分:8)
声明指向实现有限状态机的函数的指针数组。
int (* fsm[])(void) = { ... }
最令人高兴的是,强制每个刺激/状态检查所有代码路径都很简单。
在嵌入式系统中,我经常会将ISR映射到指向这样的表并根据需要进行渲染(在ISR之外)。
答案 13 :(得分:8)
位移仅定义为移位量31(在32位整数上)..
如果你想要一个需要使用更高移位值的计算移位,你会怎么做?以下是Theora视频编解码器的用法:
unsigned int shiftmystuff (unsigned int a, unsigned int v)
{
return (a>>(v>>1))>>((v+1)>>1);
}
或者更具可读性:
unsigned int shiftmystuff (unsigned int a, unsigned int v)
{
unsigned int halfshift = v>>1;
unsigned int otherhalf = (v+1)>>1;
return (a >> halfshift) >> otherhalf;
}
按照上面显示的方式执行任务比使用这样的分支更快:
unsigned int shiftmystuff (unsigned int a, unsigned int v)
{
if (v<=31)
return a>>v;
else
return 0;
}
答案 14 :(得分:7)
另一个不错的预处理器“技巧”是使用“#”字符来打印调试表达式。例如:
#define MY_ASSERT(cond) \
do { \
if( !(cond) ) { \
printf("MY_ASSERT(%s) failed\n", #cond); \
exit(-1); \
} \
} while( 0 )
编辑:以下代码仅适用于C ++。感谢smcameron和Evan Teran。
是的,编译时断言始终很好。它也可以写成:
#define COMPILE_ASSERT(cond)\
typedef char __compile_time_assert[ (cond) ? 0 : -1]
答案 15 :(得分:6)
#if TESTMODE == 1
debug=1;
while(0); // Get attention
#endif
while(0);对程序没有任何影响,但编译器会发出一个警告“这没什么用”,这足以让我去查看有问题的行,然后看看我想引起注意的真正原因。
答案 16 :(得分:6)
我不会把它称为最喜欢的技巧,因为我从来没有使用它,但是提到Duff的设备让我想起this article关于在C中实现Coroutines的问题。它总是给我一个轻笑,但是我相信它可能会有用一段时间。
答案 17 :(得分:6)
我是xor hacks的粉丝:
交换2个没有第三个临时指针的指针:
int * a;
int * b;
a ^= b;
b ^= a;
a ^= b;
或者我真的很喜欢只有一个指针的xor链表。 (http://en.wikipedia.org/wiki/XOR_linked_list)
链表中的每个节点都是前一节点和下一节点的Xor。要遍历,可以通过以下方式找到节点的地址:
LLNode * first = head;
LLNode * second = first.linked_nodes;
LLNode * third = second.linked_nodes ^ first;
LLNode * fourth = third.linked_nodes ^ second;
等
或向后追溯:
LLNode * last = tail;
LLNode * second_to_last = last.linked_nodes;
LLNode * third_to_last = second_to_last.linked_nodes ^ last;
LLNode * fourth_to_last = third_to_last.linked_nodes ^ second_to_last;
等
虽然不是非常有用(你无法从任意节点开始遍历),但我发现它非常酷。
答案 18 :(得分:5)
这本书来自“足够的绳索射击自己的脚”:
在标题声明
中#ifndef RELEASE
# define D(x) do { x; } while (0)
#else
# define D(x)
#endif
在您的代码位置测试语句中,例如:
D(printf("Test statement\n"));
do / while有助于宏的内容扩展到多个语句。
只有在未使用编译器的'-D RELEASE'标志时才会打印该语句。
然后你就可以了。将标志传递给你的makefile等。
不确定这在Windows中是如何工作的,但在* nix中效果很好
答案 19 :(得分:3)
这类内容的两本优秀资料书籍是The Practice of Programming和Writing Solid Code。其中一个(我不记得是哪个)说:首选枚举到#define,因为enum会被编译器检查。
答案 20 :(得分:3)
不是特定于C,但我一直很喜欢XOR运算符。它可以做的一件很酷的事情是“没有临时值的交换”:
int a = 1;
int b = 2;
printf("a = %d, b = %d\n", a, b);
a ^= b;
b ^= a;
a ^= b;
printf("a = %d, b = %d\n", a, b);
结果:
a = 1,b = 2
a = 2,b = 1
答案 21 :(得分:3)
Rusty实际上在ccan中生成了一整套构建条件,请查看构建断言模块:
#include <stddef.h>
#include <ccan/build_assert/build_assert.h>
struct foo {
char string[5];
int x;
};
char *foo_string(struct foo *foo)
{
// This trick requires that the string be first in the structure
BUILD_ASSERT(offsetof(struct foo, string) == 0);
return (char *)foo;
}
实际标题中还有许多其他有用的宏,很容易就位。
我试着通过坚持内联函数来尽力抵制黑暗面(和预处理程序滥用)的拉动,但我确实喜欢你所描述的那些聪明有用的宏。
答案 22 :(得分:2)
请参阅"Hidden features of C"问题。
答案 23 :(得分:2)
我喜欢列表中使用的 container_of
的概念。基本上,您不需要为列表中的每个结构指定next
和last
字段。而是将列表结构标题附加到实际的链接项。
查看include/linux/list.h
的真实示例。
答案 24 :(得分:1)
我认为使用 userdata 指针非常简洁。时尚潮流正在逐渐消失。它不是一个C功能,而是很容易在C中使用。
答案 25 :(得分:1)
我使用X-Macros让预编译器生成代码。它们对于在一个地方定义错误值和相关的错误字符串特别有用,但它们可以远远超出它。
答案 26 :(得分:1)
我们的代码库有一个类似于
的技巧#ifdef DEBUG
#define my_malloc(amt) my_malloc_debug(amt, __FILE__, __LINE__)
void * my_malloc_debug(int amt, char* file, int line)
#else
void * my_malloc(int amt)
#endif
{
//remember file and line no. for this malloc in debug mode
}
允许在调试模式下跟踪内存泄漏。我一直认为这很酷。
答案 27 :(得分:1)
有趣的宏:
#define SOME_ENUMS(F) \
F(ZERO, zero) \
F(ONE, one) \
F(TWO, two)
/* Now define the constant values. See how succinct this is. */
enum Constants {
#define DEFINE_ENUM(A, B) A,
SOME_ENUMS(DEFINE_ENUMS)
#undef DEFINE_ENUM
};
/* Now a function to return the name of an enum: */
const char *ToString(int c) {
switch (c) {
default: return NULL; /* Or whatever. */
#define CASE_MACRO(A, B) case A: return #b;
SOME_ENUMS(CASE_MACRO)
#undef CASE_MACRO
}
}
答案 28 :(得分:0)
我喜欢空的if-else和while(0)运算符。
例如:
#define CMD1(X) do { foo(x); bar(x); } while (0)
#define CMD2(X) if (1) { foo(x); bar(x); } else
答案 29 :(得分:0)
在C99中,您可以直接将URL嵌入到函数内的源代码中。例如:
#include <stdio.h>
int main(int argc, char** argv) {
http://stackoverflow.com/
printf("Hello World");
}
答案 30 :(得分:0)
我总是喜欢愚蠢的预处理器技巧来制作通用容器类型:
/* list.h */
#ifndef CONTAINER_TYPE
#define CONTAINER_TYPE VALUE_TYPE ## List
#endif
typedef struct CONTAINER_TYPE {
CONTAINER_TYPE *next;
VALUE_TYPE v;
} CONTAINER_TYPE;
/* Possibly Lots of functions for manipulating Lists
*/
#undef VALUE_TYPE
#undef CONTAINER_TYPE
然后你可以这样做:
#define VALUE_TYPE int
#include "list.h"
typedef struct myFancyStructure *myFancyStructureP;
#define VALUE_TYPE myFancyStructureP
#define CONTAINER_TYPE mfs
#include "list.h"
永远不要再写一个链表了。如果VALUE_TYPE总是一个指针,那么这是一个过度杀伤,因为void *也可以正常工作。但是通常存在非常小的结构,间接开销通常没有意义。此外,您还获得了类型检查(即,您可能不希望将链接的字符串列表与链接的双精度列表连接起来,即使两者都可以在void *链接列表中工作)。
答案 31 :(得分:0)
使用其他无意义的? :运算符初始化一个const变量
const int bytesPerPixel = isAlpha() ? 4 : 3;
答案 32 :(得分:0)
if(---------)
printf("hello");
else
printf("hi");
填写空白,以便输出中既不会出现hello也不会出现hi
ans:fclose(stdout)
答案 33 :(得分:0)
以下是一个示例,说明如何使C代码完全不知道用于运行应用程序的HW实际使用的内容。 main.c进行设置,然后可以在任何编译器/ arch上实现自由层。我认为抽象C代码有点简洁,所以它并不是特定的。
在此处添加完整的可编辑示例。
/* free.h */
#ifndef _FREE_H_
#define _FREE_H_
#include <stdio.h>
#include <string.h>
typedef unsigned char ubyte;
typedef void (*F_ParameterlessFunction)() ;
typedef void (*F_CommandFunction)(ubyte byte) ;
void F_SetupLowerLayer (
F_ParameterlessFunction initRequest,
F_CommandFunction sending_command,
F_CommandFunction *receiving_command);
#endif
/* free.c */
static F_ParameterlessFunction Init_Lower_Layer = NULL;
static F_CommandFunction Send_Command = NULL;
static ubyte init = 0;
void recieve_value(ubyte my_input)
{
if(init == 0)
{
Init_Lower_Layer();
init = 1;
}
printf("Receiving 0x%02x\n",my_input);
Send_Command(++my_input);
}
void F_SetupLowerLayer (
F_ParameterlessFunction initRequest,
F_CommandFunction sending_command,
F_CommandFunction *receiving_command)
{
Init_Lower_Layer = initRequest;
Send_Command = sending_command;
*receiving_command = &recieve_value;
}
/* main.c */
int my_hw_do_init()
{
printf("Doing HW init\n");
return 0;
}
int my_hw_do_sending(ubyte send_this)
{
printf("doing HW sending 0x%02x\n",send_this);
return 0;
}
F_CommandFunction my_hw_send_to_read = NULL;
int main (void)
{
ubyte rx = 0x40;
F_SetupLowerLayer(my_hw_do_init,my_hw_do_sending,&my_hw_send_to_read);
my_hw_send_to_read(rx);
getchar();
return 0;
}
答案 34 :(得分:-1)
在不使用任何运算符的情况下添加两个数字(a和b):
printf("%d", printf("%*s%*s",a,"\r",b,"\r") );
打印+ b。
答案 35 :(得分:-1)
我不知道这是不是一招。但是,当我在大学读大学时,我和我的朋友在我们的C ++入门课程中完成了一个实验室。我们不得不取一个人的名字并将其大写,然后将其展示回来,然后让他们选择“最后,第一次”显示他们的名字。对于本实验,我们被禁止使用数组表示法。
他向我展示了这段代码,我认为这是我当时看到的最酷的东西。
char * ptr = "first name";
//flies to the end of the array, regardless of length
while( *ptr++ );
答案 36 :(得分:-4)
填写空白以打印下面的“正确”和“错误”:
if(--------)
printf("correct");
else
printf("wrong");
答案是!printf("correct")