2013-08-29 4 views
6

내 생산 코드의 목업 : GCC에서FILE ** 변수를 표준 출력으로 설정하는 방법은 무엇입니까?

/* version 1 */ 
#include <stdio.h> 
FILE** fout = &stdout; 
int main() { 
    fprintf(*fout, "hello\n"); 
} 

작품 좋은,하지만 보도 (단항 '&'피연산자로 필요한 좌변)와 Mingw에서 컴파일에 실패.

나는 Is setting a FILE* equal to stdout portable?을 보았다. 이해하겠습니다.

/* version 2 */ 
#include <stdio.h> 
int main() { 
    FILE* fout = stdout; 
    fprintf(fout, "hello\n"); 
} 

은 완전히 유효합니다. 그러나 전역 변수를 미리 설정해야합니다. 불행히도

/* version 3 */ 
#include <stdio.h> 
FILE* fout = stdout; 
int main() { 
    fprintf(fout, "hello\n"); 
} 

은 버전 1을 대체하기에 적합하지 않습니다. gcc (2 행 : 이니셜 라이저 요소가 상수가 아님)에서 컴파일되지 않습니다.

main()이 시작되기 전에 초기화되는 변수에 stdout을 얻는 방법을 알고 싶습니다.

+0

표준 출력 스트림이 이미 열려 있습니다! 동시에 두 개의 stdout이 동시에 열릴 수는 없습니다. 아마도 #define을 사용하거나 단순히 main을 글로벌 var에 저장하게하십시오. main이 시작하기 전에 왜 시작해야합니까? –

+0

메인 시작 전의 이유 : 라이브러리 작성자로서 라이브러리 코드에서 초기화 된 struct default_settings를 제공하고 다른 많은 매개 변수 설정 중에서'fout = stdout '을 포함하게됩니다. –

답변

6

는 리눅스 (glibc는)에서 stdout는 다음과 같이 정의된다 :

extern struct _IO_FILE *stdout; 

그래서, 당신은 당신이 그와 함께 무엇을해야 할 수 있습니다.

그러나,는 MinGW에 stdoutstdio.h에서, 다음과 같이 정의된다 :

#define stdout (&_iob[STDOUT_FILENO]) 

아아, 그건 당신이 발견으로, 그것은 당신이 사용할 수있는 것이 아니라, 당신의 주소를 취할 수있는 것이 아닙니다, 그리고 글로벌 이니셜 라이저. :-(

문제의 근본 원인은 C 표준에서 매크로가 있어야한다는 것입니다. 즉, 휴대용 프로그램은 내부에 무엇이 있는지에 대한 가정을해서는 안된다는 것입니다. 그래서 두려운 것이 있습니다. 프로그래밍 방식으로 stdout을 읽지 마십시오. 이런 종류의 라이브러리는 많은 라이브러리가 다른 라이브러리보다 먼저 호출되어야하는 lib_initialize() 함수를 필요로하는 이유입니다.

C++에서는 전역 변수에 대한 생성자가 허용되며 라이브러리의 경우에도 자동으로 main 전에 호출됩니다. gcc으로 C 프로그램을 해킹하여 같은 일을하는 것이 가능하지만, 그것은 사악한 속임수이며 내 머리 꼭대기에서 그 일을하는 것을 기억할 수 없습니다. 어쨌든 fout를로드해야 할 것, 제로 꽤 저렴와 비교 : 큰 효율성 문제가되지 않습니다

#include <stdio.h> 
FILE* fout = NULL; 

int my_library_function() { 
    if (!fout) 
     fout = stdout; 
    fprintf(fout, "hello\n"); 
} 

을 :

난 그냥이 작업을 수행 할 것입니다.

5

C standard (7.21.1)에 따르면 stdout은 "FILE 포인터"유형의 표현식 인 매크로입니다. 반드시 글로벌 변수 일 필요는 없습니다. 포터블 C로 주소를 가져 오는 것이 아니라, gcc에서는 작동하지만 mingw에서는 작동하지 않습니다.

코드의 두 번째 버전 사용 - 이식성이 뛰어납니다. 당신이 foutmain 내부의 초기화를 이동 한 경우

세 번째도 OK 것 :

/* version 3 */ 
#include <stdio.h> 
FILE* fout; 
int main() { 
    fout = stdout; 
    fprintf(fout, "hello\n"); 
} 

stdout는 (적어도, 반드시 아니므로이 초기화, fout의 선언과 결합 할 수 없습니다) 상수 표현.

당신이 FILE ** 포인터를 갖고 싶어

는 사용
/* version 4 */ 
#include <stdio.h> 
FILE* mystdout; 
FILE** fout = &mystdout; 
int main() { 
    mystdout = stdout; 
    fprintf(*fout, "hello\n"); 
} 

하지만

다시 mystdout의 초기화 선언에있을 수없고, 같은 이유로.

+0

링크 및 설명 주셔서 감사합니다. 그러나 "다른 두 버전의 코드를 사용하십시오.이 코드는 이식성이 뛰어납니다." 필자가 쓴 것처럼, 제 3 판은 컴파일에 실패합니다. 나는'fout'이'FILE *'또는'FILE **'타입인지 여부에 상관하지 않습니다. 중요한 것은 내 라이브러리 사용자가 기본적으로'fout = stdout'을 가져야한다는 것입니다. 코드에'fout = stdout'과 같은 라인을 삽입하도록 요구해서는 안됩니다. 이 작업은 main()이 시작되기 전에 기본적으로 초기화되어야합니다. –

+0

맞습니다. 버전 3은 내 대답에 글로 쓰는 것처럼'main' 안에 전역 변수를 초기화해야합니다. 나는 편집 중이다. – nickie

+0

@JoachimWuttke 그러면 외부에서 볼 수있는 전역 변수를 사용하는 것이 어떻습니까? 그러면 사용자는 편리하게'globalFile = some_other_file; '이라고 쓸 수 있습니다. –

2

파일 설명자가 일반적으로 이식 가능하지 않기 때문에 나는 이것이 매우 좋은 생각이라고 생각하지 않습니다. 또한 라이브러리 함수의 구현을 매우 낮게 만들거나 파일 설명자와 FILE 포인터를 동기화하는 데 어려움을 겪습니다. @JoachimWuttke 명시 적 요청으로

그러나, 여기에 내가 내 이전 의견에 생각했던 내용은 다음과 같습니다

/* version 5 */ 
#include <stdio.h> 
#include <unistd.h> 

int fdout = 1; 

void use(FILE *fout) 
{ 
    fdout = fileno(fout); 
} 

void printing() { 
    const char msg[] = "hello\n"; 
    write(fdout, msg, sizeof(msg)-1); 
} 

int main() { 
    printing(); // this one goes to stdout 
    FILE *f = fopen("output.txt", "wt"); 
    use(f); 
    printing(); // this one goes to "output.txt" 
    printing(); // this one too 
    fclose(f); 
    use(stdout); 
    printing(); // this one goes to stdout too 
    return 0; 
} 
+0

고마워요, 니키. 첫 번째 대답은 내가 염두에 두었던 것처럼 초기화를 쉽게 만들어 준다. 그러나'fprintf'가'write'로 대체되는 대신에. 나는이 가격을 기꺼이 지불하지 않을 것을 두려워한다. 그래서 나는 쉽고 깨끗한 해결책이 없다는 통찰력에 복종합니다. –

관련 문제