요약정뤼중~

구조체(struct)

여러가지 변수를 모아둔 것.

선언하는 법

/* 노미네뜨 검증 X */
struct Person
{
    char name[20];
    int age;
    char address[100];
};

struct Person p; /* 변수 만들때 .*/

예제

/* 노미네뜨 검증 X */
#include <stdlib.h>
#include <stdio.h>

struct Person
{
	char name[20];
	int age;
	char address[100];
};

int main()
{
	struct Person p; /* 실제 변수를 만듬*/
	p.age = 5;		 /* 변수의 멤버에 접근 */
	printf("%d\n", p.age);

	struct Person *pp;
	pp = &p;
	pp->age = 10; /* (*pp).age = 10 과 동일 */
	printf("%d\n", pp->age);

	struct Person *malp;
	malp = (struct Person *)malloc(sizeof(struct Person) * 1); /* sizeof 를 이용하여 필요한 메모리 계산 */
	malp->age = 20;
	printf("%d\n", malp->age);
}
5
10
20

Norminette

  • 이름은 알파벳 소문자, 숫자, ‘_’ (Unix 문자) 로만 이루어져야 합니다.
  • struct의 이름은 s_ 로 시작해야 합니다.
  • struct, union, enum을 선언할 때에는 탭을 삽입하시기 바랍니다.
  • struct, union, enum 타입의 변수를 만들 때에는 자료형에 스페이스가 하나 존재해야 합니다. (예: struct스페이스바s_abc탭a;)
  • .c 파일에 선언하는 하지 마십시오.

typedef

struct, union, enum을 좀 더 간편하게 쓸 수 있도록 별칭을 만드는 것. 예를 들어 이미 만들어둔 s_abc 구조체에 대해 typedef struct s_abc t_abc 라고 하게 되면 앞으로 struct s_abc 라고 길게 쓰지 않고 t_abc 로 간편하게 쓸 수 있다.

Norminette

norminette 안걸리게 하기. 내부 인자에 맞게 t_bar를 맞춘 다음, s_abct_bar에 맞춘다. t_abc는 그냥 직전에 탭 하나만 있으면 되는 듯.

정적 라이브러리 (.a 파일)

뭔가 라이브러리 부분은 러쉬나 BSQ에서 이용하진 않을 것 같은 뇌피셜

# 파일은 mysum.c, mysum.h 두 개의 파일이 존재한다고 가정
gcc -c mysum.c
ar rcv libmysum.a mysum.o

정적 라이브러리는 단순히 보통의 목적파일(object file)의 모음이다. 관례적으로, 정적 라이브러리는 “.a”의 확장자로 끝난다. 이것은 ar(archiver)프로그램에 의해서 만들어진다.

위의 프로그램을 컴파일 하기 위해서는 라이브러리의 위치와 어떤 라이브러리를 사용할것인지를 알려줘야 한다. 라이브러리의 위치는 ‘-L’ 옵션을 이용해서 알려줄수 있으며, ‘-l’ 옵션을 이용해서 어떤 라이브러리를 사용할것인지를 알려줄수 있다. -l 뒤에 사용될 라이브러리 이름은 라이브러리의 이름에서 “lib”와 확장자 “a”를 제외한 나머지 이름이다. 즉 libmysum.a 를 사용할 것이라면 “-lmysum” 이 될것이다.

gcc -o print_sum print_num.c -L./ -lmysum

동적 라이브러리를 배우지는 않지만 간략히 언급만 하고 넘어간다면, 동적 라이브러리는 정적 라이브러리처럼 컴파일 시 링크되는 개념이 아니라, 컴파일 때에는 본체 프로그램만 하고, 라이브러리는 프로그램을 실행할 때 가져온다는 차이가 있다. (.dll 파일)

헤더 파일

헤더 파일과

헤더 파일과 라이브러리의 차이가 뭐냐고 묻는다면, 이는 사실 완전히 성격이 다른 것이다. 라이브러리란 유용하게 갖다 쓸 함수들이 컴파일된 채로 있는 걸 의미하며, 헤더 파일은 어떤 기능을 구현하고자 할 때 “구현부인 소스 파일과 선언부인 헤더 파일로 나누

