출력 스트림에서 한문자씩 출력하기.

 cin 이 제공하는 메소드 중 하나의 문자를 입력받는 get 메소드가 존재하는 것처럼 cout에는 하나의 문자를 출력하는 put 메소드가 존재한다. put 메소드의 사용에는 제약이 따르게 되는데, 이는 put 메소드에서 사용 가능한 파라미터는 단지 하나의 문자(character)뿐이라는 것이다.

 물론 숫자를 넘길 수도 있는데, 이 또한 내부에서 문자로 변환하여 화면에 출력하게 된다. 예를 들어 다음을 실행시켜 보자.

int letter = 65;
cout.put(letter);

char clet = ‘B’;
cout.put(clet);

 위의 명령을 실행시키면 화면에 A와 B가 출력된다. put 메소드는 하나의 문자만을 입력받고 출력하는 작업이 반복적으로 이루어져야 하는 경우에 유용하게 사용할 수 있을 것이다.

ios::nocreate

 C++ 프로그래밍을 공부하던 중, 예제 프로그램을 컴파일하다 다음과 같은 에러를 발견했다.

 Description    Resource    Path    Location    Type
‘nocreat’ is not a member of ‘std::ios’    Config.cpp    /TEMS/src    line 40    C/C++ Problem

 환경은 이클립스 3.5.2 버전이었으며, g++ 버전은 version 4.4.3 (Ubuntu 4.4.3-4ubuntu5) 이었다.

 문제의 원인을 찾기 위해 구글링을 해보니 답은 간단했다.

 더이상 ios::nocreate 는 표준 C++ 에서 지원하지 않는다는 것.

http://cboard.cprogramming.com/cplusplus-programming/48548-if-ofstream.html 에서 다음과 같은 답변을 찾았다.

 ios::nocreate was removed from standard c++, you may use ios::in to open
the file for reading, and not create one if it doesn’t exist.
The file shouldn’t be created if you have a standard compliant compiler.
(I think dc++ is)

ios::nocreate 를 쓰고 싶다면 그냥 ios::in 만 사용하면 된다. 🙂

윈도우 네트워크 프로그래밍 중 발생한 warning C4761

 윈도우에서 네트워크 프로그램 소스를 컴파일 하던 중 이상한 warning 을 발견하였다.


C:Documents and SettingsOwnerMy DocumentsNetworkhelloworld_client_winhelloworld_client_win.c(34) : warning C4761: integral size mismatch in argument; conversion supplied
 문제의 소스 부분은 다음이었다.

servAddr.sin_port = htons(atoi(argv[2]));
 보기에는 문제가 없는 부분이다. 정상작동하는 소스였다.(..리눅스에서 확인)

 물론 warning 을 무시하고 링크를 하여도 문제없이 프로그램을 잘 실행되었다.

 그렇다면 무엇이 문제일까….먼저 C4761 에 관한 내용을 찾아 보았다.

 다음과 같은 내용을 찾을 수 있었다.



Compiler Warning (level 1) C4761

integral size mismatch in argument : conversion supplied


The base types of the actual and formal parameters of a function were different.


The compiler converted the actual parameter to the type of the formal parameter.

 관련 링크 : http://msdn2.microsoft.com/en-us/library/aa733937(VS.60).aspx

 내용인즉 인자의 사이즈가 맞지 않는다는 것이다.

 다시 소스를 살펴보다가 다음을 발견하였다.


unsigned short__cdecl htons(unsigned short)
 즉 인자값으로 short 형이 와야 하는 것이다. 하지만 atoi() 함수는 리턴형이 int 값이므로 여기서 형변환의 문제가 생긴 것이다.

 다음과 같이 고쳐주니 warning 이 사라지고 깔끔한 컴파일이 되었다.


servAddr.sin_port = htons((unsigned short)atoi(argv[2]));

파일 디스크립터와 파일 포인터 사용시 주의점

 네트워크 프로그래밍을 하더도중…한가지 풀리지 않는 의문점이 생겨 KLDP 게시판에 글을 올렸다.

 내용 보기 : http://kldp.org/node/90394

 글을 올린지 30분도 채 안되서 답변이 올라왔다.

 문제인즉…

 다음과 같은 코드를 실행했을때…
 

(Language : c)
  1. #include
  2. #include
  3. #include
  4. #include
  5. #include
  6.  
  7. void error_handling(char *message);
  8.  
  9. int main(void)
  10. {
  11.     int fildes;
  12.     FILE *fp;
  13.  
  14.     fildes = open(“data.dat”, O_WRONLY | O_CREAT | O_TRUNC);
  15.     if(fildes == -1)
  16.         error_handling(“open() error!”);
  17.    
  18.     /* 파일 디스크립터를 이용하여 파일 포인터 생성 */
  19.     fp = fdopen(fildes, “w”);
  20.     write(fileno(fp), “This is write funcion1!! nn, 27);
  21.     fputs(“This is fputs function!! nn, fp);
  22.     write(fileno(fp), “This is write funcion2!! nn, 27);
  23.    
  24.     fclose(fp);
  25.    
  26.     return 0;
  27. }
  28.  
  29. void error_handling(char *message)
  30. {
  31.     fputs(message, stderr);
  32.     fputc(n, stderr);
  33.     exit(1);
  34. }

 나타나는 결과 화면이 다음과 같다는데 있었다.

