顯示具有 GCC 標籤的文章。 顯示所有文章
顯示具有 GCC 標籤的文章。 顯示所有文章

2021年5月23日 星期日

Ubuntu 14.04 使用 codeblock

Ref:https://www.itread01.com/p/160649.html 1、安裝 sudo apt-get install codeblocks codeblocks-common codeblocks-contrib wxformbuilder libwxbase3.0-0 libwxgtk3.0-0 wx-common wx3.0-headers wx3.0-i18n wx3.0-examples 2、基礎配置 http://wiki.codeblocks.org/index.php?title=Syntax_highlighting_custom_colour_themes 新增theme setting => syntax highlighting 配置theme setting => editor => margin and caret 配置游標顯示(預設為黑色,黑色theme時看不到) setting => editor => keyboard shortcuts 配置快捷鍵 setting => editor => code complete 配置程式碼自動完成 3、專案 file => new => project 建立專案 專案屬性 => build options 配置專案環境,tag,shared lib,標頭檔案等 file => new => file / class 新新增程式碼檔案 4、常用快捷鍵 ctrl + G 跳轉到某一行 alt + G 開啟某一檔案 ctrl + shift + G 跳轉到某一函式 ctrl + F9 編譯 ctrl + F10 執行 ctrl + F11 重新編譯 F9 編譯+執行 5、DEBUG Project > Set programs' arguments...

2020年12月27日 星期日

Handbook - GCC

Q: How to get assembler output from C/C++ source?
A:  REF https://stackoverflow.com/questions/137038/how-do-you-get-assembler-output-from-c-c-source-in-gcc
             gcc -S -o my_asm_output.s helloworld.c
             objdump -S --disassemble helloworld > helloworld.dump
             >>> Doesn't apply for clang-based compiler.


2019年8月18日 星期日

Pragma

#print a message from the C preprocessor?

Source: https://stackoverflow.com/questions/3826832/is-there-a-portable-way-to-print-a-message-from-the-c-preprocessor

The warning directive is probably the closest you'll get, but it's not entirely platform-independent:
#warning "C Preprocessor got here!"
AFAIK this works on most compilers except MSVC, on which you'll have to use a pragma directive:
#pragma message ( "C Preprocessor got here!" )

2019年2月6日 星期三

likely,unlikely宏與GCC內建函數__builtin_expect()

Source https://blog.csdn.net/mopyman/article/details/595302

在讀linux/kernel/fork.c的時候遇到了unlikely宏定義,一路追蹤,最後找到了GCC內建函數__builtin_expect(),查閱GCC手冊,發現其定義如下:
long __builtin_expect (long exp, long c)                                    [Built-in Function]
You may use __builtin_expect to provide the compiler with branch prediction
information. In general, you should prefer to use actual profile feedback for this
(‘-fprofile-arcs’), as programmers are notoriously bad at predicting how their
programs actually perform. However, there are applications in which this data is
hard to collect.
The return value is the value of exp, which should be an integral expression. The
value of c must be a compile-time constant. The semantics of the built-in are that it
is expected that exp == c. For example:
if (__builtin_expect (x, 0))
foo ();
would indicate that we do not expect to call foo, since we expect x to be zero. Since
you are limited to integral expressions for exp, you should use constructions such as
if (__builtin_expect (ptr != NULL, 1))
error ();
when testing pointer or floating-point values.
大致是說,由於大部分程序員在分支預測方面做得很糟糕,所以GCC提供了這個內建函數來幫助程序員處理分支預測,優化程序。其第一個參數exp為一個整型表達式,這個內建函數的返回值也是這個exp,而c為一個編譯期常量,這個函數的語義是:你期望exp表達式的值等於常量c,從而GCC為你優化程序,將符合這個條件的分支放在合適的地方。
因為這個程序只提供了整型表達式,所以如果你要優化其他類型的表達式,可以採用指針的形式。

unlikely的定義如下:
#define unlikely(x)  __builtin_expect(!!(x), 0)
也就是說我們期望表達式x的值為0,從而如果我們用
…….
if(unlikely(x)){
bar();
}來測試條件的話,我們就不期望bar()函數執行,所以該宏的名字用unlikely也就是不太可能來表示。
likely宏與次類似.
說到底__builtin_expect函數就是為了優化可能性大的分支程序。

Linux內核源碼特殊用法

Source http://ilinuxkernel.com/files/5/Linux_Kernel_Source_Code.htm

