主頁 > 後端開發 > Linux kernel中常見的宏整理

Linux kernel中常見的宏整理

2020-11-11 05:30:14 後端開發

0x00 宏的基本知識

// object-like
#define 宏名 替換串列 換行符
//function-like
#define 宏名 ([識別符號串列]) 替換串列 換行符

替換串列和識別符號串列都是將字串 token 化以后的串列,區別在于識別符號串列使用,作為不同引數之間的分割符,每一個引數都是一個 token 化的串列,在宏中空白符只起到分割 token 的作用,空白符的多少對于前處理器是沒有意義的,

宏的一些奇技淫巧:

https://gaomf.cn/2017/10/06/C_Macro/

以下是整理的一些linux kernel中的常見宏,由于不同體系架構,或者不同模塊的宏定義不同,只挑選了其中容易看懂的宏作為記錄,實作的功能大體一樣,

Linux內核中do{...}while(0)意義:

  • 輔助定義復雜的宏,避免參考的時候出錯,如果不用{},if后面的陳述句只有第一條進行了判斷,同時避免宏展開后“;”造成編譯不通過.

  • 避免使用goto,對程式流進行統一的控制,使用break跳出

  • 避免空宏引起的warning

  • 定義一個單獨的函式塊來實作復雜的操作

0x01 常見宏整理

__CONCAT宏

"##"用于粘貼兩個引數,"#"用于替換引數:

#define __CONCAT(a, b) a ## b

BUG_ON(condition)

條件為真,產生崩潰, 原理:未定義的例外,

相對應的有 WARN_ON:

#define BUG() assert(0)
#define BUG_ON(x) assert(!(x))
 
/* Does it make sense to treat warnings as errors? */
#define WARN() BUG()
#define WARN_ON(x) (BUG_ON(x), false)

BUILD_BUG_ON宏

#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))
  1. condition為真時,sizeof(char[-1]),產生錯誤,編譯不通過

  2. condition為假時,sizeof(char[1]),編譯通過

檢查運算式e是否為0,為0編譯通過且回傳0;如果不為0,則編譯不通過,

struct { int : –!!(0); } -=> struct { int : 0; }

如果e為0,則該結構體擁有一個int型的資料域,并且規定它所占的位的個數為0,

struct { int : –!!(1); } -=> struct { int : –1; }

如果e非0,結構體的int型資料域的位域將變為一個負數,產生語法的錯誤,

typeof獲得x的變數型別,根據傳入引數型別的不同,產生不同的行為,實作“編譯時多型”,實際typeof是在預編譯時處理,最后實際轉化為資料型別被編譯器處理,

所以其中的運算式在運行時是不會被執行的,比如typeof(fun()),fun()函式是不會被執行的,typeof只是在編譯時分析得到了fun()的回傳值而已,

typeof還有一些局限性,其中的變數是不能包含存盤類說明符的,如static、extern這類都是不行的,

typecheck宏

宏typecheck用于檢查x是否為type型別,如果不是會拋出(warning: comparison of distinct pointer types lacks a cast),typecheck_fn用于檢查函式function是否為type型別,不一致跑出(warning: initialization from incompatible pointer type),

/*
 * Check at compile time that something is of a particular type.
 * Always evaluates to 1 so you may use it easily in comparisons.
 */
#define typecheck(type,x) \
({ type __dummy; \
    typeof(x) __dummy2; \
    (void)(&__dummy == &__dummy2); \
    1; \
})
/*GCC的一個擴展特性,形如({ ... })這樣的代碼塊會被視為一條陳述句,
* 其計算結果是{ ... }中最后一條陳述句的計算結果,
* 所以上述會回傳1
*/
/*
 * Check at compile time that 'function' is a certain type, or is a pointer
 * to that type (needs to use typedef for the function type.)
 */
#define typecheck_fn(type,function) \
({ typeof(type) __tmp = function; \
    (void)__tmp; \
})

min宏

通過type進行隱式轉換安全通過編譯,否則會跑出warning:

