뇌 마음 반반저장소

[42] 왕초보의 헤더파일(Header file) 뿌시기 본문

좌뇌

[42] 왕초보의 헤더파일(Header file) 뿌시기

맹진저 2022. 11. 30. 21:39
728x90

 헤더 파일은 libft에서 생각보다 간단하게 작성된다. 점점 프로젝트가 커지면서 많은 #define과 구조체를 쓰면서 헤더 파일은 점점 뚱뚱해진다. 오늘은 헤더의 간단한 구조를 알아보고 넘어가 보겠다!

들어가기에 앞서서 헤더 파일을 한마디로 표현한다면..? 꼬임방지! 참조 파일!이라고 할 수 있겠다.

1. 헤더파일 시작과 끝 #ifndef # endif

 헤더파일은 시작! 끝!이라는 구문을 써줘야 한다. 이는 중복으로 정의하는 것을 피하기 위한 매크로, 전처리기 지시어다.

 한 파일에 2개의 헤더파일을 여러 곳에 막 정의해 놓으면 컴파일할 때 컴파일러가 헷갈려서 에러 메시지를 보낸다고 한다. 그렇다면 한 프로젝트에서 여러 헤더 파일을 써도 괜찮은 걸까? 궁금해서 여러 헤더 파일을 쓸 수 있는지 찾아봤다. 👇

더보기

결론은 사용할 수 있다. 하지만 정렬하고 복잡하게 꼬이는 관계를 잘 계산해서 놓아야 한다.

구조체 중복 정의로 발생하는 오류 예시 이미지: https://m.blog.naver.com/clown7942/110123052710

여러 헤더파일을 사용하는 경우 예시 : https://m.blog.naver.com/kks227/220174978475

 

다른 프로젝트를 하면서 libft를 따로 폴더에 넣고 쓸 때, 헤더까지 통째로 복사해서 폴더 안에 헤더를 놓아도 괜찮았다. 그러니까 예를 들어서

1. ft_printf의 메인 헤더 ft_printf.h를 최상위 폴더에 놓고

2. libft 폴더를 하위 폴더로 놓고

3. ft_printf.h에 libft와의 관련성을 작성하고

4. 컴파일하면!

libft파일들이 정상적으로 컴파일 되는 것을 볼 수 있다.

 

하지만 나같은 베이비 개발자는 일단 헤더 하나 쓰는 거라도 잘해야겠지. 암 암.

 

#ifndef LIBFT_H // 헤더의시작을 제목과 함께 알린다.
# define LIBFT_H

#endif //헤더의 끝을 알린다.

1. LIBFT_H : 이것은 다른 c파일에 헤더로 사용될 헤더 타이틀, 특 이 헤더의 제목, 이름 이 된다.

2. #ifndef : "if not defined"의 약자로 풀어서 설명한다면 아래와 같다.

 -> 만약에 LIBFT_H가 # define메크로로 정의되지 않았다면,

 -> 다른 c파일에서 #include "libft_h"로 헤더를 호출했을 때

 -> #ifndef문 밑에부터 #endif가 나올때까지 포함된 모든 소스를 가져다 쓸 수 있어!

라는 뜻이라고 한다.. 너무 편리하지 않나요?👧🏻

 

이렇게 정의된 헤더파일의 제목을 c파일 맨 위에 이렇게 쓰고 사용하면 이 전체 헤더 파일을 사용할 수 있다.

#include "libft.h"

2. 절대 능력 #define (사용자 정의 헤더 파일)

#define은 상수값을 의미한다. 나는 수학용어를 잘 몰라서 상수를 찾아봐야만 했다..

상수(常數, constant)란 수식에서 변하지 않는 값을 뜻한다. 이것은 변하는 값 변수와 반대이다.

상수로 헤더에 한번 지정하고 나면 어떤 함수 기능에 가서도 이 상수 이름을 쓴다면 그대로 변하지 않는 값을 쓸 수 있어서 편하다. (실제로 피신 때 Rush에서 한 친구가 이 아이디어를 낸 적이 있었는데 정말 기발했다.)

# define Yaimma "goni"
# define pointA 80 //매크로 상수 예시
# define mul(X) ((X)*(X)) //매크로 함수 예시

헤더에 이렇게 입력해 놓는다면 우리는 실제 함수들에 가서도 이 값을 쓸 수 있게 된다!

 

매크로 상수로 예를 들면

while (i < pointA)

