5

以下程序:

#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>

int main() {
  fclose( stderr );
  printf( "%d\n", fileno( stderr ) );
  return 0;
}

-1在 ubuntu 11.04 和2ICS 4.0.3 模拟器上显示。找不到有关此问题的任何信息 - 我可以使此代码在两个平台上类似地工作吗?freopenonstderr有同样的问题。

更新:

以前的小程序演示了我遇到的实际问题的原因:如果我尝试freopen stderr在不存在的目录中归档,在 linux 上stderr是关闭的,但在 android 上它保持打开状态!甚至更多 - 如果我在这个打开的stderr文件中写入 smth 然后fopen在其他文件上执行,我打印到stderr的文本将写入这个打开的文件。

所以,这个程序:

#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>

# define LOGD( ... ) printf( __VA_ARGS__ ); printf( "\n" )

# ifdef ANDROID
#   define HOMEDIR "/data/data/com.myapp/" // for android
# else
#   define HOMEDIR "/home/darkmist/" // for linux
# endif

# define _T( x ) x

void TestFreopen_mkdir() {
  int mkdirres = mkdir( HOMEDIR "1.d", 0777 );
  LOGD(_T("TestFreopen mkdirres=0x%08x"),mkdirres);
}

void TestFreopen() {
  LOGD(_T("TestFreopen begin"));

  LOGD(_T("TestFreopen stderr=0x%08x"),fileno(stderr));
  fprintf(stderr,"fprintf_1 to stderr\n");

  // TestFreopen_mkdir(); // case 1

  if ( NULL == freopen( HOMEDIR "1.d/1", "w", stderr ) ) {
    LOGD( "freopen failed" );
    if ( -1 != fileno( stderr ) ) {
      fclose( stderr );
      LOGD( "freopen closed" );
    }
  }

  LOGD(_T("TestFreopen stderr=0x%08x"),fileno(stderr));
  fprintf(stderr,"fprintf_2 to stderr\n");

  TestFreopen_mkdir(); // case 2

  FILE* fopen_file = fopen( HOMEDIR "1.d/2", _T( "wb" ) );

  LOGD(_T("TestFreopen fopen_file=0x%08x"),fileno(fopen_file)); // same as for reopened stderr!!

  fprintf(stderr,"fprintf_3 to stderr\n");
  fprintf(fopen_file,"fprintf_1 to fopen_file\n");
  fflush(fopen_file);

  LOGD(_T("TestFreopen end"));
}

int main() {
  TestFreopen();
  return 0;
}

在 linux 上显示:

$ ./a.out
TestFreopen begin
TestFreopen stderr=0x00000002
fprintf_1 to stderr
freopen failed
TestFreopen stderr=0xffffffff
TestFreopen mkdirres=0x00000000
TestFreopen fopen_file=0x00000002
TestFreopen end

$ cat ~/1.d/2 
fprintf_1 to fopen_file

这在android上:

$ adb push ./a.out /data/data/com.myapp
573 KB/s (34635 bytes in 0.058s)

$ adb shell run-as com.myapp /data/data/com.myapp/a.out
TestFreopen begin
TestFreopen stderr=0x00000002
fprintf_1 to stderr
freopen failed
freopen closed
TestFreopen stderr=0x00000002
TestFreopen mkdirres=0x00000000
TestFreopen fopen_file=0x00000002
TestFreopen end

$ adb shell run-as com.myapp cat /data/data/com.myapp/1.d/2
fprintf_3 to stderr
fprintf_1 to fopen_file
4

3 回答 3

10

在我看来,您期望正式声明为文件指针在关闭后的未定义行为在具有非常不同 C 库的设备上是相同的。

一旦您以设计为失败的方式调用 freopen() ,将来使用该文件指针的尝试就不能依赖于获得一致的结果。

您发现了一些有趣的结果,剩余的部分仍然可以用于意外结果,但问题不在于它们不会导致某种异常,问题是您试图在语言中使用无效的文件指针并没有宣传自己有保障措施。

问题不在于系统,而在于您的程序滥用系统。

于 2012-06-13T16:38:42.720 回答
4

在任何平台上关闭 stderr 后尝试使用它都没有任何意义。

于 2012-05-31T18:40:41.337 回答
3

fclose()是libc提供的一个函数,所以libc的不同实现可能会有不同的行为,因为fclose()之后的文件描述符的状态是不确定的

Ubuntu 使用 eglibc,而 android 使用仿生作为标准 libc。


eglibc

查看fclose()的 eglibc 2.15 源代码,我们有:

来自 iofclose.c :

...
_IO_acquire_lock (fp);
if (fp->_IO_file_flags & _IO_IS_FILEBUF)
    status = INTUSE(_IO_file_close_it) (fp);
else
    status = fp->_flags & _IO_ERR_SEEN ? -1 : 0;
...

您正在关闭 strerr,将执行第一条语句。查看以下函数:

来自 fileops.c

fp->_flags = _IO_MAGIC|CLOSED_FILEBUF_FLAGS;
fp->_fileno = -1;
fp->_offset = _IO_pos_BAD;

return close_status ? close_status : write_status;

如您所见,eglibc 将显式设置 fileno 为 -1。


仿生

Bionic 以自己的方式处理文件,从fileno()开始,它返回fp->_file

来自 stdio.h

#define __sfileno(p)    ((p)->_file)

来自文件号.c

int
fileno(FILE *fp)
{
        int ret;

    FLOCKFILE(fp);
        ret = __sfileno(fp);
        FUNLOCKFILE(fp);
        return (ret);
}

查看fclose()的仿生源代码,我们有:

来自 fclose.c

int
fclose(FILE *fp)
{
        int r;

        if (fp->_flags == 0) {  /* not open! */
                errno = EBADF;
                return (EOF);
        }
        FLOCKFILE(fp);
        WCIO_FREE(fp);
        r = fp->_flags & __SWR ? __sflush(fp) : 0;
        if (fp->_close != NULL && (*fp->_close)(fp->_cookie) < 0)
                r = EOF;
        if (fp->_flags & __SMBF)
                free((char *)fp->_bf._base);
        if (HASUB(fp))
                FREEUB(fp);
        if (HASLB(fp))
                FREELB(fp);
        fp->_r = fp->_w = 0;    /* Mess up if reaccessed. */
        fp->_flags = 0;         /* Release this FILE for reuse. */
        FUNLOCKFILE(fp);
        return (r);
}

来自 wcio.h

#define WCIO_FREE(fp) ((void)(0))

从本地.h

#define HASUB(fp) (_UB(fp)._base != NULL)
#define FREEUB(fp) { \
        if (_UB(fp)._base != (fp)->_ubuf) \
                free(_UB(fp)._base); \
        _UB(fp)._base = NULL; \
}

如您所见, fp->_file 在 fclose() 过程中保持不变。这实际上不是一个错误,因为fclose()之后文件描述符的状态尚未确定,但这应该阐明在 Ubuntu 和 Android 中执行程序之间的区别。

为了使您的代码可移植,您应该避免关闭 stderr 或在关闭它后将其重新打开到 /dev/null 之类的位置。

于 2012-06-18T20:26:29.787 回答