errno 和 strerror函数

        今天写了一个很简单的代码,编译时没啥错误和警告(主要编译选项没开启警告),然后运行时居然 segmentation fault,把我给看傻了,代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

int main()
{
    int fd = open("readme",  O_RDWR);
    if(fd < 0)
    {
        printf("open fail: %s\n", strerror(errno));
        return fd;
    }

    return 0;
}

代码简单吧,就把 errno 对应的字符串打印出来,结果就 Segmentation fault 了,搞得我以为用法不对呢,我还去 man 了一个 strerror() 函数的用法:

 

用 %s 打印 char * 类型,是对的啊,怎么会这样呢?搞得我还去看了源码,顺便学习了一下 errno,原因后面再讲到。先看一下函数 strerror() 的源码:

/* Copyright (C) 1991-2022 Free Software Foundation, Inc.
   This file is part of the GNU C Library.
   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.
   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.
   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <https://www.gnu.org/licenses/>.  */
#include <string.h>
#include <locale/localeinfo.h>
char *
strerror (int errnum)
{
  return __strerror_l (errnum, __libc_tsd_get (locale_t, LOCALE));
}

再看 __strerror_l  函数的源码:

/* Return a string describing the errno code in ERRNUM.  */
char *
__strerror_l (int errnum, locale_t loc)
{
  int saved_errno = errno;
  char *err = (char *) __get_errlist (errnum);
  if (__glibc_unlikely (err == NULL))
    {
      struct tls_internal_t *tls_internal = __glibc_tls_internal ();
      free (tls_internal->strerror_l_buf);
      if (__asprintf (&tls_internal->strerror_l_buf, "%s%d",
		      translate ("Unknown error ", loc), errnum) == -1)
	tls_internal->strerror_l_buf = NULL;
      err = tls_internal->strerror_l_buf;
    }
  else
    err = (char *) translate (err, loc);
  __set_errno (saved_errno);
  return err;
}

 接着往下看 __get_errlist() 这个函数:

#ifndef ERR_MAP
# define ERR_MAP(n) n
#endif
const char *const _sys_errlist_internal[] =
  {
#define _S(n, str)         [ERR_MAP(n)] = str,
#include <errlist.h>
#undef _S
  };
const char *
__get_errlist (int errnum)
{
  int mapped = ERR_MAP (errnum);
  if (mapped >= 0 && mapped < array_length (_sys_errlist_internal))
    return _sys_errlist_internal[mapped];
  return NULL;
}

最后就是直接返回对应的指针数组元素值,这个指针数组初始化方式可以参考上一篇文件 这里 。定义了宏 #define _S(n, str)         [ERR_MAP(n)] = str 其实就是 _S(n,str) 被替换为 [n]=str,即为元素初始化,而在 errlist.h 头文件里使用了该宏:

_S(0, N_("Success"))
#ifdef EPERM
/*
TRANS Only the owner of the file (or other resource)
TRANS or processes with special privileges can perform the operation. */
_S(EPERM, N_("Operation not permitted"))
#endif
#ifdef ENOENT
/*
TRANS This is a ``file doesn't exist'' error
TRANS for ordinary files that are referenced in contexts where they are
TRANS expected to already exist. */
_S(ENOENT, N_("No such file or directory"))
#endif
#ifdef ESRCH
/*
TRANS No process matches the specified process ID. */
_S(ESRCH, N_("No such process"))
#endif
#ifdef EINTR

/*往下还有很多,这里就不完整展现了*/

而这些 EPERM、ENOENT、ESRCH 等待,就是 errno 的值,这里初始化了 errno 对应的错误字符串,所以经过预编译,_sys_errlist_internal 数组的值应该类似这样:

const char *const _sys_errlist_internal[] =
{
    [0] = "Success",
    [1] = "Operation not permitted",
    [2] = "No such file or directory",
    [3] = "No such process",
    [4] = "Interrupted system call",
    /*.....*/
};
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
#ifndef _ASM_GENERIC_ERRNO_BASE_H
#define _ASM_GENERIC_ERRNO_BASE_H
#define	EPERM		 1	/* Operation not permitted */
#define	ENOENT		 2	/* No such file or directory */
#define	ESRCH		 3	/* No such process */
#define	EINTR		 4	/* Interrupted system call */
#define	EIO		 5	/* I/O error */
#define	ENXIO		 6	/* No such device or address */
#define	E2BIG		 7	/* Argument list too long */
#define	ENOEXEC		 8	/* Exec format error */
#define	EBADF		 9	/* Bad file number */
#define	ECHILD		10	/* No child processes */
#define	EAGAIN		11	/* Try again */
#define	ENOMEM		12	/* Out of memory */
#define	EACCES		13	/* Permission denied */
#define	EFAULT		14	/* Bad address */
#define	ENOTBLK		15	/* Block device required */
#define	EBUSY		16	/* Device or resource busy */
#define	EEXIST		17	/* File exists */
#define	EXDEV		18	/* Cross-device link */
#define	ENODEV		19	/* No such device */
#define	ENOTDIR		20	/* Not a directory */
#define	EISDIR		21	/* Is a directory */
#define	EINVAL		22	/* Invalid argument */
#define	ENFILE		23	/* File table overflow */
#define	EMFILE		24	/* Too many open files */
#define	ENOTTY		25	/* Not a typewriter */
#define	ETXTBSY		26	/* Text file busy */
#define	EFBIG		27	/* File too large */
#define	ENOSPC		28	/* No space left on device */
#define	ESPIPE		29	/* Illegal seek */
#define	EROFS		30	/* Read-only file system */
#define	EMLINK		31	/* Too many links */
#define	EPIPE		32	/* Broken pipe */
#define	EDOM		33	/* Math argument out of domain of func */
#define	ERANGE		34	/* Math result not representable */
#endif