#define min(x, y) __careful_cmp(x, y, <)
#define __cmp(x, y, op) ((x) op (y) ? (x) : (y))
#define __safe_cmp(x, y) \
        (__typecheck(x, y) && __no_side_effects(x, y))
#define __no_side_effects(x, y) \
        (__is_constexpr(x) && __is_constexpr(y))
 
#define __cmp_once(x, y, unique_x, unique_y, op) ({ \
        typeof(x) unique_x = (x); \
        typeof(y) unique_y = (y); \
        __cmp(unique_x, unique_y, op); })
/*重新賦值為了防止x++這種重復+1 */
#define __careful_cmp(x, y, op) \
    __builtin_choose_expr(__safe_cmp(x, y), \ //比較x, y的型別
        __cmp(x, y, op), \ //x,y型別一樣時
        __cmp_once(x, y, __UNIQUE_ID(__x), __UNIQUE_ID(__y), op))
          //x, y型別不同時

__UNIQUE_ID保證變數唯一,

__is_constexpr宏

判斷x是否為整數常量運算式:

/*
 * This returns a constant expression while determining if an argument is
 * a constant expression, most importantly without evaluating the argument.
 * Glory to Martin Uecker <[email protected]>
 */
#define __is_constexpr(x) \
    (sizeof(int) == sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8)))

如果x是常量運算式,則(void )((long)(x) 0l)是一個空指標常量,就會使用第三個運算元即((int *)8)的型別,如果不是常量運算式,則會使用第二個運算元void型別,

所以會出現以下兩種情況:

sizeof(int) == sizeof(*((int *) (NULL))) // if `x` was an integer constant expression
sizeof(int) == sizeof(*((void *)(....))) // otherwise

因為sizeof(void) = 1,所以如果x是整數常量運算式,則宏的結果為1,否則為0,

https://stackoverflow.com/questions/49481217/linux-kernels-is-constexpr-macro

描述:此函式為GNU擴展,用來判斷兩個型別是否相同,如果type_a與 type_b相同的話,就會回傳1,否則的話,回傳0,

int __builtin_choose_expr(exp, e1, e2);

max宏

同min 宏,

roundup宏

回傳一個能夠整除y并且大于x,最接近x的值,向上取整,可用于地址的記憶體對齊:

#define roundup(x, y) ( \
{ \
    const typeof(y) __y = y; \
    (((x) + (__y - 1)) / __y) * __y; \
} \
)

clamp 宏

判斷val是否在lo和hi的范圍內,如果小于lo,回傳lo,如果大于hi則回傳hi,如果在lo和hi之間就回傳val:

/**
 * clamp - return a value clamped to a given range with strict typechecking
 * @val: current value
 * @lo: lowest allowable value
 * @hi: highest allowable value
 *
 * This macro does strict typechecking of @lo/@hi to make sure they are of the
 * same type as @val. See the unnecessary pointer comparisons.
 */
#define clamp(val, lo, hi) min((typeof(val))max(val, lo), hi)

abs宏

取絕對值:

/**
 * abs - return absolute value of an argument
 * @x: the value. If it is unsigned type, it is converted to signed type first.
 * char is treated as if it was signed (regardless of whether it really is)
 * but the macro's return type is preserved as char.
 *
 * Return: an absolute value of x.
 */
#define abs(x) __abs_choose_expr(x, long long, \
        __abs_choose_expr(x, long, \
        __abs_choose_expr(x, int, \
        __abs_choose_expr(x, short, \
        __abs_choose_expr(x, char, \
        __builtin_choose_expr( \
            __builtin_types_compatible_p(typeof(x), char), \
            (char)({ signed char __x = (x); __x<0?-__x:__x; }), \
            ((void)0)))))))
 
#define __abs_choose_expr(x, type, other) __builtin_choose_expr( \
    __builtin_types_compatible_p(typeof(x), signed type) || \
    __builtin_types_compatible_p(typeof(x), unsigned type), \
    ({ signed type __x = (x); __x < 0 ? -__x : __x; }), other)

swap 宏

利用typeof獲取要交換變數的型別:

