20

サンプルが機能することを確認するために、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がどのように定義されているのかわかりません。任意のポインタまたはヘルプをいただければ幸いです。私はそれについて興味があります。何も壊れていません。

ありがとうございました。

4

5 に答える 5

14

errno変数は一種の奇妙なアヒルです。最近のほとんどのランタイムライブラリはスレッドをサポートしているため、変数を1つ だけにすることはできません。errnoもしあれば、2つのスレッドが同時にerrno値を設定することを実行でき、大きな混乱が生じます。

ランタイムライブラリは、この問題を回避するためにさまざまなトリックを実行します。たとえば、次のようなことを行うことができます。

#define errno __get_errno()

ここで、参照はerrno実際に内部__get_errno()関数を呼び出します。これは、現在のスレッドの正しいエラー番号の値を返します。このメソッドの欠点は、errno = 0;(一部のコードで実行される可能性がある)などのerrnoへの割り当てができないことです。ランタイムライブラリは通常、より洗練されたアプローチを選択します。

一部のランタイムライブラリ(使用しているものなど)は、スレッドごとに異なる値を持つことができる特別なタイプの「スレッドローカル変数」を宣言できます。システム上のデバッガーがその種の変数を表示できないようです。

于 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 int1そのため、GDBを使用してその値を出力することはできません。

1 もちろん、ご覧のとおり、関数呼び出しは実際の変数へのポインターを返す必要があり、errnoマクロはそれを逆参照します。

于 2012-07-15T21:30:08.957 に答える
6

errno実際には、C標準では、変更可能な左辺値に展開されるマクロである必要があります。最も単純なケースでは、宣言された変数の名前に展開できますが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

更新KevinCoxのコメントは、回避策を示唆しています。

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

そして、gcc6.2とgdb7.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

他の人が言っているように、errnogdbが出力できる変数ではありません。ただし、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 に答える