unix c의 아이러니..

moonzoo의 이미지

unix c라고 말하는 것이 맞을지는 모르겠지만....

C언어에서 보통 0은 false값, 1은 true값을 나타냅니다.

if문이나 while문등에서도 위와 같이 값을 평가하는데요..

문제는 unix c에서 제공하는 많은 시스템콜들이.

성공시에 0을 반환합니다.

여기서 바로 공통된 에러처리 루틴을 만드는데 골머리를 쓰게

만듭니다 --a

유닉스의 역사는 잘 모르지만 유닉스는 C로 설계되었는데.

C에서 일반적으로 false값을 나타내는 0을..

시스템콜등의 성공적인 수행값을 나타내는데 많이 사용했을까요.
(shell script들도 마찬가지구요..)

이런 생각해보신 분이나.. 대강의 이유를 알고 계신분 있나요?

bus710의 이미지

저도 그런 점이 궁금했습니다...

1. 0 이라는 값 하나라도 허투루 쓰지 않으려고-_-

2. 메모리 주소가 0x00000000 부터 시작하는 것에서 비롯된다 -_-a

c라도 배열이나 포인터 값은 0 번지 부터 시작되는 것을 봐서 그냥 진리값과는 별개로 분리해서 생각해야 하지 않나 싶습니다.

life is only one time

purewell의 이미지

실패 이유를 알 수 있죠.

0 이면 성공,
0 이외의 값이면 실패 이유를 알려주는 값을 반환하는 경우가 많습니다.

이러한 값은 errno와도 깊은 연관이 있습니다.

_____________________________
언제나 맑고픈 샘이가...
http://purewell.biz

bus710의 이미지

purewell wrote:
이러한 값은 errno와도 깊은 연관이 있습니다.

음.... 결국 처음 유닉스를 만들기 시작했을 때부터의 관습이라는 것인가요-0-;;

life is only one time

정태영의 이미지

purewell wrote:
실패 이유를 알 수 있죠.

0 이면 성공,
0 이외의 값이면 실패 이유를 알려주는 값을 반환하는 경우가 많습니다.

이러한 값은 errno와도 깊은 연관이 있습니다.

수행 중 에러가 발생했을 경우 errno 에 어떤 상황으로 인해 실패했는지를 저장해두고 -1 을 리턴하는거 아니던가요 ;)

오랫동안 꿈을 그리는 사람은 그 꿈을 닮아간다...

http://mytears.org ~(~_~)~
나 한줄기 바람처럼..

purewell의 이미지

정태영 wrote:
purewell wrote:
실패 이유를 알 수 있죠.

0 이면 성공,
0 이외의 값이면 실패 이유를 알려주는 값을 반환하는 경우가 많습니다.

이러한 값은 errno와도 깊은 연관이 있습니다.

수행 중 에러가 발생했을 경우 errno 에 어떤 상황으로 인해 실패했는지를 저장해두고 -1 을 리턴하는거 아니던가요 ;)

$ man pthread_create
$ man read

꼭 그렇지만은 아닌 것도 많죠

_____________________________
언제나 맑고픈 샘이가...
http://purewell.biz

ixevexi의 이미지

저는 제일 처음
c언어를 배울떄
보통 성공시 1을 반환하는 함수의 예제를 많이 보면서도

당췌 main은 왜 항상 return 0;인거냐...

라는 고민을 나름대로 많이 했습니다 -_-;;
저랑 비슷한 고민 맞나요? ㅋㅋㅋ

C++, 그리고 C++....
죽어도 C++

eminency의 이미지

저도 혼자서 많이 고민했던 문제입니다.

결론은... 0을 False로 보는 것은 논리적인(수학적인) 이유이며 성공시 0을 리턴하는 것은 Purewell님 말씀처럼 에러값을 다변화 하기 위한, 다분히 편의를 위한 관례적인 이유이다...라고요.

True-False와 Success-Fail의 관계는 좀 다르죠.

물론 근거없이 혼자서 내린 결론입니다 -_-

노루가 사냥꾼의 손에서 벗어나는 것 같이, 새가 그물치는 자의 손에서 벗어나는 것 같이 스스로 구원하라 -잠언 6:5

정태영의 이미지

purewell wrote:
정태영 wrote:
purewell wrote:
실패 이유를 알 수 있죠.

0 이면 성공,
0 이외의 값이면 실패 이유를 알려주는 값을 반환하는 경우가 많습니다.

이러한 값은 errno와도 깊은 연관이 있습니다.

수행 중 에러가 발생했을 경우 errno 에 어떤 상황으로 인해 실패했는지를 저장해두고 -1 을 리턴하는거 아니던가요 ;)

$ man pthread_create
$ man read