/*
 * swap - swap value of @a and @b
 */
#define swap(a, b) \
    do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0)

container_of宏

根據一個結構體變數中的成員變數來獲取整個結構體變數的指標,

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
/*結構體地址為0,將member地址轉成size_t型別作為偏移
/**
 * container_of - cast a member of a structure out to the containing structure
 * @ptr: the pointer to the member.
 * @type: the type of the container struct this is embedded in.
 * @member: the name of the member within the struct.
 *
 */
#define container_of(ptr, type, member) ({ \
    const typeof( ((type *)0)->member ) *__mptr = (ptr); \ //*__mptr保存該member變數的指標
    (type *)( (char *)__mptr - offsetof(type,member) );}) //變數指標減去自身偏移得到指向結構體的指標

likely和unlikely宏

把分支預測的資訊提供給編譯器,以降低因為指令跳轉帶來的分支下降:

#define likely(x) __builtin_exp ect(!!(x), 1)
#define unlikely(x) __builtin_exp ect(!!(x), 0)

GCC的內建方法會判斷 EXP == C 是否成立,成立則將if分支中的執行陳述句緊跟放在匯編跳轉指令之后,否則將else分支中的執行陳述句緊跟匯編跳轉指令之后,

這樣cache在預取資料時就可以將分支后的執行陳述句放在cache中,提高cache的命中率,

ALIGN對齊宏

對齊是采用上對齊的方式,例如0x123以16對齊,結果是0x130,因為對齊常在分配記憶體時使用,所以分配的要比需要的大,

#define ALIGN(x, a) __ALIGN_KERNEL((x), (a))
#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1)
#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask))
#define __ALIGN_MASK(x, mask) __ALIGN_KERNEL_MASK((x), (mask))

__get_unaligned_le(ptr)宏

獲取未對齊的資料,主要是識別資料大小:

#define __get_unaligned_le(ptr) ((__force typeof(*(ptr)))({ \
    __builtin_choose_expr(sizeof(*(ptr)) == 1, *(ptr), \
    __builtin_choose_expr(sizeof(*(ptr)) == 2, get_unaligned_le16((ptr)), \
    __builtin_choose_expr(sizeof(*(ptr)) == 4, get_unaligned_le32((ptr)), \
    __builtin_choose_expr(sizeof(*(ptr)) == 8, get_unaligned_le64((ptr)), \
    __bad_unaligned_access_size())))); \
 }))
 
 static inline u32 get_unaligned_be32(const void *p)
{
    return __get_unaligned_cpu32((const u8 *)p);
}
 
static inline u32 __get_unaligned_cpu32(const void *p)
{
    const struct __una_u32 *ptr = (const struct __una_u32 *)p;
    return ptr->x;
}
 
struct __una_u16 { u16 x; } __packed;
struct __una_u32 { u32 x; } __packed;
struct __una_u64 { u64 x; } __packed;

編譯器默認會對結構體采用位元組對齊的方式,__packed關鍵字可以取消位元組對齊,采用1位元組對齊,

__put_unaligned_le宏

寫入未對齊的資料,

#define __put_unaligned_le(val, ptr) ({ \
    void *__gu_p = (ptr); \
    switch (sizeof(*(ptr))) { \
    case 1: \
        *(u8 *)__gu_p = (__force u8)(val); \
        break; \
    case 2: \
        put_unaligned_le16((__force u16)(val), __gu_p); \
        break; \
    case 4: \
        put_unaligned_le32((__force u32)(val), __gu_p); \
        break; \
    case 8: \
        put_unaligned_le64((__force u64)(val), __gu_p); \
        break; \
    default: \
        __bad_unaligned_access_size(); \
        break; \
    } \
    (void)0; })
 
 static inline void put_unaligned_be32(u32 val, void *p)
{
    __put_unaligned_cpu32(val, p);
}
 
static inline void __put_unaligned_cpu32(u32 val, void *p)
{
    struct __una_u32 *ptr = (struct __una_u32 *)p;
    ptr->x = val;
}

ACCESS_ONCE 宏