1      前言

Linux內核源碼主要以C語言為主,有一小部分涉及彙編語言,編譯器使用的是Gcc。初次看內核源碼,會遇到一些難以理解、晦澀的代碼;而恰恰是這些晦澀的代碼,在內核源碼中經常出現。把一些晦澀、常見的代碼看懂後,大家會發現看內核代碼越來越順利。
本文以x86_64架構中的Linux 2.6.32-71.el6RHEL 6)源碼為例,選擇一些經常出現且晦澀的源碼進行解釋,選擇的源碼雖以2.6.32-71.el6為例,但很多內容同樣使用其他版本的源碼。主要內容包括GccC語言的擴展用法、及其他一些雜項。

2      GccC語言的擴展用法

2.1      __attribute__

在我們看文件系統(File Sytems)或頁面緩存(Page Cache)管理內容時,會經常遇到struct address_space數據結構,其定義在include/linux/fs.h中。
00624: struct  address_space {
00625:       struct inode          *host;           / * owner: inode, block_device */
00626:       struct radix_tree_root  page_tree;   / * radix tree of all pages */
00627:       spinlock_t              tree_lock;    / * and lock protecting it */
00628:       unsigned int           i_mmap_writable;/ * count VM_SHARED mappings */
00629:       struct prio_tree_root    i_mmap;
00629: / * tree of private and shared mappings */
00630:       struct list_head     i_mmap_nonlinear;/ *list VM_NONLINEAR mappings */
00631:       spinlock_t              i_mmap_lock;     / * protect tree, count, list */
00632:       unsigned int           truncate_count / * Cover race condition with truncate */
00633:       unsigned long        nrpages;       / * number of total pages */
00634:       pgoff_t                  writeback_index;/ * writeback starts here */
00635:       const struct address_space_operations *a_ops;/ * methods */
00636:       unsigned long        flags;            / * error bits/ gfp mask */
00637:       struct backing_dev_info *backing_dev_info; / * device readahead, etc */
00638:       spinlock_t              private_lock;       / * for use by the address_space */
00639:       struct list_head     private_list; / * ditto */
00640:       struct address_space    *assoc_mapping;/ * ditto */
00641: __attribute__((aligned(sizeof(long))));

大家注意到,在結構體定義結束出__attribute__((aligned(sizeof(long))))
這句的作用是什麼?對結構體的定義有什麼影響?
對於關鍵字__attribute__,在標準的C語言中是沒有的。它是Gcc中對C語言的一個擴展用法。關鍵字__attribute__可以用來設置一個函數或數據結構定義的屬性。對一個函數設置屬性的主要目的是使編譯器對函數進行可能的優化。對函數設置屬性,是在函數原型定義中設置,如下面一個例子:
void fatal_error() __attribute__ ((noreturn));
. . .
void fatal_error(char *message)
{
fprintf(stderr,"FATAL ERROR: %s\n",message);
exit(1);
}

在這個例子中,noreturn屬性告訴編譯器,這個函數不返回給調用者,所以編譯器就可以忽略所有與執行該函數返回值有關的代碼。
可以在同一個定義中,設置多個屬性,各個屬性用逗號分開即可。如下面的定義就是告訴編譯器,它不改變全局變量和該函數不能擴展為內聯函數。
int getlim() __attribute__ ((pure,noinline));

屬性(attributes)也可以用來設置變量和結構體的成員。如,為了保證結構體中的一個成員變量與結構體有特殊方式的對齊(alignment),可以用以下形式定義:
struct mong {
char id;
int code __attribute__ ((align(4)));
};

address_space結構體中,顯然__attribute__是用來設置結構體struct address_space的,就是給該結構體設置一個屬性。設置什麼樣的屬性呢?該結構體的屬性是aligned(sizeof(long)) ,就是設置struct address_space結構體按sizeof(long)個字節對齊。
    這裡的屬性aligned的含義是:設置與內存地址對齊(alignment)的方式。如
     int alivalue __attribute__ ((aligned(32)));

變量alivalue的地址就是32字節對齊。對於我們內核源碼的例子,當然屬性有很多中,不僅僅是aligned,比如還有deprecatedpackedunused等。並且設置變量或結構體的屬性,與設置函數的屬性有所不同。
GCCC語言的擴展,更多內容請參考鏈接。http://gcc.gnu.org/onlinedocs/gcc/C-Extensions.html#C-Extensions
我們再來看一個實例代碼摘自linux/include/module.h
00083: #ifdef MODULE
00084: #define MODULE_GENERIC_TABLE(gtype,name)             \
00085: extern const struct gtype##_id     mod_##gtype##_table      \
00086:   __attribute__ ((unused, alias(__stringify(name))))
00087:
00088: extern struct module __this_module;
00089: #define THIS_MODULE (&   this_module )
00090: #else  / * ! MODULE */
00091: #define MODULE_GENERIC_TABLE(gtype,name)
00092: #define THIS_MODULE ((struct module *)0)
00093: #endif

注意到86行的__attribute__ ((unused, alias(__stringify(name))))。前面已經提到,可以為一個變量或函數設置多個屬性(attribute),各個屬性之間用逗號隔開。86行的宏有兩個屬性:unusedaliasunused使該類型的數據項顯示為未被使用的,這樣編譯時就不會產生任何告警信息;alias使該定義是其他符號的別名。如
void __f () { /* Do something. */; }
void f () __attribute__ ((weak, alias ("__f")));
定義“f”是“__f”的一個弱別名。

2.2      關鍵字替代

先看一段源碼,摘自include/linux/compiler-gcc.h
00010: / * Optimization barrier */
00011: / * The "volatile" is due to gcc bugs */
00012: #define barrier() __asm              __volatile__("": : :"memory")

在文件arch/x86/include/asm/msr.h另外一段代碼。
00076: static inline unsigned long long native_read_msr_safe(unsigned int msr,
00077:                                                int *err)
00078: {
00079:       DECLARE_ARGS(val, low, high);
00080:
00081:       asm volatile("2: rdmsr ; xor %[err],%[err]\n"
00082:                   "1:\n\t"
00083:                   ".section .fixup,\"ax\"\n\t"
00084:                   "3:  mov %[fault],%[err] ; jmp 1b\n\t"
00085:                   ".previous\n\t"
00086:                   _ASM_EXTABLE(2b, 3b)
00087:                   : [err] "=r" (*err), EAX_EDX_RET(val, low, high)
00088:                   : "c" (msr), [fault] "i" (- EIO));
00089:       return EAX_EDX_VAL(val, low, high);
00090: }
00091:

給出的兩段代碼都使用了嵌入式彙編。但不同的是關鍵字的形式不一樣。一個使用的是__asm__,另外一個是asm。事實上,兩者的含義都一樣。也就是__asm__等同於asm,區別在於編譯時,若使用了選項-std-ansi,則關閉了關鍵字asm,而其替代關鍵字__asm__仍然可以使用。
類似的關鍵字還有__typeof____inline__,其等同於typeofinline

2.3      typeof

在內核雙鏈表include/linux/kernel.h中,有以下一段代碼。該宏的具體含義,這裡不多作解釋,後面的章節會介紹。這裡我們關注一個關鍵字typeof
00669: / **
00670:  * container_of - cast a member of a structure out to the containing structure
00671:  * @ptr: the pointer to the member.
00672:  * @type:the type of the container struct this is embedded in.
00673:  * @member: the name of the member within the struct.
00674:  *
00675:  */
00676: #define container_of(ptr type member) ({             \
00677:       const typeof( ((type *)0)- >member ) *__mptr = (ptr) \
00678:       (type *)( (char *)__mptr - offsetof(type,member) );})
00679:

從字面意思上理解,typeof就是獲取其類型,其含義也正是如此。關鍵字typeof返回的是表達式的類型,使用上類似於關鍵字sizeof,但它的返回值是類型,而不是一個大小。下面是一些例子:
char *chptr; // A char pointer
typeof (*chptr) ch; // A char
typeof (ch) *chptr2; // A char pointer
typeof (chptr) chparray[10]; // Ten char pointers
typeof (*chptr) charray[10]; // Ten chars
typeof (ch) charray2[10]; // Ten chars

2.4      asmlinkage

asmlinkage在內核源碼中出現的頻率非常高,它是告訴編譯器在本地堆棧中傳遞參數,與之對應的是fastcallfastcall是告訴編譯器在通用寄存器中傳遞參數。運行時,直接從通用寄存器中取函數參數,要比在本地堆棧(內存)中取,速度快很多。
00492: / *
00493:  * sys_execve() executes a new program.
00494:  */
00495: asmlinkage
00496: lon sys_execve(char __user *name, char __user __user *argv,
00497:               char __user __user *envp, struct pt_regs *regs)
00498: {
00499:       long error;
00500:       char *filename;
00501:
00502:       filename = getname(name);
00503:       error = PTR_ERR(filename);
00504:       if (IS_ERR(filename))
00505:               return error;
00506:       error = do_execve(filename, argv, envp, regs);
00507:       putname(filename);
00508:       return error;
00509: }