라고 적는다면 컴파일 시에 기계는 "어? pointA! 헤더에서 정해준 값!" 하고 80을 대입할 것이다.

 

매크로 함수로 예를 들면

# define mul(X) ((X)*(X))

만약 함수에서 mul(2)라고 넣었다면 실제 값은 2*2 = 4로 출력이 돼서 함수 식에서 사용될 것이다. 하지만 매크로 함수를 찾아보다가 흥미로운 점들을 발견했다. 실제로 사용하려면 아주 조심해야 하는 친구더라.

왜냐하면 일반 함수는 인수를 프로그램이 실행 중일 때 전달받지만, 매크로 함수는 인수를 컴파일 이전에 미리 치환하기 때문이다. 이런 경우는 숫자 계산을 할 때 우리가 원하는 답을 구할 수 없을지 모른다!

 

아래의 사이트에 정말 자세하게 매크로 함수에 대한 설명이 나와있다. 하이라이트만 뽑아보면 👇

더보기

!유의점!

1. 매크로 함수의 전체를 괄호(())로 감싸야합니다.

2. 매크로 함수의 인수들도 각각 괄호로 감싸야합니다.

3. 매크로 함수를 호출할 때에는 증감 연산자(++, --)나 복합 대입 연산자 등은 사용하지 않는 것이 좋습니다.

 

!매크로 함수의 장점!

1. 매크로 함수는 단순 치환만을 해주므로, 인수의 타입을 신경 쓰지 않습니다.

2. 매크로 함수를 사용하면 여러 개의 명령문을 동시에 포함할 수 있습니다.

3. 함수 호출에 의한 성능 저하가 일어나지 않으므로, 프로그램의 실행 속도가 향상됩니다.

 

!매크로 함수의 단점!

1. 원하는 결과를 얻는 정확한 매크로 함수의 구현은 어려우며, 따라서 디버깅 또한 매우 어렵습니다.

2. 매크로 함수의 크기가 증가하면 증가할수록 사용되는 괄호 또한 매우 많아져서 가독성이 떨어집니다.

 

따라서 매크로 함수는 크기가 큰 함수보다는 간단한 함수를 대체하는 데 사용하는 것이 좋습니다.

매크로 함수 : http://www.tcpschool.com/c/c_prepro_macroFunc

 

참 쉽쥬? 👧🏻

3. 이미 만들어진 함수들의 헤더 #include

#include <unistd.h>
#include "myself.h"

1. <> : 42에서 많이 사용하는 write함수는 <unistd.h>라는 헤더 파일을 사용한다. 이미 자주 쓰는 함수가 내장되어 있는 함수를 어떤 똑똑한 사람들이 만들어 놓은 것이다. 그래서 이미 존재하는 헤더를 쓸 때는 <> 꺽쇠 모양 괄호로 표시해 준다. 그러면 이미 만들어진 책 한 권을 끼워서(?) 참조하여 함께 사용할 수 있게 되는 것이다.

 

wirte함수의 원형은 아래와 같다.

ssize_t write(int fd, const void *buf, size_t count)

이 함수의 원형은 바로 <unistd.h>라는 곳에 연결이 돼있어서 우리는 write("쓰고 싶은파일", "쓰고싶은말", "쓰고싶은 말의 사이즈") 이렇게 바로 사용할 수 있는 것이다.

 

자주 쓰는 헤더를 보고 싶다면 아래를 클릭👇

더보기

헤더들은 지금 정리해 두고 나중에 프로젝트에 따로 파헤쳐 보겠다!

 

stdio.h

Standard Input/Output library (표준 입출력 라이브러리)의 약어로써, C 언어의 표준 라이브러리 함수의 매크로 정의, 상수, 여러 형의 입출력 함수가 포함된 헤더 파일이다.

 

파일 작업 관련
remove : 파일을 삭제한다
rename : 파일 이름을 변경한다.
tempfile : 임시 파일을 연다
tempnam : 임시 파일의 이름을 생성한다.

파일 접근 관련
fclose: 파일을 닫는다
fflush: 스트림을 비운다 (flush)
fopen: 파일을 연다
freopen: 다른 파일이나 모드로 스트림을 다시 연다.
setbuf: 스트림 버퍼를 설정한다.
setvbuf: 스트림의 버퍼 및 모드(fully buffered, line buffered, unbuffered)까지 설정한다.