訪問目標地址一次,先取得x的地址,然后把這個地址轉換成一個指向這個地址型別的指標,然后再取得這個指標所指向的內容,達到了訪問一次的目的,volatile表示不進行優化,強制訪問一次,

在一些并發的場景中對變數進行優化有可能導致錯誤,需要時刻得到變數的最新值,所以用volatile強制訪問一次進行更新,

使用 ACCESS_ONCE() 的兩個條件是:

  • 在無鎖的情況下訪問全域變數

  • 對該變數的訪問可能被編譯器優化成合并成一次或者拆分成多次

#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))

https://blog.csdn.net/ganggexiongqi/article/details/24603363

ACCESS_OK宏

CVE-2017-5123(waitid系統呼叫),檢查指標是不是屬于用戶空間的,x86架構下ACCESS_OK宏的實作:

/**
 * access_ok: - Checks if a user space pointer is valid
 * @addr: User space pointer to start of block to check
 * @size: Size of block to check
 *
 * Context: User context only. This function may sleep if pagefaults are
 * enabled.
 *
 * Checks if a pointer to a block of memory in user space is valid.
 *
 * Returns true (nonzero) if the memory block may be valid, false (zero)
 * if it is definitely invalid.
 *
 * Note that, depending on architecture, this function probably just
 * checks that the pointer is in the user space range - after calling
 * this function, memory access functions may still return -EFAULT.
 */
#define access_ok(addr, size) \
({ \
    WARN_ON_IN_IRQ(); \
    likely(!__range_not_ok(addr, size, user_addr_max())); \
})
/*__range_not_ok回傳0才能驗證通過
 
#define __range_not_ok(addr, size, limit) \
({ \
    __chk_user_ptr(addr); \
    __chk_range_not_ok((unsigned long __force)(addr), size, limit); \
})
 
/*
 * Test whether a block of memory is a valid user space address.
 * Returns 0 if the range is valid, nonzero otherwise.
 */
static inline bool __chk_range_not_ok(unsigned long addr, unsigned long size, unsigned long limit)
{
    /*
     * If we have used "sizeof()" for the size,
     * we know it won't overflow the limit (but
     * it might overflow the 'addr', so it's
     * important to subtract the size from the
     * limit, not add it to the address).
     */
    if (__builtin_constant_p(size))
        return unlikely(addr > limit - size);
    /*__builtin_constant_p判斷編譯時是否為常數,如果是則回傳1 */
    /* Arbitrary sizes? Be careful about overflow */
    addr += size;
    if (unlikely(addr < size))
        return true;
    return unlikely(addr > limit);
}

mdelay宏

忙等待函式,在延遲程序中無法運行其他任務,會占用CPU時間,延遲時間是準確的,

msleep是休眠函式,它不涉及忙等待.用msleep(200)的時候實際上延遲的時間,大部分時候是要多于200ms,是個不定的時間值,

#define MAX_UDELAY_MS 5
#define mdelay(n) (\ /*延遲毫秒級*/
    (__builtin_constant_p(n) && (n)<=MAX_UDELAY_MS) ? udelay((n)*1000) : \
    ({unsigned long __ms=(n); while (__ms--) udelay(1000);}))
 
static void udelay(int loops) /*延遲微秒級 */
{
    while (loops--)
        io_delay(); /* Approximately 1 us */
}
 
static inline void io_delay(void)
{
    const u16 DELAY_PORT = 0x80;
    asm volatile("outb %%al,%0" : : "dN" (DELAY_PORT));
}
/*對 I/O 埠 0x80 寫入任何的位元組都將得到 1 us 的延時*/

系統呼叫宏

linux 內核中最常見的宏使用之一,系統呼叫:

#define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__)
#define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)
/*…:省略號代表可變的部分,用__VA_AEGS__ 代表省略的變長部分*/
#define SYSCALL_DEFINE_MAXARGS    6  /*系統呼叫最多可以帶6個引數*/

以open系統呼叫為例:

SYSCALL_DEFINE