fastcall的使用是和平台相關的,asmlinkagefastcall的定義都在文件arch/x86/include/asm/linkage.h中。
00009: #ifdef CONFIG_X86_32
00010: #define asmlinkage CPP_ASMLINKAGE   __attribute__((regparm(0)))
00011: / *
00012:  * For 32- bit UML - mark functions implemented in assembly that use
00013:  * regparm input parameters:
00014:  */
00015: #define asmregparm __attribute__((regparm(3)))

2.5      UL

UL通常用在一個常數的後面,標記為“unsigned long”。使用UL的必要性在於告訴編譯器,把這個常數作為長型數據對待。這可以避免在部分平台上,造成數據溢出。例如,在16位的整數可以表示的範圍為-32,768 ~ +32,767;一個無符號整型表示的範圍可以達到65,535。使用UL可以幫助當你使用大數或長的位掩碼時,寫出的代碼與平台無關。下面一段代碼摘自include/linux/hash.h
00017: #include  types.h>

00018:
00019: / * 2^31 + 2^29 - 2^25 + 2^22 - 2^19 - 2^16 + 1 */
00020: #define GOLDEN_RATIO_PRIME_32 0x9e370001UL
00021: / * 2^63 + 2^61 - 2^57 + 2^54 - 2^51 - 2^18 + 1 */
00022: #define GOLDEN_RATIO_PRIME_64 0x9e37fffffffc0001UL
00023:

2.6      constvolatile

