我在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;
}
以下是我尝试打印值时发生的情况:
(gdb) p errno
Cannot find thread-local variables on this target
(gdb)
我可以很好地打印fp的值。正如您所期望的那样,它的值是0x00
。
我查看了/usr/include/errno.h
以及许多其他包含errno.h
一部分的文件,我无法弄清楚如何定义errno。任何指针或帮助将不胜感激。我只是好奇它;什么都没有打破。
谢谢。
答案 0 :(得分:14)
errno
变量有点奇怪。因为现在大多数运行时库都支持线程,所以不能只有一个 errno
变量。如果有,则两个线程可以同时执行设置errno
值的事情,并且会产生很大的混淆。
运行时库可以使用各种技巧来避免此问题。例如,人们可能会这样做:
#define errno __get_errno()
其中对errno
的引用实际上调用了内部__get_errno()
函数,该函数返回当前线程的正确错误编号值。这种方法的缺点是它阻止了对errno的赋值,例如errno = 0;
(某些代码可能会这样做)。运行时库通常会选择更复杂的方法。
一些运行时库(比如你正在使用的那个)可以声明一种特殊类型的“线程局部变量”,它可以在每个线程上具有不同的值。听起来你的系统上的调试器无法显示那种变量。
答案 1 :(得分: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
宏会取消引用它。
答案 2 :(得分:5)
errno
是一个扩展为可修改左值的宏。在最简单的情况下,它可以扩展为声明变量的名称,但对于需要针对不同线程使用不同errno
个对象的实现,它通常定义如下:
#define errno (*__errno_location ())
gdb
通常能够评估函数调用;例如,在我的系统上:
(gdb) p __errno_location()
$1 = -134383968
(gdb) p errno
Cannot find thread-local variables on this target
第一个打印值恰好是__errno_location()
返回的指针值的低32位。我不太了解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
中的错误,或者至少是一个缺失的功能。
更新 Kevin Cox的评论提示了一种解决方法:
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)
答案 3 :(得分:5)
正如其他人所说,errno
不是gdb可以打印的变量。但是gdb可以
求值函数,__errno_location()
返回一个指向`errno'的指针。我们唯一需要做的就是调用函数并取消引用restult:
(gdb) p *__errno_location()
就是这样。
答案 4 :(得分:1)
_CRTIMP int* __cdecl __MINGW_NOTHROW _errno(void);
#define errno (*_errno())
这样使用,您可以只传递首选veriable的地址,该地址将包含从调用函数返回的实际错误值。
E.g。您可以将函数_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
将包含错误。