到这里我们已经知道函数 strerror() 是怎么实现的了,而且也知道函数使用方法也是对的,但为什么会出现 Segmentation fault 呢?原因是没包含头文件,导致 strerror() 函数变成隐式声明了,而隐式声明的函数它的默认返回值是 int 类型(没找到C语言关于这点的说明文档,有同学知道的话烦告知一下),所以用 %s 打印 int 类型导致 segmentation fault 了。所以改用 %d 来打印还会出现段错误吗?

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

const char *const _sys_errlist_internal[] =
{
    [0] = "Success",
    [1] = "Operation not permitted",
    [2] = "No such file or directory",
    [3] = "No such process",
    [4] = "Interrupted system call",
    /*.....*/
};
int main()
{
    int fd = open("readme",  O_RDWR);
    if(fd < 0)
    {
        printf("open fail: %d\n", strerror(errno));
        return fd;
    }
    
    return 0;
}

正确的做法:

1,编译时添加 -Wall 把所有的警告都输出

2,添加头文件 #inlcude <string.h>

因为没做第一步,编译完全看不出问题,假如做了第一步,编译警告可提供更多的线索,如:

gcc 警告信息也是相当重要的啊,要是直接忽略还得花时间查半天,还搞得莫名其妙^-^ 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/434716.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024护网面试题精选(一)

0x00.基础漏洞篇 00-TOP10漏洞 1.SQL注入 2.失效的身份认证和会话管理 3.跨站脚本攻击XSS 4.直接引用不安全的对象 5.安全配置错误 6.敏感信息泄露 7.缺少功能级的访问控制 8.跨站请求伪造CSRF 9.实验含有已知漏洞的组件 10.未验证的重定向和转发 01-SQL注入漏洞 …

PackagesNotFoundError:学习利用报错信息找到解决方法

反思&#xff1a;之前看到报错经常是直接复制报错信息去网上搜&#xff0c;但很多情况下报错信息里其实就给出了解决方案 报错信息&#xff1a; Collecting package metadata (current_repodata.json): done Solving environment: unsuccessful initial attempt using frozen …

关于Mybatis-Plus报错 Not Found TableInfoCache 解决办法

0. 接口结构&#xff1a;1. 方法报错&#xff1a;2. 解决方法&#xff1a;3. 原因分析&#xff1a; 0. 接口结构&#xff1a; 【接口】&#xff1a; public interface PurchaseOrderService extends IService<PurchaseOrder> {}【接口实现类】&#xff1a; public cla…

某多多anti_token(先水个文后续会完善)第一部分

声明: 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;不提供完整代码&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01;wx a15018…

某酷ckey140逆向(之前下架了重新上传补发)

声明: 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;不提供完整代码&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01;wx a15018…

[R] Underline your idea with ggplot2

Preview: # 介绍&#xff1a;之前的教程中&#xff0c;我们学习了如何使条形图或直方图看起来更好 比如&#xff1a; 1. How to select a graph calibrate the geom part 2. How to select variables calibrate the aes part 3. How to add a title calibrate the labs …

【Mybatis】批量映射优化 分页插件PageHelper 逆向工程插件MybatisX

文章目录 一、Mapper批量映射优化二、插件和分页插件PageHelper2.1 插件机制和PageHelper插件介绍2.2 PageHelper插件使用 三、逆向工程和MybatisX插件3.1 ORM思维介绍3.2 逆向工程3.3 逆向工程插件MyBatisX使用 总结 一、Mapper批量映射优化 需求: Mapper 配置文件很多时&…

16:00面试,16:08就出来了,问的问题过于变态了。。。

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到2月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40%…

Sparse A*算法的时间复杂度