컴파일 과정

  1. 전처리기: #define, #include 등의 매크로를 처리하여 매크로 없는 깔끔한 소스 파일을 제작함 #include 는 기능적으로는 복사 붙여넣기와 같음.
  2. 컴파일. 소스코드를 실행가능한 파일로 변환하는 과정. 문법 에러 등 에러 발생시 실패. 성공하면 목적 파일(오브젝트 파일, .o 파일)이 생성됨.
  3. 링크. 실행가능한 파일들을 묶고 조합하는 과정. 함수 등의 기능을 실제로 사용하려고 하지만, 그 내용을 찾는 데 실패했을 때(구현부를 컴파일할 때 포함시키지 않으면) 에러가 뜸. 구체적인 에러 위치를 알려주지 않음.

Makefile

gcc -o .. 소스파일.. 라이브러리.. 등등.. 너무 명령어가 길어지니까 이것들을 절차에 맞게 잘 컴파일할 수 있도록 컴파일 방법을 Makefile 이라는 파일에 적어두고, make 명령어로 컴파일 및 빌드할 수 있음.

파일 내부 구조

  • 매크로 : 반복적으로 쓸 문자를 변수처럼 정의해줌. 매크로에 매크로를 정의할 수 있음. 매크로를 불러올 땐 $(매크로이름)로 함.
  • 타겟절 : 목표, 룰, 라벨이라고도 함. 목표! 기본적으로 만들어야 할 파일 이름이 옴. (예: something.o) 후술할 특수 목적 타겟도 있음. (all, clean, re 등)
  • 의존성 : 해당 타겟이 완성되기 전에 미리 완성되어야 할 다른 타겟들을 적음.
  • 명령어 : 해당 타겟을 완성시키기 위한 명령어를 적음. 앞에 탭 하나가 있어야 함. 해당 타겟을 완성하려고 할 때 실행되는 실제 명령어

특수 매크로

  • $@ : 해당 타겟을 의미. (타겟 정의시 : 왼쪽에 있는 문자들)
  • $^ : 해당 모든 의존성을 의미. (타겟 정의시 : 오른쪽에 있는 문자들)

기초 실행법

Makefile이 있는 폴더 (프로젝트 폴더)에 들어가서 make 명령어를 치면된다. 아무런 인수 없이 make 명령을 실행한다면 최상단에 있는 목표(룰, 타겟)가 실행된다. 그러므로 all 룰을 제일 위에 두자.

예제

CC = gcc 
AR = ar
RANLIB = ranlib
CFLAGS = -Wall -Werror -Wextra  # TARGET을 만들 때 사용될 C 플래그
TARGET = power_exe # 최종 생성될 파일
OBJECTS = main.o # Target을 만들 때 사용될 오브젝트 파일

LIB_NAME = test# 라이브러리의 이름 지정
LIB_OBJS = test.o abcd.o # 라이브러리에 사용할 목적 파일들 지정
LIB_FILE_NAME = lib$(LIB_NAME).a # 실제 생성될 라이브러리 파일 (확장자는 .a)
LIBS += -ltest # GCC 라이브러리 지정 옵션 (-l이름 == lib이름.a)
LIB_DIR = -L. # GCC 라이브러리 경로 지정 옵션

INC_DIR = -I./inc # #include 할 헤더 파일이 있는 경로들. 여기 기반으로 헤더파일 검색함.

all : $(LIB_FILE_NAME) $(TARGET)

$(LIB_FILE_NAME) : $(LIB_OBJS)
	$(AR) rcv $@ $^
	$(RANLIB) $@

$(TARGET) : $(OBJECTS)
	$(CC) $(CFLAGS) -o $@ $^ $(LIB_DIR) $(LIBS) $(INC_DIR)

%.o : %.c
	$(CC) $(CFLAGS) -c -o $@ $^ $(INC_DIR)

clean:
	rm -f $(OBJECTS) $(LIB_FILE_NAME)

fclean: clean
	rm -f $(TARGET)

