DLL,LIB, FILEIO에 대한얘기

2021. 11. 13. 22:43c,c++ 기본/next step

320x100

안녕하세요 대학생개발자입니다.

이번 내용에서는 dll과 lib에 대한 차이점을 아주 간단하게 정리하고

FILE IO에 대해서 얘기를 좀 해보려고합니다.

 

음... 우선 우리가 dll이나 lib를 쓰는 이유가 뭘까에 대한 고민을 우선 해봐야겠는데요...

당연히 dll과 lib가 없으면 무조건적으로 안된다는 아닙니다. 왜냐면 소스를 그대로 주는 것과 dll, lib를 주는것과는 다를바가 없거든요... 어차피 내가 주려고 하는 라이브러리를 준다는 부분에서는 동일한거니까요

 

하지만 source code의 경우에는 역시... 고대로 전달하게 되면 내 모든것을 공개하는것과 동일합니다... 남이 내가 어떻게 코드를 짰는지 노출의 위험이 있는 것부터 시작해서 내가 만들어놓은 코드를 변경할 여지도 있고 이런 위험이 있습니다.

그렇기 때문에 우리는 사실상 라이브러리를 준다고 하면 lib나 dll을 주는것입니다.

그럼 여기서 dll과 lib가 각각 무엇이냐?

dll은 dynamic link library입니다. 동적으로 링크되는 라이브러리라는거죠. 반대로 lib의 경우에는 static입니다. 정적으로 링크된다는 의미죠.

우린 동적, 정적의 의미를 아마 처음으로 본게 동적할당과 정적할당 즉 변수에 대해서 얘기할때 알게되었을 겁니다. 정적이라는건 컴파일타임에 정해진다는거죠... 반대로 동적이란건 런타임에 정해진다는거구요...

여기서 라이브러리또한 거의 똑같습니다 단 라이브러리는 정적 라이브러리의 경우 link때 결정된다는 것이죠.

 

lib의 경우에는 실행파일 내부에 소스코드가 포함이 됩니다. 물론 .cpp나 .h와 같이 볼 수있는 그런 모습은 아니지만 말이에요... 내 프로그램은 이 파일을 링킹을 하면서 포함시켜서 실행파일에 넣어버립니다. 즉 실행파일의 사이즈가 커지게되겠죠.

 

반대로 dll의 경우에는 실행파일 내부에 소스가 포함되지 않습니다. 당연히 cpp, h처럼 볼 수 있는것도 아니구요... 얘의 경우에는 dll이 따로 하나의 프로세스처럼 메모리에 올라가있습니다. 그리고 그 메모리를 여러 프로세스가 같이 쓰는거구요. 장점이라고하면 실행파일이 매우 가벼워지겠죠. 무거운 라이브러리를 다른 메모리로 빼놔 버렸으니까요.

 

음... 이렇게 말해보니 둘다 각기 장단점이 있습니다.

저같은 경우에는 서버를 개발하려는 입장이다보니... 그냥 서버가 크든 말든 상관없습니다... 서버 컴퓨터에는 따로 뭐 받을 것도 없을뿐더러 dll의 경우에는 이것저것 따로 설치해야되는것도 생기고 코드하나 바뀜으로 인해서 다른 프로세스들도 다 영향을 받기 때문에 그냥 static library가 훨씬더 유용할 것이라고 생각하고있습니다. (물론 취업을 하면 생각이 바뀔지도 모릅니다). 당장은 dll보다 lib에 대해서 더 긍정적인 입장입니다.

 

뭐... 일단 똥싸다 만느낌이긴 하지만 여기까지 정리를 해놓고

FILE IO로 넘어가보겠습니다.

 

일단 io로 우리가 가장 많이 쓰는건 printf, scanf 이런게 있을건데요... 당연히 이것도 io입니다.

콘솔에 입력하고 출력하는거니까요... 당연한거겠지만 file도 io가 존재합니다.

아마 scanf를 자주 쓰시는 분들이 이런 말씀을 많이 하시더라구요... 왜 scanf는 %d를 입력해야되냐 이런거말이죠...