형식 있는(formatted) 입출력
fprintf: 형식 있는 데이터를 스트림에 쓴다.
fscanf: 형식 있는 데이터를 스트림에서 읽는다.
printf: stdout에 형식 있는 데이터를 출력한다.
scanf: 형식 있는 데이터를 stdin에서 읽는다.
sprintf: 문자열에 형식 있는 데이터를 쓴다.
sscanf: 문자열에서 형식 있는 데이터를 읽는다.
vfprintf : 스트림에 형식 있는 가변 인자 목록(variable argument list)을 쓴다.
vprintf : stdout에 가변 인자 목록을 출력한다.
vsprintf : 문자열에 가변 인자 목록을 출력한다.

문자(character) 입출력
fgetc: 스트림에서 문자를 받는다.
fgets: 스트림에서 문자열을 받는다.
fputc: 스트림에 문자를 쓴다.
fputs: 스트림에 문자열을 쓴다.
getc: 스트림에서 문자를 받는다.
getchar: stdin에서 문자를 받는다.
gets: stdin 에서 문자열을 받는다.
putc: 스트림에 문자를 쓴다.
putchar: 문자를 stdout에 쓴다.
puts: 문자열을 stdout 에 쓴다.
ungetc: 스트림으로부터 문자를 되돌린다.

직접적인 입출력:
fread: 스트림으로부터 데이터 블록을 읽는다.
fwrite: 스트림에 데이터 블록을 쓴다.

파일 위치 지정
fgetpos: 현재 스트림의 (읽어 들이는) 위치를 얻는다.
fseek: 스트림 위치 표시자의 위치를 재조정한다.
fsetpos: 스트림의 위치 표시자를 설정한다.
ftell: 스트림의 현재 위치를 얻는다.
rewind: 위치 표시자의 위치를 맨 앞으로 조정한다.

오류 처리
clearerr: 오류 표시자를 초기화한다.
feof: 파일 끝 표시자 (End of file indicator)을 확인한다.
ferror: 오류 표시자를 확인한다.
perror: 오류 메시지를 출력한다.

매크로
EOF : 파일 끝(end of file)
FILENAME_MAX : 파일 이름의 최대 길이
NULL : 널 포인터
TMP_MAX : 임시 파일의 (최대) 개수

타입
FILE : 스트림을 제어하는 정보를 가지고 있는 객체
fpos_t : 파일의 (읽어 들이는) 위치를 설정하기 위한 정보를 가지는 객체
size_t : 부호 없는 정수

 

unistd.h

유닉스 계열에서 사용하는 표준 심벌들과 상수를 정의해놓은 헤더 파일. 얘는 윈도에서 못쓴다...!

함수 : write, read

 

string.h

 C 언어의 표준 라이브러리로, 메모리 블록이나 문자열을 다룰 수 있는 함수들을 포함하고 있다.

 

문자열 복사 함수
memcpy : 메모리의 특정한 블록을 복사한다.
memmove : 메모리의 특정한 블록을 이동시킨다.
strcpy : 문자열을 복사한다.
strncpy: 문자열에서 지정한 부분만큼 복사한다.

문자열 합치는 함수
strcat : 두 개의 문자열을 합친다.
strncat : 두 개의 문자열을 지정한 부분 만큼 합친다.

문자열 비교 함수
memcmp : 메모리의 두 부분을 비교한다.
strcmp : 두 개의 문자열을 비교한다.
strcoll : locale을 이용하여 두 개의 문자열을 비교한다.
strncmp : 두 개의 문자열의 일부분을 비교한다.
strxfrm: locale 을 이용하여 문자열을 변환한다.

문자열 검색 함수
memchr: 메모리 블록에서 특정한 문자를 찾는다.
strchr : 문자열에서 특정한 문자를 찾아 그 위치를 구한다.
strcspn : 특정한 문자열에 포함된 문자들을 다른 문자열에서 찾아 가장 먼저 나오는 것을 구한다.
strpbrk: 특정한 문자열에 포함된 문자들을 다른 문자열에서 찾아 일치되는 것을 가리킨다.
strrchr : 문자열에서 특정한 문자를 찾되, 뒤에서 부터 찾는다.
strspn : 특정한 문자열에서 다른 문자열에 포함되어 있는 부분을 찾되, 처음부터 연속된 부분으로 구한다.
strstr : 특정한 문자열을 다른 문자열에서 검색한다.
strtok : 문자열을 토큰으로 분리한다.

