为什么不能打印出错误的价值?

oct*_*bus 16 c gdb errno

我在SO"低质量"帖子中查看以下代码以确保样本有效,我的问题是为什么我不能打印errno的值?

#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;
}
Run Code Online (Sandbox Code Playgroud)

以下是我尝试打印值时发生的情况:

(gdb) p errno
Cannot find thread-local variables on this target
(gdb)
Run Code Online (Sandbox Code Playgroud)

我可以很好地打印fp的值.正如您所料,它的价值在于0x00.

我看了/usr/include/errno.h很多其他包含文件作为其中的一部分errno.h,我无法弄清楚如何定义errno.任何指针或帮助将不胜感激.我只是好奇它; 什么都没有打破.

谢谢.

Gre*_*ill 14

errno变量是一种古怪的家伙.因为现在大多数运行时库都支持线程,所以不能只有一个 errno变量.如果有的话,那么两个线程可以同时做两个设置errno值的事情,并且会产生很大的混乱.

运行时库可以使用各种技巧来避免此问题.例如,有人可能会这样做:

#define errno __get_errno()
Run Code Online (Sandbox Code Playgroud)

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

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


Sha*_*baz 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
Run Code Online (Sandbox Code Playgroud)

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

1 当然,你可以看到函数调用应该返回指向实际变量的指针,errno宏将取消引用它.


Kei*_*son 5

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

#define errno (*__errno_location ())
Run Code Online (Sandbox Code Playgroud)

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

(gdb) p __errno_location()
$1 = -134383968
(gdb) p errno
Cannot find thread-local variables on this target
Run Code Online (Sandbox Code Playgroud)

第一个打印值恰好是返回的指针值的低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
Run Code Online (Sandbox Code Playgroud)

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

这可能是一个错误,或者至少是一个缺失的功能gdb.

更新凯文考克斯的评论建议解决方法:

print *((int*(*)())__errno_location)()
Run Code Online (Sandbox Code Playgroud)

使用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) 
Run Code Online (Sandbox Code Playgroud)


Rpt*_*ptx 5

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

(gdb) p *__errno_location()
Run Code Online (Sandbox Code Playgroud)

就是这样.