關鍵字const的含義不能理解為常量,而是理解為“只讀”。如int const*x是一個指針,指向一個const整數。這樣,指針可以改變,但整數值卻不能改變。然而int *const x是一個const指針,指向整數,整數的值可以改變,但指針不能改變。下面代碼摘自fs/ext4/inode.c
00347: static int ext4_block_to_path(struct inode *inode,
00348:                           ext4_lblk_t i_block,
00349:                           ext4_lblk_t offsets[4], int *boundary)
00350: {
00351:       int ptrs = EXT4_ADDR_PER_BLOCK(inode- >i_sb);
00352:       int ptrs_bits = EXT4_ADDR_PER_BLOCK_BITS(inode- >i_sb);
00353:       const long direct_blocks = EXT4_NDIR_BLOCKS,
00354:               indirect_blocks = ptrs,
00355:               double_blocks = (1 << (ptrs_bits 2));

關鍵字volatile標記變量可以改變,而沒有告警信息。volatile告訴編譯器每次訪問時,該變量必須重新加載,而不是從拷貝或緩存中讀取。需要使用volatile的場合有,當我們處理中斷寄存器時,或者並發進程之間共享的變量。
task_struct結構體如下,包含volatileconst兩個特殊關鍵字。
01231: struct  task_struct {
01232:       volatile long state; / * - 1 unrunnable, 0 runnable, >0 stopped */
01233:       void *stack;
01234:       atomic_t usage;
01235:       unsigned int flags; / * per process flags, defined below */
01236:       unsigned int ptrace;
01237:
01238:       int lock_depth;           / * BKL lock depth */
01239:
01240: #ifdef CONFIG_SMP
01241: #ifdef __ARCH_WANT_UNLOCKED_CTXSW
01242:       int oncpu;
01243: #endif
01244: #endif
01245:
01246:       int prio, static_prio, normal_prio;
01247:       unsigned int rt_priority;
01248:       const struct sched_class *sched_class;


3      雜項

3.1      __volatile__

在嵌入式彙編代碼中,經常看到__volatile__修飾符,我們提到__volatile__volatile實際上是等同的,這裡不多作強調。__volatile__修飾符對彙編代碼非常重要。它告訴編譯器不要優化內聯的彙編代碼。通常,編譯器認為一些代碼是冗餘和浪費的,於是就試圖儘可能優化這些彙編代碼。

3.2      likely()unlikely()

unlikely()和likely()這兩個語句也很常見。先看mm/page_alloc.c中的函數__alloc_pages(),這個函數是內存管理中分配物理頁面的核心函數。
02100: / *
02101:  * This is the 'heart' of the zoned buddy allocator.
02102:  */
02103: struct page *
02104: __alloc_pages_nodemask(gfp_t  gfp_mask, unsigned int order,
02105:                     struct zonelist *zonelist, nodemask_t *nodemask)
02106: {
02107:       enum zone_type high_zoneidx = gfp_zone(gfp_mask);
02108:       struct zone *preferred_zone;
02109:       struct page *page;
02110:       int migratetype = allocflags_to_migratetype(gfp_mask);
02111:
02112:       gfp_mask &= gfp_allowed_mask ;
02113:
02114:       lockdep_trace_alloc(gfp_mask);
02115:
02116:       might_sleep_if(gfp_mask & __GFP_WAIT);
02117:
02118:       if (should_fail_alloc_page(gfp_mask, order))
02119:               return NULL;
02120:
02121:       / *
02122:        * Check the zones suitable for the gfp_mask contain at least one
02123:        * valid zone. It's possible to have an empty zonelist as a result
02124:        * of GFP_THISNODE and a memoryless node
02125:        */
02126:       if (unlikely(! zonelist- >_zonerefs- >zone))
02127:               return NULL;
02128:

注意到2126行的unlikely()語句。那麼unlikely()和likely()的含義是什麼?
linux內核源碼中,unlikely()和likely()是兩個宏,它告訴編譯器一個暗示。現代的CPU都有提前預測語句執行分支(branch-prediction heuristics)的功能,預測將要執行的指令,以優化執行速度。unlikely()和likely()通過編譯器告訴CPU,某段代碼是likely,應被預測;某段代碼是unlikely,不應被預測。likely()和unlikely()定義在include/linux/compiler.h
00106: # ifndef likely
00107:  define likely(x) (__builtin_constant_p(x) ? ! ! (x) : __branch_check__(x, 1))
00108: # endif
00109: # ifndef unlikely
00110:  define unlikely(x)  (__builtin_constant_p(x) ? ! ! (x) : __branch_check__(x, 0))
00111: # endif

3.3      IS_ERRPTR_ERR

許多內部的內核函數返回一個指針值給調用者,而這些函數中很多可能會失敗。在大部分情況下,失敗是通過返回一個NULL指針值來表示的。這種技巧有作用,但是它不能傳遞問題的確切性質。某些接口確實需要返回一個實際的錯誤編碼,以使調用者可以根據實際出錯的情況做出正確的決策。
許多內核接口通過把錯誤值編碼到一個指針值中來返回錯誤信息。這種函數必須小心使用,因為他們的返回值不能簡單地和NULL比較。為了幫助創建和使用這種類型的接口,中提供了一小組函數。
void *ERR_PTR(long error);
這裡error是通常的負的錯誤編碼。調用者可以使用IS_ERR來檢查所返回的指針是否是一個錯誤編碼:
long IS_ERR(const void* ptr);
如果需要實際的錯誤編碼,可以通過以下函數把它提取出來:
long PTR_ERR(const void* ptr);

應該只有在IS_ERR對某值返回真值時才對該值使用PTR_ERR,因為任何其他值都是有效的指針。

3.4      __init,__initdata,__exit,__exitdata

先看linux內核啟動時的一段代碼,摘自init/main.c
00541: asmlinkage void __init  start_kernel(void)
00542: {
00543:       char command_line;
00544:       extern struct kernel_param __start      param[],
  __stop   param[];
00545:
00546:       smp_setup_processor_id();
00547:
00548:       / *
00549:        * Need to run as early as possible, to initialize the
00550:        * lockdep hash:
00551:        */
00552:       lockdep_init();
00553:       debug_objects_early_init();
00554:
00555:       / *
00556:        * Set up the the initial canary ASAP:
00557:        */
00558:       boot_init_stack_canary();
00559:
00560:       cgroup_init_early();
00561:
00562:       local_irq_disable();
00563:       early_boot_irqs_off();
00564:       early_init_irq_lock_class();
00565:
00566: / *
00567:  * Interrupts are still disabled. Do necessary setups, then
00568:  * enable them
00569:  */

函數start_kernel()有個修飾符__init__init實際上是一個宏,只有在linux內核初始化是執行的函數或變量前才使用__init。編譯器將標記為__init的代碼段存放在一個特別的內存區域裡,這個區域在系統初始化後,就會釋放。
同理,__initdata用來標記只在內核初始化使用的數據,__exit__exitdata用來標記結束或關機的例程。這些通常在設備驅動卸載時使用。

3.5      內核源碼語法檢查

看進程管理內容時,do_fork()的源碼是必讀的。我們注意到do_fork()最後兩個參數前,都有__user修飾符。那麼這麼修飾符的含義和用處是怎樣的?摘自kernel/fork.c
01397: lon do_fork(unsigned long clone_flags,
01398:             unsigned long  stack_start,
01399:             struct pt_regs *regs,
01400:             unsigned long  stack_size,
01401:             int __user *parent_tidptr,
01402:             int __user *child_tidptr)
01403: {
01404:       struct task_struct *p;
01405:       int trace = 0;
01406:       long nr;
01407:
01408:       / *
01409:        * Do some preliminary argument and permissions checking before we
01410:        * actually start allocating stuff
01411:        */
01412:       if (clone_flags & CLONE_NEWUSER) {
01413:               if (clone_flags & CLONE_THREAD)
01414:                     return - EINVAL;
01415:               / * hopefully this check will go away when userns support is
01416:               * complete
01417:               */
01418:               if (! capable(CAP_SYS_ADMIN) | ! capable(CAP_SETUID) ||
01419:                            ! capable(CAP_SETGID))
01420:                     return - EPERM;
01421:       }

先來看__user的在include/linux/compiler.h中的定義:
00006: #ifdef     CHECKER     
00007: # define __user          __attribute__((noderef, address_space(1)))
00008: # define __kernel / * default address space */
00009: # define __safe           __attribute__((safe))
00010: # define __force  __attribute__((force))
00011: # define __nocast__attribute__((nocast))
00012: # define __iomem      __attribute__((noderef, address_space(2)))
00013: # define __acquires(x)      __attribute__((context(x,0,1)))
00014: # define __releases(x)__attribute__((context(x,1,0)))
00015: # define __acquire(x) __context__(x,1)
00016: # define __release(x) __context__(x,- 1)
00017: # define __cond_lock(x,c) ((c) ? ({ __acquire(x); 1; }) : 0)
00018: extern void __chk_user_ptr(const volatile void __user *);
00019: extern void __chk_io_ptr(const volatile void __iomem *);
00020: #else
00021: # define __user
00022: # define __kernel
00023: # define __safe
00024: # define __force
00025: # define __nocast
00026: # define __iomem
00027: # define __chk_user_ptr(x) (void)0
00028: # define __chk_io_ptr(x) (void)0
00029: # define __builtin_warning(x y...) (1)
00030: # define __acquires(x)
00031: # define __releases(x)
00032: # define __acquire(x) (void)0
00033: # define __release(x) (void)0
00034: # define __cond_lock(x,c) (c)
00035: #endif

通過其定義,似乎Gcc中現在還沒有支持這個用法。通過字面意思理解,__user很顯然是告訴它是一個用戶數據。雖然Gcc還不支持這種用法,但借助適當的工具,就可以在內核編譯時就可以發現內核源碼中的一些錯誤;如前面的__user,若編譯時發現傳遞進來的不是用戶數據,那麼就產生告警。
__user定義中,我們發現還有__kernel__safe__force__iomem,這些都是用來做內核源碼語法檢查的;其中__iomem在驅動代碼中很常見。
目前內核社區使用SPARSE工具來做內核源碼的檢查。SPARSE是語法分析器,能在編譯器前端發現源碼的語法。它能檢查ANSI C以及很多Gcc的擴展。SPASE提供一系列標記來傳遞語法信息,如地址空間的類型、函數所需獲取或釋放的鎖等。


Source https://blog.csdn.net/leeshuheng/article/details/5800377
__pure函數是指除了作一些運算以外(例如:strlen(...))沒有其他作用,函數的返回值只影響調用函數的auto變量和按值傳遞的參數。
__const函數是更嚴格的__pure函數,它的返回值只影向調用函數的按值傳遞的參數。
__noreturn函數是指該函數一定會調用類似於exit(int)的函數終止進程。必須謹慎使用該擴展。
__deprecated函數是指該函數已經廢棄或應謹慎使用。在調用__deprecate前綴的函數時,編譯器會給出警告。
__must_check函數是指調用函數一定要處理該函數的返回值,否則編譯器會給出警告。
__used函數是告訴編譯器必須編譯該函數,即使在可見的代碼範圍內從沒使用過此函數。
其餘的幾個屬性都很容易理解了,不贅述了。