꼭 그렇지만은 아닌 것도 많죠

pthread 는 man page 2번 섹션이 아니니까 무효입니다 :twisted:

read 는 시스템콜 답게 역시 에러 상황에선 -1 리턴에 errno 변경이구요

man 2 read wrote:
.
.
interrupted by a signal. On error, -1 is returned, and errno is set
appropriately. In this case it is left unspecified whether the file
position (if any) changes.

오랫동안 꿈을 그리는 사람은 그 꿈을 닮아간다...

http://mytears.org ~(~_~)~
나 한줄기 바람처럼..

Fe.head의 이미지

결국 하나의 리턴값으로 2가지 이상을 기능할려는 것 때문에 발생한것 같습니다.

read 같은것을 보면 0이면 0바이트를 읽었다는것이고
-1이면 실패라는거죠.
결국 반환값이 2가지 이상 기능을 하고 있습니다.

int read(int fd, char * buff, size_t buff_size);
보다는

boolean read(int fd, char * buff, size_t buff_size, int * read_cnt);
이게 더 맞을것 같은데요.

고작 블로킹 하나, 고작 25점 중에 1점, 고작 부활동
"만약 그 순간이 온다면 그때가 네가 배구에 빠지는 순간이야"

정태영의 이미지

fe.practice wrote:
결국 하나의 리턴값으로 2가지 이상을 기능할려는 것 때문에 발생한것 같습니다.

read 같은것을 보면 0이면 0바이트를 읽었다는것이고
-1이면 실패라는거죠.
결국 반환값이 2가지 이상 기능을 하고 있습니다.

int read(int fd, char * buff, size_t buff_size);
보다는

boolean read(int fd, char * buff, size_t buff_size, int * read_cnt);
이게 더 맞을것 같은데요.

read 는 읽은 바이트 수를 리턴합니다 :) 그리고 c 에는 boolean 타입이 없구요

오랫동안 꿈을 그리는 사람은 그 꿈을 닮아간다...

http://mytears.org ~(~_~)~
나 한줄기 바람처럼..

cppig1995의 이미지

정태영 wrote:
read 는 읽은 바이트 수를 리턴합니다 :) 그리고 c 에는 boolean 타입이 없구요

C Programming FAQs, cinsk 님 번역 (조금 편집) wrote:
C99 (ISO/IEC 9899:1999) 표준부터 _Bool 은 내장 타입이며,
<stdbool.h> 를 #include 하면 bool, true, false 도 사용 가능합니다.
이렇게 만든 것은 기존의 코드와 충돌 방지를 위한 것입니다.

따라서 boolean 만 _Bool 로 (혹은 라이브러리의 io.h 에 <stdbool.h> 가 포함되었다면 bool) 바꾸면 되겠네요.

뭐 물론 아실테지만, 잡담 늘어놓습니다.

Real programmers /* don't */ comment their code.
If it was hard to write, it should be /* hard to */ read.

Fe.head의 이미지

정태영 wrote:
fe.practice wrote:
결국 하나의 리턴값으로 2가지 이상을 기능할려는 것 때문에 발생한것 같습니다.

read 같은것을 보면 0이면 0바이트를 읽었다는것이고
-1이면 실패라는거죠.
결국 반환값이 2가지 이상 기능을 하고 있습니다.

int read(int fd, char * buff, size_t buff_size);
보다는

boolean read(int fd, char * buff, size_t buff_size, int * read_cnt);
이게 더 맞을것 같은데요.

read 는 읽은 바이트 수를 리턴합니다 :) 그리고 c 에는 boolean 타입이 없구요


read : 읽은 바이트수 반환, 그리고 성공 실패반환
두개의 속성을 반환합니다.

읽은 속성만을 반환하는것이 아니죠.

boolean은 그냥 이렇게 했으면 좋았겠다라고 써놓은것입니다.

boolean read(int fd, char * buff, size_t buff_size, int * read_cnt);

제일 뒤에 read_cnt를 추가하였으니 몇개 읽었는지 알수 있고
반환값으로 실패 성공을 알수 있죠.

고작 블로킹 하나, 고작 25점 중에 1점, 고작 부활동
"만약 그 순간이 온다면 그때가 네가 배구에 빠지는 순간이야"

cinsk의 이미지

공통된 에러 처리 루틴을 만들 효과적인 방법은 존재하지 않습니다. 앞에서 이미 말하셨던 내용들이지만, 요약하면 몇 가지로 구분할 수 있는데:

  • 성공적으로 끝났을 때 0을, 그렇지 않은 경우 -1을 리턴하는 부류. 예: close(2)
  • 성공적으로 끝났을 때는 0또는 양수, 그렇지 않은 경우 -1을 리턴하는 부류. 예: read(2)
  • 참일 경우 0이 아닌 값을, 거짓인 경우 0을 리턴하는 부류. 예: isalpha(3)
  • 성공했을 때 어떤 non-null 포인터 값을, 실패했을 때 0 (null pointer constant)을 리턴하는 부류. 예: fopen(3)
  • 수학 함수 부류 (ISO C에 준하는...)
  • network 함수 부류 (RFCxxxx에 준하거나 POSIX에 준하는..)
