报错storage size of ‘act’ isn’t known当使用std=c99编译struct sigaction

时间:2022-09-12 17:05:31

问题


今天在学习进程间通信之-信号signal–linux内核剖析(九)
遇见了一个奇怪的问题

storage size of ‘oldact’ isn’t known

于是FQ去google之。

分析了好久,终于发现问题的原因了。于是记录下来

发现


测试的代码如下


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

#include <signal.h>

#include <bits/sigaction.h>
int main()
{
struct sigaction act;
return 0;
}
  • gcc test.c按照默认进行编译,无错误

报错storage size of ‘act’ isn’t known当使用std=c99编译struct sigaction

  • gcc test.c -std=gnu99gcc test.c -std=gnu9x无错

报错storage size of ‘act’ isn’t known当使用std=c99编译struct sigaction

  • gcc test.c -std=c99出错

报错storage size of ‘act’ isn’t known当使用std=c99编译struct sigaction

  • gcc test.c --ansi出错

报错storage size of ‘act’ isn’t known当使用std=c99编译struct sigaction

原因解析


参见 sigaction() and ANSI C

======
because sigaction is a POSIX system call.
Maybe so, but it isn’t part of ANSI-C

Generally, you need to isolate system dependencies in as few places as possible, so that most of your code can be compiled with -ansi
If you dance barefoot on the broken glass of undefined behaviour, you’ve got to expect the occasional cut.
If at first you don’t succeed, try writing your phone number on the exam paper.

======
Am I right in thinking then that -ansi sets something that makes the preprocessor ignore the non ANSI standard parts of signal.h?
Yes.
Things which are not in ANSI-C get excluded when you specify -ansi, simply because they’re not guaranteed to be everywhere else. Regard -ansi as a strict portability checker.

because it is required to run on several different flavours of UNIX.
Perhaps you should relax the test a little bit, down to POSIX compatibility. That should allow you to port your code easily to most unices (plural of unix), and still use sigaction() since it is part of POSIX.

If you can’t manage ANSI, at least POSIX is a good 2nd choice to go for.

If you have many source files, you should be able to arrange for nearly all of them to be compiled with -ansi, and just a few which rely on POSIX. That way, any porting surprises you do have will be localised in just a few places.
If you dance barefoot on the broken glass of undefined behaviour, you’ve got to expect the occasional cut.
If at first you don’t succeed, try writing your phone number on the exam paper.

-std=c99 problem with signal.h

Actually if you want strict ansi/c99 compliance, you should use “signal” instead of “sigaction” which is defined by POSIX.1-2001.

In fact POSIX.1 supports both but prefers “sigaction”, whereas ansi/c99 supports only “signal”.

Of course, “signal” is very much system dependent.

See “man signal”, “man sigaction”.

The following link has a lot of information.
https://www.securecoding.cert.org/confluence/display/seccode/SIG00-C.+Mask+signals+handled+by+noninterruptible+signal+handlers

其实说的很明晰了,就是说signal是一个ansi/C99编译器支持的函数,sigaction是后来在POSIX.1-2001中新增的接口,那么由于向前兼容的特性,ansi/c99并不支持sigaction函数接口。

也就是说,大多数操作系统均支持signal,但是却只有部分支持sigaction。

因此编译器在处理的时候,如果你指定了–ansi或者-std=c99的指令后,编译器会认为你要是编译一个标准的C程序,那么就在编译时(准确的来说是预处理阶段)将你不符合C标准的地方舍弃掉,

怎么舍弃呢?当然是通过宏了。

验证


我们知道gcc编译器可以通过-E来进行预处理来生成预处理后的文件。

那么我们现在来验证一下子,我们在生成的预处理文件中,搜索指定的sigaction关键字,看预处理的文件中,有没有该结构体的定义

gcc -E test.c -std=c99 | grep sigaction
gcc -E test.c --ansi | grep sigaction

报错storage size of ‘act’ isn’t known当使用std=c99编译struct sigaction

很明显,我们指定了使用C标准的编译器后,编译器在预处理阶段针对非C标准的接口都屏蔽掉了。

同样我们使用GNU编译器扩展的C编译器进行预处理,看看有什么

gcc -E test.c -std=gnu99  | grep sigaction
gcc -E test.c -std=gnu9x | grep sigaction

报错storage size of ‘act’ isn’t known当使用std=c99编译struct sigaction

好了,我们现在是不是很清楚了

关于具体gcc编译参数的详情,请参见GCC Command-Line Options

报错storage size of ‘act’ isn’t known当使用std=c99编译struct sigaction

我们可以看到–ansi指定使用古老的标准ansiC标准编译,-std=c99则指定使用新的C99标准编译,而gnu99gnu9x 则是gcc编译器对C99标准自己的实现

这些的关系是什么呢,

首先标准委员会指定了语言的标准,也就是ansi和后来的C88,C99标准

但是只有标准怎么行,必须有编译器来支持啊,于是各个编译器都开始逐步支持新的标准,同时也就是出现了-std=c89,-stdc99,为了兼容之前的程序,而保留旧的标准–ansi

但是对标准的支持,不可能一下子,只实现了一部分,慢慢再进行扩展,另外由于标准中某些地方可能并不实用,可能需要一些扩展,于是出现了-std=gnu99,现在很多编译器对C的语法都有一些不同的扩展,而这些扩展有些已经加入了新的标准中,有些并未加入或者正在议案加入。

解决


解决方法1


就是我们在编译的过程中编译器的类型,比如-std=gnu99

解决方法2


#include <signal.h>之后显式添加sigaction的头文件

#include <bits/sigaction.h> 

这样编译器会显式的编译sigaction的定义

但是这种情况下需要注意,#include <bits/sigaction.h> 必须在#include <signal.h>之后

因为signal中会通过#error进行预处理,检查`bits/sigaction.h是否包含,
如果用户没有包含的话,预处理阶段就会报错

#error "Never include <bits/sigaction.h> directly; use <signal.h> instead."

报错storage size of ‘act’ isn’t known当使用std=c99编译struct sigaction