Sparse A*(SAS)算法是A*算法的变型算法&#xff0c;下面将结合A*算法的流程分析SAS的时间复杂度。对于SAS算法而言&#xff0c;其航迹规划的时间 T T T主要由两部分组成&#xff1a; T s T_s Ts​&#xff1a;在当前结点扩展可行子结点的时间&#xff1b; T 0 T_0 T0​&#…

Qt QPainter的使用方法

重点&#xff1a; 1.QPainter在QWidget窗口的paintEvent中使用。 2.QPainter通常涉及到设置画笔、设置画刷、绘图&#xff08;QPen、QBrush、drawxx&#xff09;三个流程。 class Widget : public QWidget {Q_OBJECTprotected:void paintEvent(QPaintEvent *event) Q_DEC…

基于51单片机的四位并行数据主从机传输设计

基于51单片机的四位并行数据主从机传输设计[proteus仿真] 主从机通信系统这个题目算是课程设计和毕业设计中常见的题目了&#xff0c;本期是一个基于51单片机的四位并行数据主从机传输设计 需要的源文件和程序的小伙伴可以关注公众号【阿目分享嵌入式】&#xff0c;赞赏任意文…

机器学习---数据分割

之前的文章中写过&#xff0c;我们可以通过实验测试来对学习器的泛化误差进行评估并进而做出选择。 为此&#xff0c;需使用一个“测试集"(testing set)来测试学习器对新样本的判别能力&#xff0c;然后以测试集上的“测 试误差”(testing error)作为泛化误差的近似。通…

操作系统系列学习——内核级线程实现

文章目录 前言内核级线程实现 前言 一个本硕双非的小菜鸡&#xff0c;备战24年秋招&#xff0c;计划学习操作系统并完成6.0S81&#xff0c;加油&#xff01; 本文总结自B站【哈工大】操作系统 李治军&#xff08;全32讲&#xff09; 老师课程讲的非常好&#xff0c;感谢 【哈工…

Linux第71步_将linux中的多个文件编译成一个驱动模块

学习目的&#xff1a;采用旧字符设备测试linux系统点灯&#xff0c;进一步熟悉其设计原理。采用多文件参与编译&#xff0c;深度学习编写Makefile&#xff0c;有利于实现驱动模块化设计。 1、创建MyOldLED目录 输入“cd /home/zgq/linux/Linux_Drivers/回车” 切换到“/home…

softmax和sigmoid的区别

sigmoid 公式&#xff1a; s i g m o i d ( x ) 1 1 e − x sigmoid(x) \frac{1}{1 e^{-x}} sigmoid(x)1e−x1​ 函数曲线如下&#xff1a; 导数公式&#xff1a; f ( x ) ′ e − x ( 1 e − x ) 2 f ( x ) ( 1 − f ( x ) ) f(x)\prime \frac{ e^{-x}}{(1 e^{-x})…

备战蓝桥杯————二分搜索(一)

引言 一、二分查找 基本概念 代码框架 二、二分查找 题目描述 解题思路及代码 结果展示 三、寻找左侧边界的二分搜索 使用背景 基本代码 引言 在计算机科学的世界里&#xff0c;二分查找算法无疑是一种经典且强大的工具。它以其高效的性能&#xff0c;在有序数据集中…

95、评估使用多线程优化带来的性能提升

本节评估一下&#xff0c;通过对卷积的 co 维度进行多线程切分之后&#xff0c;对于模型的性能提升。 评估下性能 在进行多线程程序运行时&#xff0c;建议电脑中的 CPU 不要有其他繁重的任务执行。 在相同的环境下&#xff0c;分别运行 5th_codegen 和 6th_multi_thread 下的…

Pytorch 复习总结 6

Pytorch 复习总结&#xff0c;仅供笔者使用&#xff0c;参考教材&#xff1a; 《动手学深度学习》Stanford University: Practical Machine Learning 本文主要内容为&#xff1a;Pytorch 计算机视觉。 本文先介绍了计算机视觉中两种常见的改进模型泛化性能的方法&#xff1a…

香港投资+移民计划高峰论坛圆满落幕洞察趋势,探索未来财富之路

3月4日&#xff0c;由中国香港太阳联合资本有限公司牵头举办的「香港投资移民计划高峰论坛」在港交所圆满结束。吸引超260位高净值人士参加&#xff0c;已收到近600个投资移民意向。此次论坛汇聚了来自世界各地的投资移民专家、企业家、以及潜在投资者&#xff0c;共同探讨香港…

网络编程(3/4)

广播 ​ #include<myhead.h>int main(int argc, const char *argv[]) {//1、创建套接字int sfd socket(AF_INET, SOCK_DGRAM, 0);if(sfd -1){perror("socket error");return -1;}//2、将套接字设置成允许广播int broadcast 1;if(setsockopt(sfd, SOL_SOC…