왜냐면 scanf는 포멧입력입니다. %d가 들어가면 얘는 문자열 파싱을 통해 아 얘가 decimal을 입력하려는 거구나 라고 판단하는거죠... 아마 이런 경험을 해보신 분들 있을겁니다. scanf에 문자열을 공백을 두고 입력하셨던 분들이 분명히 있을건데요... 이럴경우 두번째 scanf가 씹히게 되는 경우가 발생한 적이 있을겁니다. 아마 이런 경우를 해결하려고 fflush를 쓰시는 분들이 분명히 있으실거 같은데... 사실 fflush는 그런 기능을 하는 함수가 아닙니다... 있는 버퍼를 모조리 밀어버리는 기능을 하는 함수지 사실 빼는 기능을 하는 함수는 아닙니다. 빼는 기능을 하시려면 rewind를 쓰셔야됩니다. rewind는 스트림을 초기화 시켜버리는 함수거든요... 음... 링버퍼로 따지면 그냥 writePointer = readPointer = begin해버리는것과 같은거죠.

흠... 또 삼천포로 셀뻔했지만 일단 제대로 잡았습니다.

그 file io에 대한 간단한 정리를 하고 좀 철학이라고해야되나... 뭐 어째뜬 심도있는 논의를 해봐야할 부분이 있어서 그부분에 대해서 짧게 적어보겠습니다.

 

우선 윈도우즈에서 파일을 읽는 법은 file에 대한 position이 존재합니다.

position을 앞으로 땡길수도있고 뒤로 밀수도있습니다. 이걸 통해서 우리는 read를 하기도하고 write를 하기도하는거죠.

일단 FILE*를 이용한 파일 오픈을 할때는 옵션을 넣는데요... 대체적으로 w,r, 등등... 뭐 이런거 넣습니다... 하지만 윈도우는 디폴트로 r,w 등을 넣게되면 텍스트 모드로 읽게됩니다. 즉 읽는 방법이 달라지는겁니다. newline charactor의 경우에는 0d0a 1a의 경우에는 EOF등 이런식으로 semantics가 존재합니다. 그러므로 바이너리 모드로 읽으려면 b를 명시적으로 적어주어야 합니다.

+추가로

file을 오픈할때 nullptr을 잘 체크하세요 의외로 실패하는 경우가 많습니다... 추가로 다른데서 사용하고 있으면 열지 못한다는 점도 유의하시길 바랍니다.

file을 종료하지 않고 계속 열어둔 채로 사용하는것도 좋지 않습니다. 파일을 사용했으면 재빠르게 닫아주는게 좋은데요... 이유가 버퍼링을 하다가 고대로 터져버리면 flush되는게 아니라 그냥 없어집니다... 그래서 썻으면 무조건 close하는게 일반적인 방법입니다 혹은 꾸준히 fflush같은걸 이용해서 플러시를 해주던가 하십셔.

 

일단 뭐 저건 크게 중요하지 않습니다 구글에 검색하면 금방 나오는 내용이니까요... 일단 서버개발자라면 당연히 퍼포먼스가 매우 중요할 겁니다.

그리고 모든 io는 느립니다.

그렇다면 file io를 하면 안되는걸까요?? 이것도 말이안되죠..?

당연히 file io는 해야되는겁니다. 그러면 어떻게하느냐...

file io를 최소한으로 줄이는겁니다. 한번에 사이즈를 다 알아내서 메모리에 올린다음 우리 메모리에서 작업을 하게되면 더 빨리 처리할 수 있다는 겁니다.

fread함수에 대해서 간단히 정리좀 하겠습니다. 말그대로 file을 읽는함수입니다.

fread(buffer, size, count, fp) 이렇게 파라미터가 들어가는데요

여기서 size와 count의 의미가 뭐냐..?

size는 말그대로 몇바이트씩 읽어줄까 입니다. count는 size를 몇번읽어줄까 입니다...

물론 size만큼 count번 읽어주는 행동을 하지는 않습니다.