后面跟系統呼叫所帶的引數個數n,第一個引數為系統呼叫的名字,然后接2*n個引數,每一對指明系統呼叫的引數型別及名字,

SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
{
    if (force_o_largefile())
        flags |= O_LARGEFILE;
 
    return do_sys_open(AT_FDCWD, filename, flags, mode);
}

SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
展開之后是:
SYSCALL_DEFINEx(3, _open, __VA_ARGS__)

再次展開為:

__SYSCALL_DEFINEx(3, _open, __VA_ARGS__)
#define __SYSCALL_DEFINEx(x, name, ...) \
    asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) \

最后展開為:

asmlinkage long sys_open(__MAP(3,__SC_DECL,__VA_ARGS__))
 
#define __MAP0(m,...)
#define __MAP1(m,t,a) m(t,a)
#define __MAP2(m,t,a,...) m(t,a), __MAP1(m,__VA_ARGS__)
#define __MAP3(m,t,a,...) m(t,a), __MAP2(m,__VA_ARGS__)
#define __MAP4(m,t,a,...) m(t,a), __MAP3(m,__VA_ARGS__)
#define __MAP5(m,t,a,...) m(t,a), __MAP4(m,__VA_ARGS__)
#define __MAP6(m,t,a,...) m(t,a), __MAP5(m,__VA_ARGS__)
#define __MAP(n,...) __MAP##n(__VA_ARGS__)
 
#define __SC_DECL(t, a) t a
 
__MAP(3,__SC_DECL,__VA_ARGS__)
-->__MAP3(__SC_DECL,const char __user *, filename, int, flags, umode_t, mode)
-->__SC_DECL(const char __user *, filename), __MAP2(__SC_DECL,__VA_ARGS__)
-->const char __user * filename,__SC_DECL(int, flags),__MAP1(__SC_DECL,__VA_ARGS__)
-->const char __user * filename, int flags, __SC_DECL(umode_t, mode)
-->const char __user * filename, int flags, umode_t mode

最后呼叫asmlinkage long sys_open(const char __user *filename,int flags, umode_t mode);

為什么要將系統呼叫定義成宏?CVE-2009-0029,CVE-2010-3301,Linux 2.6.28及以前版本的內核中,將系統呼叫中32位引數傳入64位的暫存器時無法作符號擴展,可能導致系統崩潰或提權漏洞,

內核開發者通過將系統呼叫的所有輸入引數都先轉化成long型別(64位),再強制轉化到相應的型別來規避這個漏洞,

asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \
{ \
        long ret = __do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\
        __MAP(x,__SC_TEST,__VA_ARGS__); \
        __PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \
        return ret; \
} \
 
 
#define __TYPE_AS(t, v) __same_type((__force t)0, v) /*判斷t和v是否是同一個型別*/
#define __TYPE_IS_L(t) (__TYPE_AS(t, 0L)) /*判斷t是否是long 型別,是回傳1*/
#define __TYPE_IS_UL(t) (__TYPE_AS(t, 0UL)) /*判斷t是否是unsigned long 型別,是回傳1*/
#define __TYPE_IS_LL(t) (__TYPE_AS(t, 0LL) || __TYPE_AS(t, 0ULL))/*是long型別就回傳1*/
#define __SC_LONG(t, a) __typeof(__builtin_choose_expr(__TYPE_IS_LL(t), 0LL, 0L)) a
/*將引數轉換成long型別*/
#define __SC_CAST(t, a) (__force t) a /*轉成成原來的型別*/

# define __force __attribute__((force))

表示所定義的變數型別可以做強制型別轉換

barrier()宏

記憶體屏障,該陳述句不產生任何代碼,但是執行后重繪暫存器對變數的分配,

/* Optimization barrier */
/* The "volatile" is due to gcc bugs */
#define barrier() __asm__ __volatile__("": : :"memory")

執行該陳述句后cpu中的暫存器和cache中已快取的資料將作廢,重新讀取記憶體中的資料,這就阻止了cpu將暫存器和cache中的資料用于去優化指令,而避免去訪問記憶體,例如:

int a = 5, b = 6;
barrier();
a = b;

第三行中,GCC不會用存放b的暫存器給a賦值,而是invalidate b 的cache line,重新讀取記憶體中的b值給a賦值,

另外的記憶體屏障宏定義:

  • mfence:在mfence指令前的讀寫操作當必須在mfence指令后的讀寫操作前完成,

  • lfence:在lfence指令前的讀操作當必須在lfence指令后的讀操作前完成,不影響寫操作

  • sfence:在sfence指令前的寫操作當必須在sfence指令后的寫操作前完成,不影響讀操作

  • lock 前綴(或cpuid、xchg等指令)使得本CPU的Cache寫入記憶體,該寫入動作也會引起別的CPU invalidate其Cache,用來修飾當前指令操作的記憶體只能由當前CPU使用

記憶體對于快取更新策略,要區分Write-Through和Write-Back兩種策略,前者更新內容直接寫記憶體并不同時更新Cache,但要置Cache失效,后者先更新Cache,隨后異步更新記憶體,通常X86 CPU更新記憶體都使用Write-Back策略,

ifdef ASSEMBLY宏

一些常量宏同時在匯編和C中使用,然而,我們不能像注釋C的常量宏那樣加一個“UL”或其他后綴,所以我們需要使用以下的宏解決這個問題,

例如呼叫:#define DEMO_MACRO _AT(1, UL):在C中會被解釋為 #define DEMO_MACRO 1UL; 而在匯編中什么都不做,就是:#define DEMO_MACRO 1

#ifdef __ASSEMBLY__
#define _AC(X,Y) X
#define _AT(T,X) X
#else
#define __AC(X,Y) (X##Y)
#define _AC(X,Y) __AC(X,Y)
#define _AT(T,X) ((T)(X))
#endif
 
#define _UL(x) (_AC(x, UL))
#define _ULL(x) (_AC(x, ULL))

force_o_largefile宏

判斷是否支持大檔案,

define force_o_largefile() \

    (personality(current->personality) != PER_LINUX32)

PER_LINUX32 = 0x0008,
PER_MASK = 0x00ff,
/*,

  • Return the base personality without flags.
    */

define personality(pers) (pers & PER_MASK)

邏輯地址和物理地址互相轉換

#define __pa(x) __virt_to_phys((unsigned long)(x))
#define __va(x) ((void *)__phys_to_virt((unsigned long)(x)))

錯誤碼相關的宏

linux 內核的一些錯誤碼,以它們的負數來作為函式回傳值,簡單地使用大于等于-4095的虛擬地址來分別表示相應的錯誤碼,

在32位系統上,-4095轉換成unsigned long型別的值為0xFFFFF001,也就是說地址區間[0xFFFFF001, 0xFFFFFFFF]被分別用來表示錯誤碼從-4095到-1,

判斷一個函式回傳的指標到底是有效地址還是錯誤碼:

#define MAX_ERRNO 4095
 
#define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)
 
static inline long __must_check IS_ERR(const void *ptr)
{
    return IS_ERR_VALUE((unsigned long)ptr);
}

錯誤碼與相應地址的互換:

static inline void * __must_check ERR_PTR(long error)
{
    return (void *) error;
}

長整型轉化為指標

static inline long __must_check PTR_ERR(const void *ptr)
{
    return (long) ptr;
}

指標轉化為長整型

額外有意思的宏

遞回宏,顛倒位元組:

#define BSWAP_8(x) ((x) & 0xff)
#define BSWAP_16(x) ((BSWAP_8(x) << 8) | BSWAP_8((x) >> 8))
#define BSWAP_32(x) ((BSWAP_16(x) << 16) | BSWAP_16((x) >> 16))
#define BSWAP_64(x) ((BSWAP_32(x) << 32) | BSWAP_32((x) >> 32))

交換宏,不需要額外定義變數

#define swap(a, b) \
(((a) ^= (b)), ((b) ^= (a)), ((a) ^= (b)))

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/210188.html

標籤:C

上一篇:C語言復習上

下一篇:冒泡排序

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more