다른 잡다한 함수들
memset : 메모리 블록을 채운다.
strerror : 오류 메시지 문자열을 가리키는 포인터를 구한다.
strlen : 문자열의 길이를 잰다.

매크로
NULL : 널 포인터

타입
size_t : 부호 없는 정수

 

stdlib.h

C 언어의 표준 라이브러리로, 문자열 변환, 사 난수 생성, 동적 메모리 관리 등의 함수들을 포함하고 있다.

 

동적 메모리 관리 함수

malloc : size 바이트의 메모리를 힙에서 할당하여 반환한다.

calloc :  (num * size) 바이트의 메모리를 힙에서 할당하여 반환한다.

realloc : ptr이 가리키는 메모리를 size 바이트만큼 힙에서 재할당하여 반환한다.

free : ptr이 가리키는 메모리를 해제한다.(할당했으면 반드시 해제해야 한다.)

 

문자열 변환 함수

atof : str을 double으로 변환한다.

atoi : str을 int로 변환한다.

atol : str을 long으로 변환한다.

strtol : str을 base진법으로 long으로 변환한 뒤 endptr!=NULL이면 숫자가 끝난 후 첫 문자의 위치를 endptr에 반환한다.

 

의사 난수 생성

rand : 0부터 RAND_MAX 사이의 의사 난수를 반환한다.

srand : 의사 난수 발생기를 seed로 초기화한다. 보통 seed는 time(NULL)로 설정된다.

 

stdarg.h

C의 C 표준 라이브러리의 헤더로 인자 수를 제한 없이 할 수 있도록 하는 함수를 허용할 수 있도록 한다

 

va_start()
va_start() 매크로는 이후 va_arg() 및 va_end()가 쓸 수 있게 ap를 초기화하며, 따라서 가장 먼저 호출해야 한다.
인자 last는 가변 인자 목록 전의 마지막 인자, 즉 호출 함수에서 타입을 알고 있는 마지막 인자의 이름이다.
이 인자의 주소를 va_start() 매크로 내에서 사용할 수도 있으므로 레지스터 변수로 선언되어 있거나 함수나 배열 타입으로 선언되어 있어선 안 된다.

va_arg()
va_arg() 매크로는 호출의 다음 인자의 타입과 값을 가진 식으로 확장된다. 인자 ap는 va_start()로 초기화 한 va_list ap이다. va_arg()를 호출할 때마다 ap를 변경해서 다음 호출 때 다음 인자를 반환하도록 한다. 인자 type은 타입 이름으로, type에 *만 붙이면 지정한 타입의 객체에 대한 포인터 타입을 얻을 수 있도록 지정한다.

va_start() 매크로 다음으로 처음 va_arg() 매크로를 쓰면 last 다음 인자를 반환한다. 이어지는 호출이 나머지 인자들의 값을 반환한다.
다음 인자가 없거나 type이 (기본 인자 승격 방식에 따라 승격된) 실제 다음 인자의 타입과 호환되지 않는 경우 정해져 있지 않은 오류가 발생하게 된다. ap를 어떤 함수로 전달하고 그 함수에서 va_arg(ap, type)을 사용하는 경우에 함수 반환 후 ap의 값은 규정되어 있지 않다.

va_end()
각 va_start() 호출에는 같은 함수 내에 대응하는 va_end() 호출이 있어야 한다. va_end(ap) 호출 후 변수 ap는 규정되어 있지 않다. va_start()와 va_end()로 각각 감싸서 목록을 여러 번 순회하는 것이 가능하다. va_end()가 매크로일 수도 있고 함수일 수도 있다.

va_copy()
va_copy() 매크로는 (앞서 초기화 한) 가변 인자 목록 src를 dest로 복사한다. dest에 같은 last 인자로 va_start()를 적용하고 이어서 현재 src 상태에 도달하기까지 한 것과 같은 횟수의 va_arg() 호출을 적용한 것처럼 동작한다.
단순 명백한 구현 방식에서는 va_list가 가변 인자 함수의 스택 프레임에 대한 포인터일 것이다. 그런 (가장 흔한) 방식에서는 다음 할당이 아무 문제가 없을 것이다.

 

fcntl.h

파일을 컨트롤하는 옵션 자세한 설명 👇

https://www.joinc.co.kr/w/Site/system_programing/File/Fcntl

 

출처

https://wariua.github.io/man-pages-ko/stdarg%283%29/#bugs

https://modoocode.com/34

https://www.ibm.com/docs/ko/i/7.5?topic=files-stdargh

 