음... 결국 읽는건 하나하나 읽어오는거구요 여기서 의미하는 바는

크게 2가지 정도로 볼수있겠습니다.

1번째로 타입에 대한 의미입니다.

int의 경우에는 4바이트죠. 만약 파일로 저장된게 int가 100개 저장된거라면

fread(buffer, 1, 400, fp); 라고 해도되겠지만

fread(buffer, sizeof(int),100, fp); 라고하는게 훨씬더 명시적이고 직관적일겁니다. 무슨 의도로 저렇게 했는지도 확 눈에 띄구요...

2번째 경우에는 내가 읽어야할 바이트수를 모를때입니다. 물론 바이트를 얼마나 모른다면 fseek, ftell과 같은 함수로 찾을 수 있습니다. 하지만 그냥 왕창 읽어버리고 싶다면 return값으로 몇바이트 읽었는지 받아버리고 size를 1로 count를 아주 큰수로 잡아버리면 될것입니다. 그렇게 한다면 내가 몇바이트 읽을지는 모르지만.. 이만큼 읽었다고 알려주는거죠... 

 

일단 제가 생각하는 2가지 이유고요 사실 이거말고도 다른 이유가 분명 존재할 수 있습니다. 하지만 저는 아직 코린이기 때문에 두가지 경우밖에 파악을 못했구요... 만약 알고계신 분이 있다면 몰래 알려주시면 저혼자 간직하겠습니다 ㅋㅋㅋㅋㅋㅋㅋㅋ

 

만약에 버퍼가 무한히 크다면 당연히 fread에서 엄청나게 큰수를 집어넣으면 그만입니다. 그럼에도 불구하고 우리는 버퍼를 잡을때 합리적인 사이즈로 잡을 필요가 있습니다. 스택오버플로가 나면 안되거나 혹은 힙을 너무 크게 잡는다면 bad_alloc이 튀어나오거나 아니면 nullptr이 반환될 테니까요...

 

그럴때 쓸수있는 방법이 fseek함수를 이용해 file의끝으로 이동시킨후 ftell함수를 이용해서 사이즈를 얻고 그만큼 메모리 확보한 다음에(동적으로) 거기다가 fread로 왁하고 한번에 읽어버리는거죠.

물론 1000바이트씩 쪼개서 읽어도 되긴합니다. 하지만 이경우 file io를 많이 하게 되는 케이스로... 당연히 속도 저하가 발생합니다...

그러니까 우리는 무조건 메모리에 한번에 왕창 올려놓고 쪼금쪼금씩 메모리에서 갉아먹으면서 쓰자구요... 읽어 바로 파일 닫아버리고 그리고 메모리로 작업하는거죠 얼마나 좋아요? 안그래도 파일포인터 사용하기 어려운데 그냥 포인터만 사용하면 되는거잖아요 이젠...

 

fwrite의 경우에도 마찬가지입니다. 

우리는 fread와 마찬가지로 한번에 동적할당으로 필요한만큼 확 다 메모리에 올린다음에 한번에 왁하고 집어넣어 버릴겁니다. io는 최소한으로 가져가는게 철칙이란거죠 하하하

 

뭐 진지하게 논의해봐야 될 부분이 있다고 말씀을 드렸는데 결국 혼자서 이게 맞다고 난리를 쳤습니다...

하지만 io가 느린건 분명하니까요... 그러니까 최소한으로 씁시다.

 

자... 그럼 오늘은 여기서 정리를 해야겠군요

다들 오늘도 긴글읽어주셔서 감사드립니다.

다음번에는 더 좋은 내용으로 찾아오겠습니다.

그럼 안녕히 계세요~

320x100

'c,c++ 기본 > next step' 카테고리의 다른 글

new와 delete에 대한 얘기  (0) 2021.11.20
메모리와 c++에 관한 얘기  (0) 2021.11.14
게임서버 이론1  (0) 2021.11.09
시간과 랜덤에 관한 얘기  (0) 2021.11.08
문자열과 해시에 대한 얘기  (0) 2021.11.07