사용자 삽입 이미지
 즉 fgets() 함수의 호출 순서와 파일에 입력된 순서가 서로 맞지 않는다는 것이었다.

 이유는 간단했다.

 KLDP의 답글에 올라온 내용처럼 C표준 라이브러리의 입/출력 함수의 경우 자체 버퍼를 따로 가지고 있어서 이를 명시적으로 비워 주지 않으면 문제의 소지가 있다는 것이다.

 물론 명시적으로 해주지 않아도 표준 라이브러리만 사용한다면 문제는 없다. 하지만 지금처럼 시스템 함수와 표준 라이브러리를 병행해서 사용할 경우 위와 같은 문제가 생길 수 있기 때문에 혼용한다면 명시적으로 버퍼를 비워주는 작업이 필요하다.

 다음과 같이 말이다. 위의 코드는 아래의 코드를 추가한 결과 정상적으로 작동하였다.

(Language : c)
  1.     fputs(“This is fputs function!! nn, fp);
  2.     fflush(fp);

 원인을 안다면 해결책은 간단한 법이다.
 

리눅스(Unix)에서 fflush(stdin) 사용시 발생하는 문제점.

 scanf 로 문자열을 받고 나면 공백으로 구분되서 남은 문자들이 아직 버퍼에 남아 있기 때문에…

fflush(stdin);

을 해줌으로써 버퍼를 비워주는 프로그래밍 기법이 있다.

 하지만 이것은 엄밀히 말하면 틀린것이다. 더 정확히 이야기하자면 VC 에서만(아마도..) 된다.

 사용자들의 편의를 위해 VC에서 확장의 개념으로 만든것이라 생각하면 이해가 쉬울 것이다.

 그렇다면 왜 이것이 안되는 것일까?

C FAQ 12.26을 보면 다음과 같은 말이 나온다.


C 언어 표준에 따르면,
fflush()는 output stream에 대해서만 동작합니다.
“flush”라는 정의가 buffering된 문자들을 쓰는 것을
완료시킨다는
12.15 것이므로 문자를 취소시키는 것(discard)과는 아무런 관계가 없습니다.
따라서
입력 스트림의 입력을 무시하는 기능과는 전혀 상관이 없습니다.

아직 읽지 않은 문자(unread characters)들을 stdio input stream에서
무시하는(discard) 표준 방법은 존재하지 않습니다.
어떤 컴파일러 회사들은 fflush(stdin)이 읽지 않은 문자들을 취소할 수
있도록 라이브러리를 제공하기도 하지만, 이식성이 뛰어난 프로그램을
만들려면 절대로 써서는 안되는 기능입니다.
(어떤 stdio 라이브러리는 fpurgefabort를 같은 기능으로
제공하기도 하지만, 마찬가지로 표준은 아닙니다.)
또한 input buffer를 flush하는 것이 꼭 해결책이라고 할 수도 없습니다.
왜냐하면
일반적으로 아직 읽히지 않은 입력은 OS-level input buffer에
존재하기 때문입니다.

Input을 flush할 방법이 필요하다면 (질문
[*]
19.1과
[*]
19.2에 나온 것처럼)
시스템 의존적인 방법을 찾아야 할 것입니다.
게다가 사용자가 매우 빠른 속도로 입력하고 있고, 여러분의 프로그램이
그 입력을 무시해버릴 수 있다는 것을 꼭 염두에 두어야 합니다.

또는 n이 나오기 전까지 문자를 읽어서 무시하거나,
curses에서 제공하는 루틴인 flushinp()를 쓰면 됩니다.
덧붙여 질문
[*]
19.1,
[*]
19.2도 참고하시기 바랍니다.

 즉 원래 fflush() 함수는 output stream에 대해서만 동작하는 함수이기 때문에 input stream에는 사용할 수 없는 것이고, 또 그런 함수는 존재하지 않는다는 것이다.(물론 표준이야기이다.)

 그렇다면 어떻게 해야할까?

(Language : c)
  1. scanf(“%d”, &a);
  2.  
  3. getchar();

 이런 식으로 바로 아래 라인에 다시 입력을 받는 함수를 호출에 버퍼에 남아있는 문자들을 비워주는 것도 하나의 좋은 보기가 될 수 있다.