2. "" : 내가 만든 헤더 파일은 저렇게 "" 말 따옴표를 사용해서 정의해 준다.

4. 절대 변수 만들기, 구조체! (사용자 정의 헤더 파일)

이번 기회에 구조체를 확실히 알고 넘어가야겠다. 왜냐하면 구조체가 추후에 진행되는 프로젝트들의 알고리즘을 코딩할 때 정말 많이 사용되기 때문이다... 구조체의 꽤나 길기 때문에 자세한 공부는 다음장에서 해보겠닷!

👇구조체에 대해서 더 알아보려면 👇

작성 중...

5. 내가 가진 함수들의 목차, 함수 선언!

일단 이 부분은 앞에서도 이야기했다시피 전체 코드의 맨 앞장. 목차!라고 생각하면 편하다. 책장을 넘기면서 "어.. 그 함수 어디 갔지.. 그 조건 이름 어디 갔지... " 할 때, 목차, 즉 헤더 파일로 가면 "응! 여기 있어 일로 가면 돼!" 하고 알려주는 역할을 한다. libft를 완성하고 나면, 이 헤더 안에 내가 만든 함수의 이름을 통째로 헤더에 실어야 한다. 예를 들어,

int		ft_isalnum(int c);
int		ft_isalpha(int c);
int		ft_isascii(int c);
int		ft_isdigit(int c);

이런 식으로 함수 이름과 변수를 담은 첫 줄을 마지막에 꼭 콧수염(세미콜론)과 함께 작성해 주어야 한다! 

참 쉽죠? 👧🏻

 

4. 그 외 여러 물음표들

 1. 전역변수와 지역변수란?

전역 변수는 어떤 변수 영역 내에서도 접근할 수 있는 변수를 의미하는 전산학 용어이다. 지역 변수와 대비되는 개념.

전역변수는 "국내 전역에서 사용할 수 있는 인터넷 입니다! 유후!" 라고 생각하면 빠를까.. 그것 처럼 전역에서 사용할 수 있는 변수이다. 

지역변수는 함수 안에서만 사용되는 변수이다. "지역 안에서만 이용 가능한 서비스입니다. 엣헴~" 정도라고 볼 수 있겠다. 지역을 벗어나면 사용할 수 없는, 함수를 벗어나면 사용할 수 없는 값인 셈이다!

 

보통 이렇게 사용된다.

#include <stdio.h>

int	global = 3; //전역변수. 밖에 있어서 이 페이지에 있는 아무 함수에서나 쓸 수 있다.

int	main()
{
	int local = 1; //지역함수. main이라는 지역 안에 있는 곳에서만 유효하다.
    
    printf("%d\n", global + local);
    
    return(0);
}

프린트 되는 값은 4!

하지만 전역변수는 줏대가 없는 변수이기 때문에 잘 사용하지 않는다고 한다. (->어떤 스코프에서도 참조하고, 변경할 수 있기 때문에 지역성이 없다.)

*흥미로운 점 : Extern을 붙이면 어떻게 될까?

extern 변수는 외부에서도 사용할 수 있다는 뜻이다. 이 변수는 main.c라는 파일에서도 main1.c라는 파일에서도 사용할 수 있는 변수이다. 하지만 빌드에러가 나기 때문에 그냥 전역변수로 선언을 하고 사용해도 된다고 한다. 

 2. 변수들과 함수들은 어떻게 저장될까?

각각의 함수들이 실행될 때 할당되는 메모리는 모두 다르다. 메모리의 영역은 크게 4개로 나뉘어 지고 1번부터 4번까지 가장 최초의 주소에서 먼 주소로 저장된다. 

 

1. 코드 영역 (Code) :  이 영역은 함수, 제어문, 상수처럼 컴퓨터가 처리할 기계어 코드가 들어간다. CPU가 명령을 읽고 처리한다. 이 영역은 프로그램이 시작되고 끝날때 사라진다.

 

2. 데이터 영역 (Data + BSS) : 전역변수, Static, const,구조체로 선언된 변수가 이곳에 들어간다.  얘도 프로그램이 시작되고 끝날때 메모리가 소멸된다. (여기 저장된 변수들은 끝날 때 까지 살아있겠죠?)

 

3. 힙 영역 (Heap) : 동적할당(malloc)으로 프로그래머들이 빌릴 수 있는 공간. 메모리는 낮은 주소에서 높은 주소로 순차적으로 저장된다. 선입선출법으로 처리한다. 선입선출이란? 👇

