20

I am looking at the following code in an SO "Low Quality" post to make sure the sample works, and my question is why can't I print errno's value?

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

int main(){
    FILE *fp;
    errno = 0;
    fp=fopen("Not_exist.txt","r");
    if(fp == NULL && errno == ENOENT)
        perror("file not exist");
    return 0;
}

Here is what happens when I try to print the value:

(gdb) p errno
Cannot find thread-local variables on this target
(gdb)

I can print fp's value just fine. As you would expect it's value is 0x00.

I looked at /usr/include/errno.h and a lot of the other include files included as part of errno.h, and I cannot figure out how errno is defined. Any pointers or help would be appreciated. I'm just curious about it; nothing is broken.

Thank you.

4

5 回答 5

14

这个errno变量有点像一只奇怪的鸭子。因为现在大多数运行时库都支持线程,所以不能只有一个 errno变量。如果有的话,那么两个线程可以同时做一些事情,并且都设置了这个errno值,然后就会产生很大的混乱。

运行时库会使用各种技巧来避免这个问题。例如,可能会执行以下操作:

#define errno __get_errno()

where 引用errno实际调用内部__get_errno()函数,该函数返回当前线程的正确错误编号值。这种方法的缺点是它防止分配给 errno,例如errno = 0;(某些代码可能会这样做)。运行时库通常会选择更复杂的方法。

一些运行时库(比如你正在使用的,我想)可以声明一种特殊类型的“线程局部变量”,它可以在每个线程上具有不同的值。听起来您系统上的调试器无法显示那种变量。

于 2012-07-15T21:28:02.917 回答
12

在我的 Ubuntu 安装中,我有以下部分bits/errno.h

/* Function to get address of global `errno' variable.  */
extern int *__errno_location (void) __THROW __attribute__ ((__const__));

#  if !defined _LIBC || defined _LIBC_REENTRANT
/* When using threads, errno is a per-thread value.  */
#   define errno (*__errno_location ())
#  endif

也就是说,errno不一定是变量。由于各种原因,您可能希望有一个函数为您返回错误值,而不是简单的extern int. 1这就是为什么您不能使用 GDB 打印其值的原因。

1 当然,正如您所看到的,函数调用应该返回指向实际变量的指针,而errno宏会取消引用它。

于 2012-07-15T21:30:08.957 回答
6

errnoC 标准实际上要求它是扩展为可修改左值的宏。在最简单的情况下,它可以扩展为已声明变量的名称,但对于需要errno不同线程的不同对象的实现,它通常定义如下:

#define errno (*__errno_location ())

gdb通常能够评估函数调用;例如,在我的系统上:

(gdb) p __errno_location()
$1 = -134383968
(gdb) p errno
Cannot find thread-local variables on this target

第一个打印的值恰好是返回的指针值的低 32 位__errno_location()。我不太了解 gdb 来解释这种行为,但它确实证明了它可以执行函数调用。

作为一种解决方法,您可以修改源代码,以便将 的地址errno或其值保存在 gdb 可以显示的变量中:

(gdb) l
1       #include <errno.h>
2       #include <stdio.h>
3       int main(void) {
4           errno = 42; /* arbitrary value */
5           const int *errno_ptr = &errno;
6           int errno_value = errno;
7           printf("%d %d %d\n", errno, errno_value, *errno_ptr);
8       }
(gdb) b 8
Breakpoint 1 at 0x4005b6: file c.c, line 8.
(gdb) r
Starting program: /home/kst/c 
42 42 42

Breakpoint 1, main () at c.c:8
8       }
(gdb) p errno
Cannot find thread-local variables on this target
(gdb) p errno_value
$1 = 42
(gdb) p *errno_ptr
$2 = 42

*errno_ptr方法的优点是您只需分配一次 - 除非您正在调试多线程程序。在这种情况下, 的值&errno可能会根据您评估它的线程而有所不同。

这可能是gdb.

更新凯文考克斯的评论提出了一种解决方法:

print *((int*(*)())__errno_location)()

使用 gcc 6.2 和 gdb 7.11,print errno实际上可以工作:

(gdb) l
1       #include <errno.h>
2       int main(void) {
3           errno = 42;
4           return 0;
5       }
(gdb) b 4
Breakpoint 1 at 0x6bf: file c.c, line 4.
(gdb) r
Starting program: /home/kst/c 

Breakpoint 1, main () at c.c:4
4           return 0;
(gdb) p errno
$1 = 42
(gdb) 
于 2013-08-29T18:10:07.503 回答
6

正如其他人所说,errno不是 gdb 可以打印的变量。但是 gdb 可以评估函数,并__errno_location()返回一个指向 `errno' 的指针。那么我们唯一需要做的就是调用函数并取消引用结果:

(gdb) p *__errno_location()

就是这样。

于 2016-02-23T01:36:42.803 回答
1
_CRTIMP int* __cdecl __MINGW_NOTHROW _errno(void);
#define errno       (*_errno())

使用它的目的是,您可以只传递首选验证的地址,该地址将包含从调用函数返回的实际错误值。

例如,您可以定义函数 _errno() 如下

unsigned int errorValue;

int* _errno()
{
    return (&errorValue);
}

现在用法:

void MyFunc()
{
  if(some condition failure)
      errno = 10; //Or any error value as per your design
  else
  {
     //Actual operation
  }
}

执行后MyFunc()errorValue将包含错误。

于 2013-08-29T14:00:15.483 回答