입니다. 이 중에서 실패한 경우, errno을 설정하는 함수들도 있고, 그렇지 않은 함수들도 있습니다. :evil: 특히 위에서 network 함수 부류로 지정한 함수들은 errno 대신, 자체 함수 인자로 전달받은 포인터에 에러 내용을 설정하기도 합니다. 예: getipnodebyname(3), getipnodebyaddr(3). 또, errno에 쓸 수 있는 매크로 대신, 다른 종류의 에러 코드를 리턴해서, strerror(3)를 쓸 수 없는 경우도 있습니다. 예: getaddrinfo(3) getaddrinfo(3)의 경우, 독자적인 gai_strerror(3)를 써서, 에러 리턴 값을 해석합니다.

수학 함수인 경우 지저분한 것은 마찬가지 입니다. :? 자세하게 다 훑어보는 것은 불가능할 것 같으니, 간단히 말하자면, 에러가 발생하고 math_errhandling & MATH_ERRNO가 nonzero일 경우, errno를 설정합니다. 만약 math_errhandling & MATH_ERRNO가 zero일 경우, 해당하는 예외(exception)가 발생합니다. 예를 들면: invalid floating-point exception, divide-by-zero exception (infinity), overflow, underflow등등.

정말 심각한 것은, 이러한 것들이 어떤 것은 ISO C 표준에 준하고, 어떤 것은 RFCxxxx에 명시되어 있고, 어떤 것은 POSIX (Single Unix Specification) 표준이고 등등.. 이런 식이기 때문에, 따로 하나로 통일해서 규합한다거나 하는 일이 현실적으로 불가능합니다.

결론적으로 에러 처리 루틴을 각각 따로 만들던지, 아니면 상황에 따라 적용하는 수밖에 없습니다. (그나마 floating point 처리가 적고, network 관련 프로그램이 아니라면 그럭저럭 괜찮은 상황입니다.)

실제 응용 프로그램을 개발할 때에는, GNU extension인
error()를 쓰거나, 비슷한 것을 만들어 쓰는 것이 좋습니다. 또한 에러 메시지의 형태는 GNU Coding Standard에 있는 Formatting Error Messages를 참고하시는 것이 좋습니다.

여담으로, 위 가이드에 따라 에러 메시지를 만들면, emacs의 M-x compile 기능을 쓸 수 있어서 더욱 좋습니다. 8) 즉, 에러 메시지를 syntax highlight 기능을 써서 화려하게 보여줄 뿐만 아니라, 에러가 난 소스 파일의 그 위치를 자동으로, 보여줍니다.

[/]
sunyzero의 이미지

기본적으로 메인함수의 리턴은 유닉스 내부의 쉘에게까지 리턴값을 알려주기 때문에 0 을 성공, 1을 실패로 정했습니다.
이는 간단하게 EXIT_SUCCESS, EXIT_FAILURE를 보시면 되죠. 이는 참, 거짓하고는 별 상관이 없이 정해졌던것 같습니다.

그럼 왜 main()에서는 -1 은 사용하지 않았는가?
이것은 status code의 리턴값 부분이 하위 8bit 에 해당하기 때문에 실제로 읽을 수 있는 숫자는 255 영역 안에서만 결정되어야 하기 때문입니다.
이것은 waitpid(2) 함수를 보시면 이해가 가실 겁니다.

그외에 POSIX에서 규정했던 stdio.h 의 함수들은 주로 스트림을 사용하기 때문에 에러시 EOF를 의미하는 NULL을 반환하는 것이죠.

-1을 에러로 규정하는 함수들은 main()의 리턴이 아닌 다른 함수들에게서 나타나며, 이는 main과 다르게 여러가지 종료값을 사용할 수 있게 하도록 디자인되었습니다.
하나의 관습이긴한데 의미가 있는 관습입니다.

PS) 약간 의미가 이상한게 있어서 조금 수정했습니다.

========================================
* The truth will set you free.

mach의 이미지

그냥 ....

아닐까요? :twisted:

------------------ P.S. --------------
지식은 오픈해서 검증받아야 산지식이된다고 동네 아저씨가 그러더라.

lifthrasiir의 이미지

mach wrote:
그냥 ....

아닐까요? :twisted:

헉 낚였습니다 T_T

- 토끼군