더보기

선입 선출(先入先出, first in, first out, 줄여서 FIFO)은 시간과 우선 순위와 관련된 데이터를 정리하고 이용하는 방식을 줄여 말하는 것이다. 

 

4. 스택 영역 (Stack) : 지역변수, 함수 내에 할당된 변수들을 저장한다. 함수가 끝나면 이 메모리 공간도 사라진다. 메모리는 높은 주소에서 낮은 주소로 순차적으로 저장된다. 선입후출법으로 처리한다. 선입후출이란?👇

더보기

후입 선출(後入先出) 또는 LIFO(last in, first out)는 작은 문을 가진 좁고 둘러싸인 승강기로 예를 들 수 있다. 승강기가 도착지에 다다를 때, 마지막에 탄 사람은 먼저 내려야 한다.

 

그렇다면 선입후출이란 시간과 우선 순위와 관련된 데이터로 착착 내려온 데이터를 가장 나중에 들어온 것 부터 쓴다는 것. 마지막에 바뀐 변수의 값이 최종 값이다!

*흥미로운 점 1 : # define은 어디에 저장이 될까?

이 매크로는 단순히 정의 된 것을 치환하는 역할로 메모리로써 공간 할당이 안된다! 그냥 정의값을 만나면 헤더로 와서 그 값으로 바꿔주기만 한다는 사실!

*흥미로운 점 2 :  배열로 저장된 변수 값, 포인터로 저장된 변수값은 어디에 저장될까?

char	str[] = "ABC"; // 1
char	*str = "ABC"; // 2

이 두곳에 저장되는 장소가 다른 것은 정말 흥미롭다! 

1. str[] : 배열에 저장되는 문자는 스택에 할당이 된다. 그래서 함수가 끝나면 이 배열은 사라진다.

2. *str : const char 문자열 상. 수. 로 데이타 영역에 저장된다. 즉 static과 같은 영역으로 프로그램이 끝날 때 까지 값이 유효하다.

 

예를 들면,

#include <stdio.h>

char    *str1()
{
    char str[] = "ABC";
    return (str);
}

char    *str2()
{
    char *str = "ABCDE";
    return (str);
}

int main()
{
    printf("배열일 경우 : %s\n", str1);
    printf("포인터일 경우 : %s\n", str2);
    return(0);
}

결과는..!

> gcc main.c
main.c: In function 'str1':
main.c:6:12: warning: function returns address of local variable [-Wreturn-local-addr] 
    6 |     return (str);
      |            ^

!경고: 함수가 로컬 변수의 주소를 반환합니다.! 

str[] 문자열은 str1 함수에서만 사용하고 변경할 수 있는 함수이다. 그래서 밖으로 가지고 나가려 하면 사용할 수가 없는 것이다! (이것때문에 한세월 보낸 적이 있었드랬죠..)

 

2. Static 정적함수들은 헤더에 포함할까?

Static은 정적이란 뜻이다. 파도가 쳐도.. 피바람이 불어도.. 이 단어가 붙으면 정적으로 고정되었다는 의미랄까.. 정적 함수와 변수를 함께 알아보자.

 

1. 정적 함수 : 정적함수는 해당 파일에서만 사용이 가능한 함수가 된다. 그래서 헤더파일에는 넣지 않아도 된다는 사실! 팀을 이뤄 작업을 할 때 다른 작업자들과 충돌을 방지하기 위해, 동일의 함수의 이름과 충돌을 방지하기 위해 사용한다고 한다.

2. 정적 변수 : 정적 변수는 전역변수에서도 지역변수에서도 둘 다 사용할 수 있다. 정적 변수는 포함된 함수 안에서만 사용하고 끝난다는 사실! 정적변수는 printf에서 더욱 자세히 살펴본다. 

[42_ft_printf] printf 만들기2

 

 

하.. 중간에 한번 날라가서 너무 힘들었지만.. 이제 구조체를 파헤쳐 볼까!

 

 

도움을 주고 싶으신 내용이나

틀린 내용이 있다면 댓글로 남겨주시고,

도움이 되었다면 공감 한 번씩 눌러주세요👍❤️

 

728x90

'좌뇌' 카테고리의 다른 글

[42] 왕초보의 구조체(struct) 뿌시기  (2) 2022.12.02
[42] 왕초보의 Makefile 뿌시기  (2) 2022.11.28
Comments