re: fclean all

.PHONY: all clean fclean re

.PHONY : 만약 폴더에 이름이 clean 이라는 파일이 존재하면, make clean 이라는 명령을 수행해도 이 타겟은 이미 완료되었다고 간주되어 아무런 일이 일어나지 않는다. 그래서 make clean 을 했을 때 이 clean은 가짜 타겟이라는 것을 명시하기 위해 .PHONY에 놓는다. .PHONY에 있는 타겟들은 clean 이라는 이름을 가진 파일이 있어도 무조건 명령을 수행한다.

특정 타겟 이름의 의미는?

  • all : 최상단에 위치, 완전한 빌드(컴파일)을 의미함.
  • clean : 최종 실행 파일을 제외한 나머지 생성된 파일(.o 등) 삭제
  • fclean : 최종 파일 포함 생성한 삭제.
  • re : fclean, all 순차적으로 실행.

세부 사용법

특정 타겟을 실행하고 싶다면?

make 타겟이름

실행하는 모든 명령을 출력하는 방법

그냥 그대로 쓰면 된다. 명령어 제일 앞에 @를 넣어주면 (예: @$(CC) $(CFLAGS) -o $@ $^ $(LIB_DIR) $(LIBS) 해당 명령어는 화면에 보이지 않는다.

소스 파일의 디렉토리 설정법

소스 파일들을 설정할 때 일일히 폴더를 지정해주자. gcc 옵션으로는 존재하지 않는다.

헤더 파일의 디렉토리 설정법

위 예제에서 INC_DIR 참조. gcc 에서는 -I 옵션을 통해 #include 할 경로를 추가해줄 수 있다.

lib 파일의 출력 디렉토리 설정법

gcc 빌드할 때 -o 에 디렉토리 설정해주면 되지 않을까?

메모리 할당과 해제

malloc

  • 헤더: <stdlib.h>
  • 함수 정의: void * malloc(size_t size);
  • 리턴: 성공시 어떤 포인터, 실패시 0
  • 예제: char *p = (char *)malloc(sizeof(char) * 10);

free

  • 함수 정의: void free(void *ptr);
  • free는 해당하는 포인터를 해제합니다. 아무것도 리턴하지 않습니다.

올바르지 않은 ptr이 들어가는 상황에 대한 표준 규약은 없습니다. 메모리 할당을 어떻게 구현하는지는 플랫폼에 따라 달라지지만, 일반적으로 “시작 주소”와 “길이” 정보를 가지고 있는 내부 테이블이 존재합니다. 해제하고자 하는 포인터와 관련이 없이 아예 무관한 다른 데이터를 해제해버릴 가능성을 원천 차단하기 위해, free는 대부분의 경우 정확한 주소가 들어왔을 때에만 처리하고 올바르지 않은 주소가 들어왔을 때엔 에러로 간주하고 즉시 보고하는 식으로 설계되어 있습니다.

파일 읽고 쓰기

open

  • 헤더: <fcntl.h>
  • 함수 정의: int open(const char *path, int oflag, ...);
  • 동작: 새 파일을 열면서 새 디스크립터를 할당
  • 리턴값: 성공하면 양의 정수 (파일 디스크럽터 넘버)가 리턴되고, 실패하면 글로벌 변수인 errno를 설정하고 -1를 리턴한다.
  • 매뉴얼 : man 2 open

oflag

아래 값들을 or 연산을 하여 넣으면 됨.

       O_RDONLY        읽기 전용으로 열기
       O_WRONLY        쓰기 전용으로 열기
       O_RDWR          읽기/쓰기로 열기
       O_NONBLOCK      do not block on open or for data to become available
       O_APPEND        append on each write
       O_CREAT         파일이 있지 않으면 생성
       O_TRUNC         truncate size to 0
       O_EXCL          error if O_CREAT and the file exists
       O_SHLOCK        atomically obtain a shared lock
       O_EXLOCK        atomically obtain an exclusive lock
       O_NOFOLLOW      do not follow symlinks
       O_SYMLINK       allow open of symlinks

O_EXCL : 심볼릭 링크도 무조건 실패한다.

mode_t

헤더: <sys/stat.h>

플래그를 O_CREAT 로 하면 세 번째 인수로 파일 권한 설정인 mode_t 를 설정해야 함.

close

  • 함수 정의: int close(int fildes)
  • 간략 설명: 파일 디스크립터를 넣어서 해당 연결을 해제시킨다.
  • Return Value: 성공적으로 마쳤으면 0을 리턴한다. 그 이외에는 -1를 리턴하며, 글로벌 변수인 errno가 설정된다.
  • 매뉴얼 : man 2 close

에러

 The close() system call will fail if:

 [EBADF]        유효하지 않거나 활성화되지 않은 파일 디스크립터임.
 [EINTR]        시그널(강제 취소 등)에 의해 중단됨.
 [EIO]          A previously-uncommitted write(2) 가 입출력 에러를 일으킴.

ssize_t read(int fildes, void *buf, size_t nbyte);
ssize_t write(int fildes, const void *buf, size_t nbyte);

read

  • 헤더: <unistd.h>
  • 선언: ssize_t read(int fildes, void *buf, size_t nbyte);
  • 동작: 파일 디스크럽터에 해당하는 파일을 nbyte만큼 읽고 buf에 복사함. 내부의 포인터가 읽은 만큼 자동으로 이동됨 (read(fd, buffer, 10)를 반복한다면, 10칸씩 순차적으로 read 됨.)
  • 리턴: 성공적으로 읽을 시 읽은 바이트 수를 리턴함. 파일의 끝에 도달했다면 0을 리턴함. 에러일시 -1를 리턴하고 글로벌 변수인 errno를 설정함.

write

  • 헤더: <unistd.h>
  • 선언: ssize_t write(int fildes, const void *buf, size_t nbyte);
  • 동작: 해당 파일 디스크립터에 해당하는 IO에 bufnbyte만큼 써넣음.
  • 리턴: 성공적으로 wirte 한 바이트 수를 리턴함. 에러일시 -1를 리턴하고 글로벌 변수인 errno를 설정함.

strerror

  • 헤더 : <string.h>
  • 선언 : char* strerror(int errnum);
  • 동작 및 리턴: errnum를 넣으면 이를 설명해주는 문자열 리터럴을 리턴해준다.

ssize_t란?

사이트를 나타내는 변수에 대한 타입이긴 한데, 사실 사이즈는 양수만 있어도 되지만, (그래서 size_t라는 양수 전용 타입이 있지만) 입출력을 할 때 에러를 처리하기 위해 음수까지 열어놓은 사이즈라고 보면 됨. ssize_t를 리턴하는 함수들은 에러일 때 -1를 리턴하는 경향이 있음.

ssize_t를 이용하는 함수들이 있는 헤더파일을 불러올 때, 알아서 불러오므로 무엇을 include 해야 하는지에 대한 건 신경쓸 필요 없음.

errno 변수 이용법

errno 변수를 쓰기 위해서는 <errno.h>헤더를 선언해야 한다. extern int errno; 이렇게 선언되어 있다. 이 변수는 다른 함수를 호출할 때 값이 바뀌기 때문에 따로 저장해두는 게 좋을 수도 있다.

man errno 하여 에러의 종류를 확인할 수 있음.

man close, open, read, write, strerror, basename

c10. Makefile 끝판왕. close, open, read, write, malloc, free, strerror, basename 에 대한 확실한 숙지 필요. ulimit 사용법(크기제한 확인하는 명령어), hexdump 명령어 (-C 옵션), tail 명령어 ( -c 옵션)

c11. 함수포인터

c12. 연결리스트.

c13. binary tree

기타

매뉴얼 숫자의 의미

MANUAL SECTIONS
The standard sections of the manual include:

1      User Commands
2      System Calls
3      C Library Functions
4      Devices and Special Files
5      File Formats and Conventions
6      Games et. al.
7      Miscellanea
8      System Administration tools and Daemons

exit(1)

정상적으로 종료한다는 뜻.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

Scroll to top