一个问题引出的---对gcc与C语言标准思考

简介: 最近在总结关于Linux系统关于Time处理相关的API,当在开发库中使用到localtime_r以及clock_gettime时,会提示如下的错误(-Werror选项打开):

最近在总结关于Linux系统关于Time处理相关的API,当在开发库中使用到localtime_r以及clock_gettime时,会提示如下的错误(-Werror选项打开):


error: implicit declaration of function ‘localtime_r’ [-Werror=implicit-function-declaration]


解决步骤


怀疑GCC版本


开发库使用的项目构建工具为cmake,出现这个问题很诡异,因为之前编写小的测试程序时没有问题。第一时间感觉可能是cmake的CMakeLists.txt配置 存在问题(PS:编写测试程序时直接使用的gcc),进一步感觉是不是cmake没有使用正确版本的gcc,通过查询确定一种修改cmake修改编译器的方法如下: build.sh中增加如下代码:


COMPILE_PATH="-DCMAKE_CXX_COMPILER:FILEPATH=/usr/bin/g++ -DCMAKE_C_COMPILER:FILEPATH=/usr/bin/gcc"
cmake . $COMPILE_PATH


其中,DCMAKE_CXX_COMPILER用于指定g++的路径,CMAKE_C_COMPILER用于指定gcc的路径。


重新build,cmake提示确实更换了gcc的配置:


You have changed variables that require your cache to be deleted.
Configure will be re-run and you may have to reset some variables.
The following variables have changed:
CMAKE_C_COMPILER= /usr/bin/gcc
CMAKE_CXX_COMPILER= /usr/bin/g++
-- The C compiler identification is GNU 4.8.4
-- The CXX compiler identification is GNU 4.8.4
-- Check for working C compiler: /usr/bin/gcc
-- Check for working C compiler: /usr/bin/gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/g++
-- Check for working CXX compiler: /usr/bin/g++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done


可是,编译还是提示同样的错误,看来是错怪了cmake和gcc了。


cmake最小工程


不是gcc版本的问题,现在只剩下cmake环境和开发库环境了,首先测试cmake最小工程,是否可以编译通过(注意需要使用相同的CMakeLists.txt配置)。


工程目录其实非常简单,只包含CMakeLists.txt和一个c源文件,代码如下:


#CMakeLists.txt:
#Cmake 最低版本要求                                                                                                                                                                                          
cmake_minimum_required(VERSION 3.1)
#项目名称
project(emlib)
add_compile_options(-std=c99 -Werror)
set(CMAKE_CFLAGS_DEBUG    "$ENV{CFLAGS} -O0 -Wall -g -ggdb -fstack-protector-all")
set(CMAKE_CFLAGS_RELEASE  "$ENV{CFLAGS} -O3 -Wall -DNDEBUG")
#查找src目录下的所有源文件,将其存储到DIR_SRCS变量中
aux_source_directory(. DIR_SRCS)
#配置头文件存放的目录
include_directories(/usr/include/)
#生成制定目标
add_executable(${PROJECT_NAME} ${DIR_SRCS})
#配置链接库
target_link_libraries(${PROJECT_NAME} pthread)


test.c
#include <time.h>
int main(void)
{
  struct tm rs; 
    time_t tp; 
    time(&tp);
    localtime_r(&tp, &rs); 
  return 0;
}


编译测试,问题仍然存在。好了,基本确定为cmake的配置问题了。但是,大家看到了,cmake的配置相当的简单,到底是哪一条配置影响到Linux系统库了呢?


柳暗花明


正当问题陷入困境时,突然想到,cmake支持生成各种编译文件的功能,何不看看源文件的预编译文件,到底是因为什么原因没有声明localtime_r函数的。通过make test.i生成 test.c的预编译文件,打开搜索发现,test.i中确实没有声明localtime_r函数,那么现在的问题就是test.c为什么没有在预编译阶段包含到time.h中的localtime_r声明?


直接打开time.h文件看看就知道了,time.h文件位于/usr/include/time.h,搜索发现如下代码:


# if defined __USE_POSIX || defined __USE_MISC
/* Return the `struct tm' representation of *TIMER in UTC,
   using *TP to store the result.  */
extern struct tm *gmtime_r (const time_t *__restrict __timer,
                struct tm *__restrict __tp) __THROW;
/* Return the `struct tm' representation of *TIMER in local time,
   using *TP to store the result.  */
extern struct tm *localtime_r (const time_t *__restrict __timer,                                                                                                                                             
                   struct tm *__restrict __tp) __THROW;
# endif /* POSIX or misc */


很明显,localtime_r只有在__USE_POSIX 和 __USE_MISC宏定义时,才会进行声明!那么,应该是gcc为什么没有定义这些宏呢?


回头分析CMakeLists.txt文件,发现只有add_compile_options(-std=c99 -Werror)修改了编译器的选项,将其注释,重新编译,好了,编译通过。


那么问题应该是-std=c99导致的(相信大家之所以导入该选项,都是为了for循环时少写一行代码(for int i = 0; i < N; i++)),那么,该为了支持某些比较新的编译器特性,该选用 那些标准呢?


C标准


通过查询,发现历史上出现过很多C相关的标准,下面了一个表:


主版本 C89 AMD1 C99 C11
别名 C90 、ANSI C、 X3.159-1989 、ISO/IEC 9899:1990 C94 、C95 ISO/IEC 、9899:1999 ISO/IEC 、9899:2011
标准通过时间
标准发布时间 1990年 1995年 1999年 2011年
GCC使用此版本所用参数 -ansi -std=c90 -std=iso9899:1990 -std=iso9899:199409 -std=c99 -std=iso9899:1999 -std=c11 -std=iso9899:2011
GCC使用此版本且带C扩展时所用参数 -std=gnu90 -std=gnu99 -std=gnu11


由于我们使用的是gcc所以,-std=c99应该换为-std=gnu99,编译测试通过。

其实,gnu90、gnu99、gnu11都是标准C中扩展了GNU/GCC的一些特性,例如,类似于_USE_POSIX、_USE_POSIX宏都是在gnu标准下定义的,gcc默认情况下是使用gnu标准的,所以如果是在GNU/Linux 平台下,使用gcc开发,如果需要指定C语言标准,那么应该使用gnu相关的标准。


后记


大家看到了,本文遇到的问题不是什么特别难的编程问题,问题的原意只是因为编译器的选项问题。但是,为什么还要做这么详细的总结?其实,问题本身不重要,重要的是解决问题的方法。 本文其实展示一个解决问题的基本步骤:


  1. 首先,分析问题表象,如果不是很容易定位问题原因,那么我们就应该分步骤去解决它;


  1. 确定可能导致问题的原因域,并把它们按照可能性排序,最有可能肯定需要首先处理;


  1. 然后,按照原因域,分条测试,逐个排除,只到问题解决;


  1. 如果,测试完所有的原因域,问题都没有解决,那么可能是原因域存在漏掉的情况或者问题分析的方向存在问题或者问题现象没有分析透...


  1. 按照步骤4,重新定义原因域,重复2-3步骤;


其实,不管是大问题还是小问题,我们都应该以科学的方法、冷静的头脑,去分析,去解决,要相信只要肯用脑,办法总比困难多!


相关文章
|
3月前
|
NoSQL 编译器 程序员
【C语言】揭秘GCC:从平凡到卓越的编译艺术,一场代码与效率的激情碰撞,探索那些不为人知的秘密武器,让你的程序瞬间提速百倍!
【8月更文挑战第20天】GCC,GNU Compiler Collection,是GNU项目中的开源编译器集合,支持C、C++等多种语言。作为C语言程序员的重要工具,GCC具备跨平台性、高度可配置性及丰富的优化选项等特点。通过简单示例,如编译“Hello, GCC!”程序 (`gcc -o hello hello.c`),展示了GCC的基础用法及不同优化级别(`-O0`, `-O1`, `-O3`)对性能的影响。GCC还支持生成调试信息(`-g`),便于使用GDB等工具进行调试。尽管有如Microsoft Visual C++、Clang等竞品,GCC仍因其灵活性和强大的功能被广泛采用。
127 1
|
3月前
|
Java 编译器 vr&ar
【C语言】GCC相关常识
【C语言】GCC相关常识
27 1
|
5月前
|
编译器 C语言
C语言编译详解:GCC分步编译与一次编译多个文件
C语言编译详解:GCC分步编译与一次编译多个文件
490 2
|
编译器 C语言 Windows
【C 语言】Windows 下使用 gcc 编译器 ( 常用的编译器 | Qt 中的 gcc 编译器 | 独立安装 MinGW )(三)
【C 语言】Windows 下使用 gcc 编译器 ( 常用的编译器 | Qt 中的 gcc 编译器 | 独立安装 MinGW )(三)
164 0
【C 语言】Windows 下使用 gcc 编译器 ( 常用的编译器 | Qt 中的 gcc 编译器 | 独立安装 MinGW )(三)
|
编译器 C语言 Windows
【C 语言】Windows 下使用 gcc 编译器 ( 常用的编译器 | Qt 中的 gcc 编译器 | 独立安装 MinGW )(二)
【C 语言】Windows 下使用 gcc 编译器 ( 常用的编译器 | Qt 中的 gcc 编译器 | 独立安装 MinGW )(二)
139 0
【C 语言】Windows 下使用 gcc 编译器 ( 常用的编译器 | Qt 中的 gcc 编译器 | 独立安装 MinGW )(二)
|
编译器 Linux C语言
【C 语言】Windows 下使用 gcc 编译器 ( 常用的编译器 | Qt 中的 gcc 编译器 | 独立安装 MinGW )(一)
【C 语言】Windows 下使用 gcc 编译器 ( 常用的编译器 | Qt 中的 gcc 编译器 | 独立安装 MinGW )(一)
405 0
【C 语言】Windows 下使用 gcc 编译器 ( 常用的编译器 | Qt 中的 gcc 编译器 | 独立安装 MinGW )(一)
|
C语言 芯片
使用 gcc 命令把C语言程序反汇编
之前看过一点汇编,不过现在都忘记得差不多了。最近又很蛋疼地想起反汇编这个东西。这里使用 gcc 命令对 .c 文件进行反汇编,把 C语言 翻译成汇编语言 先准备一个简单的 C 程序 sum.c #include int add(int, int); int mode(int, i...
3910 0
|
C语言 编译器 Linux
用gcc编译c语言程序以及其编译过程
对于初学c语言编程的我们来说,学会如何使用gcc编译器工具,对理解c语言的执行过程,加深对c语言的理解很重要!!! 1、预编译 --> 2、编译 --> 3、汇编 --> 4、链接----------------------------------------------------------------------------- 0、编写c代码,并输入以下如图代码,生成c文件hello.c。
1612 0
|
C语言 编译器 机器学习/深度学习
|
1月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
34 3