Using GNU C __attribute__

本文介绍了GNU C编译器的__attribute__机制,展示了如何使用__attribute__来标记变量、函数特性,如未使用、格式检查、无返回等,以增强代码可读性和错误检测。这些技巧在跨平台代码中尤其有效。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

One of the best (but little known) features of GNU C is the __attribute__ mechanism, which allows a developer to attach characteristics to function declarations to allow the compiler to perform more error checking. It was designed in a way to be compatible with non-GNU implementations, and we've been using this for years in highly portable code with very good results.

Table of Contents

  1. __attribute__ unused
  2. __attribute__ format
  3. __attribute__ noreturn
  4. __attribute__ const
  5. Putting them together
  6. Compatibility with non-GNU compilers
  7. Other References

Note that __attribute__ spelled with two underscores before and two after, and there are always two sets of parentheses surrounding the contents. There is a good reason for this - see below. Gnu CC needs to use the -Wall compiler directive to enable this (yes, there is a finer degree of warnings control available, but we are very big fans of max warnings anyway).

 

__attribute__ unused

One of the easiest attributes to use, this marks a variable as intentionally being possibly unused. Not only does this quiet the compiler from issuing an unused-variable warning, it tells the human the same thing: this is intentional.

Of course, it's a good idea to actually remove variables that you're not using, this is not always possible. A common case of the unused int argc parameter to main() is one, as are variables sometimes excluded by conditional compilation.

int main(int argc, char **argv)
{
   /* code that uses argv, but not argc */
}

$ gcc -W test.c
test.c:1: warning: unused parameter 'argc'

The __attribute__ is added just after the variable name, and though it can appear unwieldy, it's a style you can get used to:

int main(int argc __attribute__((unused)), char **argv)
{ ...

Additional uses shown, each with a comment showing the compiler warning it might have generated. Here we assume the code is being compiled without the DEBUG macro being defined, which excludes the actual use of mypid.

/* warning: 'someFunction' declared 'static' but never defined */
static int someFunction() __attribute__((unused));

int main(int argc __attribute__((unused)), char **argv)
{
/* warning: unused variable 'mypid' */
int	mypid __attribute__((unused)) = getpid();

#ifdef DEBUG
	printf("My PID = %d\n", mypid);
#endif

	return 0;
}

__attribute__ format

This __attribute__ allows assigning printf-like or scanf-like characteristics to the declared function, and this enables the compiler to check the format string against the parameters provided throughout the code. This is exceptionally helpful in tracking down hard-to-find bugs.

There are two flavors:

  • __attribute__((format(printf,m,n)))
  • __attribute__((format(scanf,m,n)))

but in practice we use the first one much more often.

The (m) is the number of the "format string" parameter, and (n) is the number of the first variadic parameter. To see some examples:

/* like printf() but to standard error only */
extern void eprintf(const char *format, ...)
	__attribute__((format(printf, 1, 2)));  /* 1=format 2=params */

/* printf only if debugging is at the desired level */
extern void dprintf(int dlevel, const char *format, ...)
	__attribute__((format(printf, 2, 3)));  /* 2=format 3=params */

With the functions so declared, the compiler will examine the argument lists

$ cat test.c
1  extern void eprintf(const char *format, ...)
2               __attribute__((format(printf, 1, 2)));
3
4  void foo()
5  {
6      eprintf("s=%s\n", 5);             /* error on this line */
7
8      eprintf("n=%d,%d,%d\n", 1, 2);    /* error on this line */
9  }

$ cc -Wall -c test.c
test.c: In function `foo':
test.c:6: warning: format argument is not a pointer (arg 2)
test.c:8: warning: too few arguments for format

Note that the "standard" library functions - printf and the like - are already understood by the compiler by default.

__attribute__ noreturn

This attribute tells the compiler that the function won't ever return, and this can be used to suppress errors about code paths not being reached. The C library functions abort() and exit() are both declared with this attribute:

extern void exit(int)   __attribute__((noreturn));
extern void abort(void) __attribute__((noreturn));

Once tagged this way, the compiler can keep track of paths through the code and suppress errors that won't ever happen due to the flow of control never returning after the function call.

In this example, two nearly-identical C source files refer to an "exitnow()" function that never returns, but without the __attribute__ tag, the compiler issues a warning. The compiler is correct here, because it has no way of knowing that control doesn't return.

$ cat test1.c
extern void exitnow();

int foo(int n)
{
        if ( n > 0 )
	{
                exitnow();
		/* control never reaches this point */
	}
        else
                return 0;
}

$ cc -c -Wall test1.c
test1.c: In function `foo':
test1.c:9: warning: this function may return with or without a value

But when we add __attribute__, the compiler suppresses the spurious warning:

$ cat test2.c
extern void exitnow() __attribute__((noreturn));

int foo(int n)
{
        if ( n > 0 )
                exitnow();
        else
                return 0;
}

$ cc -c -Wall test2.c
no warnings!

__attribute__ const

This attribute marks the function as considering only its numeric parameters. This is mainly intended for the compiler to optimize away repeated calls to a function that the compiler knows will return the same value repeatedly. It applies mostly to math functions that have no static state or side effects, and whose return is solely determined by the inputs.

In this highly-contrived example, the compiler normally must call the square() function in every loop even though we know that it's going to return the same value each time:

extern int square(int n) __attribute__((const));

...
	for (i = 0; i < 100; i++ )
	{
		total += square(5) + i;
	}

By adding __attribute__((const)), the compiler can choose to call the function just once and cache the return value.

In virtually every case, const can't be used on functions that take pointers, because the function is not considering just the function parameters but also the data the parameters point to, and it will almost certainly break the code very badly in ways that will be nearly impossible to track down.

Furthermore, the functions so tagged cannot have any side effects or static state, so things like getchar() or time() would behave very poorly under these circumstances.

Putting them together

Multiple __attributes__ can be strung together on a single declaration, and this is not uncommon in practice. You can either use two separate __attribute__s, or use one with a comma-separated list:

/* send printf-like message to stderr and exit */
extern void die(const char *format, ...)
	__attribute__((noreturn))
	__attribute__((format(printf, 1, 2)));

/*or*/

extern void die(const char *format, ...)
	__attribute__((noreturn, format(printf, 1, 2)));

If this is tucked away safely in a library header file, all programs that call this function receive this checking.

Compatibility with non-GNU compilers

Fortunately, the __attribute__ mechanism was cleverly designed in a way to make it easy to quietly eliminate them if used on platforms other than GNU C. Superficially, __attribute__ appears to have multiple parameters (which would typically rule out using a macro), but the two sets of parentheses effectively make it a single parameter, and in practice this works very nicely.

/* If we're not using GNU C, elide __attribute__ */
#ifndef __GNUC__
#  define  __attribute__(x)  /*NOTHING*/
#endif

Note that __attribute__ applies to function declarations, not definitions, and we're not sure why this is. So when defining a function that merits this treatment, an extra declaration must be used (in the same file):

/* function declaration */
void die(const char *format, ...) __attribute__((noreturn))
                                  __attribute__((format(printf,1,2)));

void die(const char *format, ...)
{
	/* function definition */
}

Other References

We'll note that there are many more attributes available, including those for variables and types, and they are not covered here: we have chosen to just touch on the high points. Those wishing more information can find it in the GNU online documentation at http://gcc.gnu.org:

GCC 4.0

GCC 4.0 Function Attributes
GCC 4.0 Variable Attributes
GCC 4.0 Type Attributes

GCC 3.2

GCC 3.2 Function Attributes
GCC 3.2 Variable Attributes
GCC 3.2 Type Attributes

GCC 3.1

GCC 3.1 Function Attributes
GCC 3.1 Variable Attributes
GCC 3.1 Type Attributes

GCC 3.0.4

Function Attributes
Variable Attributes
Type Attributes

GCC 2.95.3

Function Attributes
Variable Attributes
Type Attributes

 

http://www.unixwiz.net/techtips/gnu-c-attributes.html

Windows 系统修复工具主要用于解决 Windows 11/10 系统中的各种常见问题,具有操作简单、功能全面等特点: 文件资源管理器修复:可解决文件资源管理器卡死、崩溃、无响应等问题,能终止崩溃循环。还可修复右键菜单无响应或选项缺失问题,以及重建缩略图缓存,让图片、视频等文件的缩略图正常显示,此外,还能处理桌面缺少回收站图标、回收站损坏等问题。 互联网和连接修复:能够刷新 DNS 缓存,加速网页加载速度,减少访问延迟。可重置 TCP/IP 协议栈,增强网络连接稳定性,减少网络掉线情况,还能还原 Hosts 文件,清除恶意程序对网络设置的篡改,保障网络安全,解决电脑重装系统后网络无法连接、浏览器主页被篡改等问题。 系统修复:集成系统文件检查器(SFC),可自动扫描并修复受损的系统文件。能解决 Windows 激活状态异常的问题,还可重建 DLL 注册库,恢复应用程序兼容性,解决部分软件无法正常运行的问题,同时也能处理如 Windows 沙箱无法启动、Windows 将 JPG 或 JPEG 保存为 JFIF 等系统问题。 系统工具维护:提供启动管理器、服务管理器和进程管理器等工具,用户可控制和管理启动程序、系统服务和当前运行的进程,提高系统的启动和运行速度,防止不必要的程序和服务占用系统资源。还能查看系统规格,如处理器线程数、最大显示分辨率等。 故障排除:集成超过 20 个微软官方诊断工具,可对系统问题进行专业排查,还能生成硬件健康状态报告。能解决搜索和索引故障、邮件和日历应用程序崩溃、设置应用程序无法启动等问题,也可处理打印机、网络适配器、Windows 更新等相关故障。 其他修复功能:可以重置组策略设置、catroot2 文件夹、记事本等多种系统设置和组件,如重置 Windows 应用商店缓存、Windows 防火墙设置等。还能添加重建图标缓存支持,恢复粘滞便笺删除
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值