728x90
반응형

클라이언트와 서버

클라이언트: 웹 사용자의 인터넷이 연결된 장치들과 이런 장치들에서 사용 가능한 크롬 같은 웹 브라우저

서버:  웹페이지, 사이트, 앱 등을 저장하는 컴퓨터

클라이언트와 서버

웹 -> 도로
클라이언트 -> 우리의 집
서버 -> 상점
라고 생각하고 생각하고 아래 글을 이해하면 쉽습니다.

웹에서 실제로 무슨 일이 일어날까?

우리가 브라우저에 웹 주소를 입력하는 것은 우리가 상점에 걸어가는 것과 비슷합니다.

1. 브라우저는 DNS 서버로 가서 웹사이트 서버의 실제 ip 주소를 찾습니다.

EX) 우리가 네이버 지도로 상점의 주소를 찾는다.

2. 브라우저가 서버에게 웹사이트의 사본을 보내달라는 HTTP 요청 메시지를 서버로 전송합니다. 
메시지, 클라이언트와 서버 사이에 전송되는 모든 데이터는 TCP/IP 연결을 통해서 전송됩니다.

EX) 상점에 가서 상품을 주문한다.

3. 이 메시지를 받은 서버는 클라이언트의 요청을 승인하고 "200 OK" 메시지를 클라이언트에게 전송합니다.
"200 OK"는 넌 웹사이트를 볼 자격이 있어! 라는 의미입니다. 그 후 서버는 웹사이트의 파일들을 데이터 패킷이라 불리는
작은 덩어리들로 브라우저에 전송을 합니다.

EX) 상점에서 우리가 주문한 상품을 전달받고, 우리는 그 상품을 집으로 가져간다.

4. 브라우저는 이 데이터 패킷들을 완전환 웹 사이트로 조립하고 우리에게 보여줍니다.

EX) 상품이 우리 집 문 앞에 도착한다.

 

인터넷 연결: 웹에서 데이터를 보내고 받게 해준다.

EX) 집과 상점 사이의 거리

TCP/IP: Transmission Control Protocol(전송 제어 규약) / Internet Protocol(인터넷 규약)
-> 데이터가 어떻게 웹을 돌아다닐지 정의하는 통신 규약

EX) 주문을 하고, 상점에 가고, 우리가 상품을 살 수 있게 해주는 운송 장치(우리의 다리 또는 자동차 등등)와 같습니다.

DNS: Domain Name System Servers(도메인 이름 시스템 서버) -> 웹사이트를 위한 주소록

우리가 63.245.217.105 같은 ip 주소를 직접 기억하는 것은 쉽지가 않기에 DNS가 발명되었습니다. DNS는 우리가
브라우저에 입력하는 웹주소 예를 들어 naver.com 같은 웹사이트를 실제 ip 주소에 맞춰주는 특별한 서버입니다.

HTTP: Hypertext Transfer Protocol(하이퍼텍스트 전송 규약)은 클라이언트와 서버가 서로 통신할 수 있게 하기 위한 언어를 정의하는 규약? 약속 같은 친구입니다. 

EX) 우리가 상품을 주문하기 위해 사용하는 언어와 같습니다.

packet: 패킷

기본적으로 웹에서 데이터가 전송될 때, 아주 작은 덩어리들로 전송됩니다. 그렇기에 다양한 웹 사용자들이 동시에 같은
웹 사이트를 다운로드할 수 있습니다. 만약 데이터가 하나의 큰 덩어리로 전송된다면, 한 번에 한 명의 사용자만 다운로드가
가능해져서 매우 비효율적이게 됩니다.

 

Reference

https://developer.mozilla.org/ko/docs/Learn/Getting_started_with_the_web/How_the_Web_works#패킷_설명

 

웹의 동작 방식 - Web 개발 학습하기 | MDN

*'웹의 동작 방식'*은 여러분의 컴퓨터나 폰의 웹 브라우저 안에서 웹페이지를 볼 때 무슨 일이 발생하는지에 대한 간소화된 개념을 제공할 것입니다.

developer.mozilla.org

 

728x90
반응형
728x90
반응형

허용 함수를 다 안다는 전제로 글을 작성했습니다. 모르면 아래 글 보기.

https://jinho-study.tistory.com/1138

 

42 서울 pipex 정리 2 (access, dup2, execve, perror, strerror)

이어서 나머지 함수들을 알아보자. https://jinho-study.tistory.com/1137 pipex 정리 1 ( 프로세스, fork, pipe, wait, waitpid) 개요 pipex는 우리가 만들 pipex 프로그램을 위와 같은 방식으로 돌렸을 때, 아래 명령어

jinho-study.tistory.com

 

전체적으로 봤을 때 로직은 아래와 같이 생각보다 간단하다. 실제로 파일 3개면 구현 가능하다.

int	main(int ac, char **av, char **envp)
{
	int		i;

	if (ac != 5)
		에러 출력 후 종료
	i = -1;
	while (++i < 2)
	{
		if (i == 0)
       		명령어 파싱 -> 첫 번째 프로세스 실행(infile을 stdin으로 받고 명령어 실행)
		else if (i == 1)
			명령어 파싱 -> 두 번째 프로세스 실행(첫 번째 프로세스 실행 결과를 stdin으로 
            받고 명령어 실행 결과를 outfile에 저장)
	}
	포크된 프로세스 만큼 wait를 해준다. 멘덴토리의 경우에는 2번
	i = -1;
	while (++i < 2)
		wait(NULL);
}

다만 여기서 알고 넘어가야 될게 2가지가 있다. minishell 내용 같긴 한데 pipex 하면서 알고 간다면

minishell 할 때 아주 큰 도움이 된다.

1 ) 쉘에서 명령어들은 병렬로 돌아간다. 

예를 들어 sleep 3 | sleep 3은 6초를 기다릴 것 같지만, 실제로는 병렬로 돌기에 3초를 기다린다.

병렬이더라도 cat | cat 같은 경우에는 애초에 앞에서 입력이 들어와야 넘어가기 때문에 프로그램이 끝나지 않는다. 

2) 파이프 개수만큼 fork 되는 것이 아니라 파이프 개수 + 1 만큼 fork 된다.

실행되는 명령어가 2개(ls | cat)라서 pipe가 한 개라고 fork를 한번 하지 않는다.

실제로 bash에서 그냥 exit은 당연히 꺼지지만 exit | exit 은 bash가 꺼지지 않는다. 

bash 꺼짐
bash 안 꺼짐

그냥 exit은 fork가 안되고 바로 실행돼서 bash가 꺼지지만, exit | exit의 경우에는 fork가 2번 실행되어서

프로세스가 3개라 bash가 꺼지지 않는다. 이러한 구조를 가지는 것은 자식 프로세스에서 명령어를 수행하고

부모(main) 프로세스에서 모든 자식 프로세스를 기다리게 하기 위함이 아닐까?라고 작성자는 이해했다. 

 

각 프로세스 별로 구현해야 되는 것들을 순서대로 쭉 나열해 보면

1) 첫 번째 프로세스 

1.1) infile 읽고 pipe 세팅

자식 프로레스)

infile을 stdin으로 dup2, fd[1]을 stdout으로 dup2 해준다.

여기서 fd[0]만 사용 안 하니까 fd[0]만 close 하는 경우가 많은데, 둘 다 close 해줘야 된다.

fd[0]만 close 하면 yes | head -1 같은 테스트의 경우 프로그램이 끝나지 않는다.

부모 프로레스)

fd[0]을 stdin으로 dup2 해준다. 여기서 fd[0]은 자식 프로세스에서의 명령어 실행 결과가 된다.

결국 첫 번째 명령어의 실행 결과가 stdin으로 들어와 있는 상태라고 생각하면 된다.

여기서도 마찬가지로 fd[0], fd[1] 둘 다 close 해준다.

static void	dup_child_1(char **av, int *fd)
{
	int	infile;

	infile = open(av[1], O_RDONLY);
	if (infile == -1)
		perror_exit("infile error");
	if (dup2(infile, STDIN_FILENO) == -1)
		perror_exit("dup2 error");
	if (dup2(fd[1], STDOUT_FILENO) == -1)
		perror_exit("dup2 error");
	close(fd[0]);
	close(fd[1]);
}

static void	child_process_1(char **av, char **envp)
{
	int		fd[2];
	pid_t	pid;

	if (pipe(fd) == -1)
		perror_exit("pipe error");
	pid = fork();
	if (pid == -1)
		perror_exit("fork error");
	if (pid == 0)
	{
		dup_child_1(av, fd);
		execute(av[2], envp);
	}
	if (dup2(fd[0], STDIN_FILENO) == -1)
		perror_exit("dup2 error");
	close(fd[0]);
	close(fd[1]);
}

1.2) 명령어 파싱 후 실행

env 실행 결과

메인에서 인자로 받는 envp 안에는 이런 식으로 환경변수들이 저장되어 있다.

여기서 우리가 필요한 것은 PATH=/Users/jinhokim/.brew/bin:/Users/jinhokim/.brew/bin:/usr/local/bin:/usr/bin:/

bin:/usr/sbin:/sbin:/usr/local/munki이다. PATH= 뒤의 문자열을 : 기준으로 split 하게 되면 

["/bin", "/usr/bin", ...] 같은 결과가 되는데 총 6개의 경로가 나오게 된다. 이 경로들 안에 명령어 파일들이 들어있다.

예를 들어 ls는 /bin/ 안에 들어있는데 그렇기에 쉘에서 /bin/ls를 실행하면 ls가 실행된다.

/bin/ls

이제 저 6개의 경로와 첫 번째 명령어를 ft_strjoin한 결과를 access 함수를 통해 실행 가능한 파일인지 확인하고

없는 명령어의 경우에는 에러 출력, 있는 명령어의 경우에는 그냥 execve를 실행해주면 된다.

// path -> ["/bin", "/usr/bin", ...]
while (path[i])
	{
	// joined_cmd = "/ls"
	// ret_cmd = "/bin/ls"
        ret_cmd = ft_strjoin(path[i++], joined_cmd);
		if (access(ret_cmd, X_OK) != -1)
		{
			free(joined_cmd);
			return (ret_cmd);
		}
		free(ret_cmd);
	}
	free(joined_cmd);
	return (NULL);
}

 

2) 두 번째 프로세스 

2.1) outfile에 실행 결과 저장

자식 프로레스)

outfile을 생성하고 stdout으로 dup2 해준다. 그러고 위에 설명했던 명령어 파싱 후 실행을 반복하면 된다.

stdout이 outfile에 연결되어 있기 때문에 이 프로세스에서 표준 출력되는 내용들은 모두 outfile 안에 저장된다.

부모 프로레스)

아무것도 할 필요가 없다.

static void	child_process_2(char **av, char **envp)
{
	pid_t	pid;
	int		outfile;

	pid = fork();
	if (pid == -1)
		perror_exit("fork error");
	if (pid == 0)
	{
		outfile = open(av[4], O_RDWR | O_CREAT | O_TRUNC, 0644);
		if (outfile == -1)
			perror_exit("outfile error");
		if (dup2(outfile, STDOUT_FILENO) == -1)
			perror_exit("dup2 error");
		execute(av[3], envp);
	}
}

 

 

728x90
반응형
728x90
반응형

이어서 나머지 함수들을 알아보자. 

https://jinho-study.tistory.com/1137

 

pipex 정리 1 ( 프로세스, fork, pipe, wait, waitpid)

개요 pipex는 우리가 만들 pipex 프로그램을 위와 같은 방식으로 돌렸을 때, 아래 명령어와 똑같이 동작하도록 구현해야 되는 과제이다. 즉 infile 파일을 읽고 명령어 2개를 실행한 결과를 outfile에

jinho-study.tistory.com

 

함수 정리

1. access

#include <unistd.h>
int access(const char *pathname, int mode);

access 함수는 파일의 권한을 확인하고 성공하면 0 실패하면 -1을 반환한다.

mode에 따라 확인하는 내용이 달라지는데 mode는 아래 4가지가 있다.

R_OK(파일 존재, 읽기 권한), W_OK(파일 존재, 쓰기 권한), X_OK(파일 존재, 실행 권한), R_OK(파일 존재)

우리는 이 함수를 써서 우리가 입력한 명령어가 실제로 있는 명령어인지 확인할 수 있다.

예시 1) 파일이 있으면 Success, 없으면 Fail이 출력된다.

#include <stdio.h>
#include <unistd.h>

int main(void)
{
	char *pathname = "./test.txt";
	if ( access(pathname, R_OK | W_OK) == 0)
		printf("읽고 쓰기 가능\n");
	else
		printf("권한이 없거나 파일이 없음");
}

 

2. dup2

#include <unistd.h>
int dup2(int fd1, int fd2)

dup2 함수는 디스크립터를 변경해주는 함수이다. fd2가 fd1을 가리키게 하고 기존 fd2는 close 된다.

아래 함수는 pipex 과제 중 만든 함수인데, 정상적으로 동작할 시 std_in을 표준 입력으로 std_out을 표준 출력으로 바꿔준다.

static void	pipe_control(int close_fd, int std_in, int std_out, t_info *info)
{
	close(close_fd);
	if (dup2(std_in, STDIN_FILENO) == -1)
		invalid_exit_opt(info, "STDIN dup2 error", 1);
	if (dup2(std_out, STDOUT_FILENO) == -1)
		invalid_exit_opt(info, "STDOUT dup2 error", 1);
	close(std_in);
	close(std_out);
}

pipex 하면서 보기는 쉽지 않은 경우이긴 하지만 stdin에 dup2를 쓰고 close를 한 경우 stdin이 죽어서 계속 입력으로 

EOF를 받게 된다. minishell 정리할 때도 작성하겠지만 아래와 같은 식으로 stdin, stdout을 dup 함수를 통해 기억해두고

마지막에 dup2를 사용해 stdin, stdout을 되돌려 줄 수 있다. 작성자는 이 에러 때문에 minsihell 할 때 10시간을 버렸다.

int	main(int ac, char **av, char **envp)
{
	int		stdin_dup;
	int		stdout_dup;

	stdin_dup = dup(0);
	stdout_dup = dup(1);
	//~~~
	//process
	//~~~
	dup2(stdin_dup, 0);
	dup2(stdout_dup, 1);
	close(stdin_dup);
	close(stdout_dup);
}

 

3. execve

#include <unistd.h>
int execve(const char *filename, char *const argv[], char *const envp[])

execve 함수는 exec 계열 함수 중 하나로 프로세스를 하나 생성해서 명령어를 실행시키고 자신을 종료시킨다.

성공 시에는 종료가 되기 때문에 실패 시에만 -1을 반환한다.

예시 1) Running ls with execve 출력 후 ls 명령어가 실행된다.

#include <stdio.h>
#include <unistd.h>

int	main(int argc, char **argv, char **envp)
{
	char *arg[2] = {"ls"};
	printf("Running ls with execve\n");
	execve("/bin/ls", arg, envp);
	printf("execve failed to run ls\n");
}

 

아래는 에러를 출력해주는 함수들인데 우리 프로그램에서 에러를 출력할 때 쉘과 거의 똑같이 출력하고 싶다면 사용하면 된다.

마음대로 출력할 거라면 그냥 fd_putstr 같은 함수를 사용하면 된다.

4. perror

#include <stdio.h>
void perror(const char* str);

perror 함수는 전역 변수 errno에 해당하는 에러 메시지를 출력해준다. str이 NULL이 아닐 시 str도 출력해준다.

예시 1) myfile이 없을 시 Could not open data file: No such file or directory가 출력된다.

#include <stdio.h>
#include <stdlib.h>
 
int main(void)
{
   FILE *fh;
 
   if ((fh = fopen("myfile", "r")) == NULL)
      perror("Could not open data file");
}

 

5. strerror

#include <string.h>
char* strerror(int errnum);

strerror 함수는 errnum의 값을 통해 발생했던 에러에 맞는 에러 메시지를 반환해준다.

예시 1) myfile이 없을 시 No such file or directory, 2가 출력된다.

#include <errno.h>
#include <stdio.h>
#include <string.h>

int main()
{
	FILE* fh;

	if ((fh = fopen("myfile", "r")) == NULL)
		printf("%s, %d\n", strerror(errno), errno);
	return 0;
}
728x90
반응형

'42 SEOUL > pipex' 카테고리의 다른 글

42 서울 pipex Mandatory  (0) 2022.12.19
42 서울 pipex 정리 1 ( 프로세스, fork, pipe, wait, waitpid)  (0) 2022.09.06
728x90
반응형

개요

pipex는 우리가 만들 pipex 프로그램을 위와 같은 방식으로 돌렸을 때, 아래 명령어와 똑같이 동작하도록 구현해야 되는 과제이다.

즉 infile 파일을 읽고 명령어 2개를 실행한 결과를 outfile에 저장해야 된다. 여기서 이 작대기 |가 파이프다.

우리는 이 과제를 통해 프로세스와 프로세스 간 통신을 할 때 사용하는 pipe에 대해 공부해야 한다.

 

프로세스(process)란?

프로그램은 컴퓨터에서 실행될 때 특정 작업을 수행하는 일련의 명령어들의 집합이다. 이 프로그램의 명령어와

정적 데이터가 메모리에 적재되면 프로세스가 된다. 간단히 말하면 프로세스는 실행 중인 프로그램을 의미한다.

우리가 작업 관리자를 켜면 잔뜩 나오는 것들이 프로세스다.

 

함수 정리

1. fork

#include <unistd.h>
pid_t fork(void);

fork 함수는 프로세스를 복사해준다. 기존의 프로세스를 부모 프로세스, 복사된 프로세스를 자식 프로세스라고 부른다.

fork 함수는 프로세스의 고유 id인 pid를 반환한다. fork 함수 실행 실패 시 -1을 반환하고 자식 프로세스를 생성하지 않는다.

성공 시에는 부모 프로세스에는 자식 프로세스 피드 값을, 자식 프로세스에는 0이 반환한다.

예시 1)

코드만 보면 if, else문이 다 돌아가는 게 말이 안 되는 것 같지만 부모, 자식 프로세스에서 각각 main문이 돌아가기 때문에 2번 출력된다.

int	main(void)
{
	pid_t pid = fork();

	if (pid == 0)
	{
		printf("Hello from Child\n");
		printf("%d %d\n", pid, getpid());
	}
	else
	{
		printf("Hello from Parent\n");
		printf("%d %d\n", pid, getpid());
	}
}

/*
실행 결과
Hello from Parent
48280 48279
Hello from Child
0 48280
*/

예시 2) 자식과 부모 프로세스는 데이터와 상태가 다르기에 변수의 변화가 영향을 끼치지 않는다.

int	main(void)
{
	int x = 1;
	if (fork() == 0)
    	printf("Child has x = %d\n", ++x);
	else
		printf("Parent has x = %d\n", --x);
}

/*
실행 결과
Parent has x = 0
Child has x = 2
*/

 

2. pipe

#include <unistd.h>
int pipe(int fd[2]);

pipe 함수는 파이프를 생성하고 2개의 디스크립터(읽기 전용, 쓰기 전용)를 생성해준다. 실패했을 경우에는 -1을 반환한다.

생성한 2개의 디스크립터와 파이프를 사용해 아래와 같은 방식으로 부모 프로세스를 자식 프로세스를 통신시킬 수 있다.

 

open 함수를 사용할 때와 마찬가지로 디스크립터를 사용하기에 pipe 함수 역시 사용하지 않는 디스크립터는 close 함수로 닫아줘야 한다.

부모에서 자식으로 일반통행 방식이기 때문에 부모 프로세스는 읽는 쪽을,  자식 프로세스는 쓰는 쪽을 닫아주면 된다.

예시 1) pipe 생성

int	main(void)
{
	int fd[2];
	
	pipe(fd);
	printf("fd[0]: %d fd[1]: %d\n", fd[0], fd[1]);
}

/*
실행 결과
fd[0]: 3 fd[1]: 4
*/

예시 2) pipe를 사용한 부모와 자식 프로세스 간 통신

#define MAX_BUF 1024

int main()
{
	int fd[2];
	pid_t pid;
	char buf[MAX_BUF];

	if (pipe(fd) < 0)
		printf("pipe error\n");
	pid = fork();
	if (pid < 0)
	{
		printf("fork error\n");
		exit(1);
	}
	if (pid == 0)
	{
		close(fd[1]);
		read(fd[0], buf, MAX_BUF);
		printf("Child got message : %s\n", buf);
	}
	else
	{
		close(fd[0]);
		ft_strcpy(buf, "Massage from Parent");
		write(fd[1], buf, ft_strlen(buf));
	}
}

/*
실행 결과
Child got message : Massage from Parent
*/

 

3. wait

#include <sys/wait.h>
pid_t  wait(int *statloc);

wait 함수는 자식 프로세스가 종료되었을 때 자식 프로세스의 pid값을 반환하고, 오류가 났을 때는 -1을 반환합니다.

wait 함수를 사용하면 부모 프로세스를 자식 프로세스가 종료될 때까지 기다리게 할 수 있습니다. 

만약 자식 프로세스가 종료되었는데 부모 프로세스가 계속 돌아가고 있다면 자식 프로세스는 좀비 프로세스 상태가 되는데 

wait 함수를 사용해 이를 방지할 수도 있다.

예시 1) 부모 프로세스가 자식 프로세스가 끝난 후 다시 돌아가는 것을 확인할 수 있다.

int main()
{
	int stat, stat_res;

    if ((fork()) == 0)
        printf("Child Hi\n");
    else
    {
		stat_res = wait(&stat);
		printf("Parent Hi\n");
		printf("wait 인자값: %d\nwait 반환값: %d\n", stat, stat_res);
        wait(NULL);
        printf("Child Bye\n");
    }
    printf("Process Bye\n");
    return 0;
}

/*
실행 결과
Child Hi
Process Bye
Parent Hi
wait 인자값: 0
wait 반환값: 57724
Child Bye
Process Bye
*/

 

아래 WIF 매크로를 사용해서 stat의 정보를 확인할 수 있다.

WIFEXITED(status): 자식 프로세스가 정상적으로 종료했으면 참을 반환한다.

WEXITSTATUS(status): 자식 프로세스의 종료 상태를 반환한다. 이 매크로는 WIFEXITED가 참을 반환했을 때만 써야 한다.

WIFSIGNALED(status): 자식 프로세스가 시그널로 종료되었으면 참을 반환한다.

WIFSTOPPED(status): 자식 프로세스가 정지된 상태라면 참을 반환한다

예시 2) 자식 프로세스가 정상적으로 종료됐으므로 WIFEXITED의 조건문이 실행된다.

int main()
{
	int pid, status;

	printf("Parent: %d\n", getpid());
	pid = fork();
	if (pid == 0)
	{
		printf("Child %d\n", getpid());
		sleep(2);
		return (0);
	}
	waitpid(pid, &status, 0);
	if (WIFSIGNALED(status))
		printf("Error\n");
	else if (WEXITSTATUS(status))
		printf("Exited Normally\n");
	else if (WIFEXITED(status))
		printf("Parent: %d\n", getpid());
	return 0;
}

 

4. waitpid

#include <sys/wait.h>
pid_t  waitpid(pid_t pid, int *statloc, int options);

waitpid 함수는 wait 함수와 거의 똑같은데, 옵션을 추가해 동작 방식을 변경할 수 있다.

아래는 옵션들(3번째 인자 종류)이다. 인자가 0일 경우에는 wait 함수와 동일하게 작동한다.

WNOHANG: 기다리는 PID가 종료되지 않아서 즉시 종료 상태를 회수할 수 없는 상황일 때 0을 반환

WUNTRACED: 중단된 자식 프로세스의 상태를 반환

WCONTINUED: 중단되었다가 재개된 자식 프로세스의 상태를 반환

에시 1) WNOHANG 사용

#include <time.h>

int main()
{
	pid_t pid;
	int status;
	time_t t;

	pid = fork();
	if (pid < 0)
		perror("fork error");
	else if (pid == 0)
	{
		sleep(3);
		exit(1);
	}
	else do
	{
		pid = waitpid(pid, &status, WNOHANG);
		if (pid == -1)
			perror("wait error");
    	else if (pid == 0)
		{
			time(&t);
			printf("child is still running at %s", ctime(&t));
			sleep(1);
		}
    	else
		{
			if (WIFEXITED(status))
				printf("child exited with status of %d\n", WEXITSTATUS(status));
			else
				puts("child did not exit successfully");
    	}
	} while (pid == 0);
}

/*
child is still running at Wed Sep  7 12:27:32 2022
child is still running at Wed Sep  7 12:27:33 2022
child is still running at Wed Sep  7 12:27:34 2022
child exited with status of 1
*/

사실 이 과제에서 WNOHANG을 사용했다는 것은 좋은 징조가 아니다. dup2 후 파이프를 전부 다 닫아주면

WNOHANG을 사용하지 않아도 잘 넘어가진다. 파이프를 잘 안 닫았을 경우 yes | head -1이나 

/dev/urandom | head -1 같은 경우에 head 한 결과가 출력도 안되고 프로그램이 끝나지도 않는다. 

728x90
반응형

'42 SEOUL > pipex' 카테고리의 다른 글

42 서울 pipex Mandatory  (0) 2022.12.19
42 서울 pipex 정리 2 (access, dup2, execve, perror, strerror)  (2) 2022.09.09
728x90
반응형

소집해제 후 1년 9개월 만에 42로 돌아왔다. 과제들이 상당히 많이 바뀐 것 같다. 

netwhat, ft_server, ft_service 등등이 사라지고 ft_printf가 쉬워졌고, 다른 과제들이 많이 추가됐다.

예전에는 과제들 하나하나가 배경 지식 하나도 없이 처음부터 너무 깊게 들어가야 되는 느낌이었는데

지금은 서클 순서대로 개념을 확장시키면서 공부할 수 있도록 과제들이 배치가 된 것 같다. 과제 별로 어떤 것을 공부해야 

하는지도 예전보다 뚜렷하게 보이는 것 같아서 참 좋다. Ex) pipex -> 프로세스, push_swap -> 자료구조 알고리즘

다만 norminette가 바뀌어서 기존 코드 다 수정하는 건 조금 귀찮긴 했다. libftㅜㅜ

일단 지금 하고 있는 pipex 과제부터 정리하려고 한다. get_nextline, ft_printf는 exam02 볼 때 정리할 것 같다.

728x90
반응형

'42 SEOUL > 잡담' 카테고리의 다른 글

42 서울 본과정 시작  (0) 2020.10.06
728x90
반응형

구조체(연결 리스트) 관련 함수들이다. 

Libft 과제의 코드는 이게 마지막이다!! 

ft_lstlast: 연결 리스트의 마지막 list 주소를 반환해주는 함수

t_list	*ft_lstlast(t_list *lst)
{
	if (!lst)
		return (lst);
	while (lst->next)
		lst = lst->next;
	return (lst);
}

 

ft_lstnew: 새 연결 리스트를 생성해주는 함수(새로운 노드를 선언)

t_list	*ft_lstnew(void *content)
{
	t_list	*lst;

	if (!(lst = (t_list *)malloc(sizeof(t_list))))
		return (NULL);
	lst->content = content;
	lst->next = NULL;
	return (lst);
}

 

ft_lstmap: 연결 리스트의 모든 content에 함수 f를 적용한 결과를 반환해주는 함수

위에서 구현한 ft_lstnew 함수를 이용해서 새로운 연결 리스트를 생성한 후에, 매개 변수로 받은 연결 리스트의 content에 함수 f를 적용한 결과를 차곡차곡 저장해주면 된다.

t_list	*ft_lstmap(t_list *lst, void *(*f)(void *), void (*del)(void *))
{
	t_list	*result;
	t_list	*t;

	if (lst == 0 || f == 0)
		return (NULL);
	if (!(result = ft_lstnew((*f)(lst->content))))
		return (NULL);
	t = result;
	lst = lst->next;
	while (lst)
	{
		if (!(t->next = ft_lstnew((*f)(lst->content))))
		{
			ft_lstclear(&result, del);
			return (NULL);
		}
		t = t->next;
		lst = lst->next;
	}
	return (result);
}

 

ft_lstsize: 연결 리스트의 길이를 반환해주는 함수

int		ft_lstsize(t_list *lst)
{
	int		len;

	len = 0;
	while (lst)
	{
		lst = lst->next;
		len++;
	}
	return (len);
}
728x90
반응형
728x90
반응형

이번 함수들은 구조체(연결 리스트)와 관련된 함수들이다. 

연결 리스트의 형식은 아래와 같다. 

typedef	struct		s_list
{
	void			*content;
	struct s_list	*next;
}					t_list;

 

ft_lstadd_back: 연결 리스트의 맨 뒤에 새로운 요소를 추가해주는 함수

void	ft_lstadd_back(t_list **lst, t_list *new)
{
	t_list	*lst2;

	lst2 = *lst;
	if (!(*lst))
		*lst = new;
	else
	{
		while (lst2->next)
			lst2 = lst2->next;
		lst2->next = new;
	}
}

 

ft_lstadd_front: 연결 리스트의 맨 앞에 새로운 요소를 추가해주는 함수

void	ft_lstadd_front(t_list **lst, t_list *new)
{
	new->next = *lst;
	*lst = new;
}

 

ft_lstclear: 연결 리스트를 초기화해주는 함수(삭제, 메모리 해제)

void	ft_lstclear(t_list **lst, void (*del)(void *))
{
	t_list	*lst2;

	lst2 = *lst;
	if (lst || *lst)
	{
		while (*lst)
		{
			*lst = lst2->next;
			del(lst2->content);
			free(lst2);
			lst2 = *lst;
		}
	}
}

 

ft_lstdelone: 연결 리스트의 content를 삭제하고 메모리를 해제해주는 함수

void	ft_lstdelone(t_list *lst, void (*del)(void *))
{
	if (lst)
	{
		del(lst->content);
		free(lst);
	}
}

 

ft_lstiter: 연결 리스트의 모든 content에 함수 f를 적용해주는 함수

void	ft_lstiter(t_list *lst, void (*f)(void *))
{
	while (lst)
	{
		f(lst->content);
		lst = lst->next;
	}
}
728x90
반응형
728x90
반응형

ft_strrchr: 문자열 s의 뒤에서부터 문자 c를 찾아 그 위치를 반환해주는 함수

char	*ft_strrchr(const char *s, int c)
{
	int		i;

	i = (int)ft_strlen(s) + 1;
	while (i--)
	{
		if (s[i] == (char)c)
			return ((char *)s + i);
	}
	return (NULL);
}

 

ft_strtrim: 문자열 s1에서 앞, 뒤에 문자열 set을 자른 문자열을 반환해주는 함수

start = 0 -> s1의 맨 앞에서부터 set 값이 없을 때까지 start 증가

end = ft_strlen(s1) -> s1의 맨 뒤에서부터 set 값이 없을 때까지 end 감소

새 문자열 str에 end - start + 1 만큼의 메모리를 할당해주고 s1의 start부터 end까지의 값들을 복사해주면 된다.

마지막에 null을 넣어주는 것을 잊지 말자!

int		ft_setcheck(char c, char const *set)
{
	int	i;

	i = 0;
	while (set[i])
	{
		if (set[i++] == c)
			return (1);
	}
	return (0);
}

char	*ft_strtrim(char const *s1, char const *set)
{
	char	*str;
	int		start;
	int		end;
	int		i;

	i = 0;
	start = 0;
	if (s1 == 0 || set == 0)
		return (NULL);
	end = (int)ft_strlen(s1);
	while (s1[start] && ft_setcheck(s1[start], set))
		start++;
	while (end > start && ft_setcheck(s1[end - 1], set))
		end--;
	if (!(str = (char *)malloc(sizeof(char) * (end - start + 1))))
		return (NULL);
	while (start < end)
		str[i++] = s1[start++];
	str[i] = '\0';
	return (str);
}

 

ft_substr: 문자열 s의 start번째 인덱스부터 시작하는 크기가 len인 문자열을 반환해주는 함수

start가 문자열 s의 길이보다 크거나 같을 때만 잘 처리해주면 된다. ft_strdup을 사용해서 빈 문자열을 반환해줬다.

char	*ft_substr(char const *s, unsigned int start, size_t len)
{
	char	*str;
	size_t	i;

	i = 0;
	if (s == 0)
		return (NULL);
	if (start >= ft_strlen(s))
		return (ft_strdup(""));
	if (!(str = (char *)malloc(sizeof(char) * (len + 1))))
		return (NULL);
	while (s[i] && i < len)
		str[i++] = s[start++];
	str[i] = 0;
	return (str);
}

 

ft_tolower: 매개변수로 들어오는 값이 대문자면 소문자로 바꿔주는 함수

int	ft_tolower(int c)
{
	if (c >= 65 && c <= 90)
		return (c + 32);
	return (c);
}

 

ft_toupper: 매개변수로 들어오는 값이 소문자면 대문자로 바꿔주는 함수

int	ft_toupper(int c)
{
	if (c >= 97 && c <= 122)
		return (c - 32);
	return (c);
}
728x90
반응형
728x90
반응형

ft_strlen: 문자열의 길이를 반환해주는 함수

size_t	ft_strlen(const char *s)
{
	size_t	i;

	i = 0;
	while (s[i])
		i++;
	return (i);
}

 

ft_strmapi: 문자열 s의 각 인덱스에 담긴 값에 함수 f를 적용한 결과를 반환해주는 함수

함수를 파라미터로 받는 함수이다. 

char	*ft_strmapi(char const *s, char (*f)(unsigned int, char))
{
	int		i;
	int		len;
	char	*str;

	i = 0;
	if (s == 0 || f == 0)
		return (NULL);
	len = (int)ft_strlen(s);
	if (!(str = (char *)malloc(sizeof(char) * (len + 1))))
		return (NULL);
	while (i < len)
	{
		str[i] = f(i, s[i]);
		i++;
	}
	str[i] = '\0';
	return (str);
}

 

ft_strncmp: 두 문자열 s1, s2를 n만큼 비교하여 그 차이를 반환해주는 함수

문자열 s1과 문자열 s2을 첫 인덱스부터 순서대로 비교하는데

s1과 s2가 끝까지 같다면 0을 반환하고, 같지 않다며 해당 위치의 s1 - s2 값을 반환해주면 된다.

int		ft_strncmp(const char *s1, const char *s2, size_t n)
{
	size_t	i;

	i = 0;
	while (s1[i] && s2[i] && i < n - 1 && s1[i] == s2[i])
		i++;
	if (n == 0)
		return (0);
	return ((unsigned char)s1[i] - (unsigned char)s2[i]);
}

 

ft_strnstr: 문자열 big에서 문자열 little과 일치하는 문자열의 위치를 반환해주는 함수

문자열 big의 첫 인덱스부터 문자열 little의 첫 문자와 같은 문자가 있는지 확인을 한다.

같은 문자가 있다면 반복문을 하나 더 파서 big의 뒷 문자들이 little과 똑같은지 확인을 해주면 된다.

char	*ft_strnstr(const char *big, const char *little, size_t n)
{
	size_t	i;
	size_t	j;
	size_t	little_len;

	i = 0;
	little_len = ft_strlen(little);
	if (!little_len)
		return ((char *)big);
	while (big[i] && i + little_len <= n)
	{
		if (big[i] == little[0])
		{
			j = 0;
			while (big[i + j] && little[j])
			{
				if (big[i + j] != little[j])
					break ;
				else if (j == little_len - 1)
					return ((char *)big + i);
				j++;
			}
		}
		i++;
	}
	return (NULL);
}
728x90
반응형
728x90
반응형

이번 함수들은 문자열과 관련된 함수들이다.

ft_strchr: 문자열 s에서 문자 c를 찾아 그 위치를 반환해주는 함수

char	*ft_strchr(const char *s, int c)
{
	int	i;

	i = 0;
	while (i < (int)ft_strlen(s) + 1)
	{
		if (s[i] == (char)c)
			return ((char *)s + i);
		i++;
	}
	return (NULL);
}

 

ft_strdup: 문자열 s를 복사한 문자열을 반환해주는 함수

새 문자열에 s의 길이 + 1 만큼 할당해주고 s를 그대로 복사해주면 된다. 마지막에 null을 넣어주는 것을 잊지 말자!

char	*ft_strdup(const char *s)
{
	char	*str;
	int		i;
	int		slen;

	i = 0;
	slen = (int)ft_strlen(s);
	if (!(str = (char *)malloc(sizeof(*s) * (slen + 1))))
		return (NULL);
	while (s[i])
	{
		str[i] = s[i];
		i++;
	}
	str[i] = '\0';
	return (str);
}

 

ft_strjoin: 두 문자열을 s1, s2를 합친 문자열을 반환해주는 함수

새 문자열에 s1의 길이 + s2의 길이 + 1 만큼 할당해주고, s1과 s2를 순서대로 복사해주면 된다. 

마지막에 null을 넣어주는 것을 잊지 말자!

char	*ft_strjoin(char const *s1, char const *s2)
{
	char	*str;
	int		i;
	int		j;
	int		s1_len;
	int		s2_len;

	i = 0;
	j = 0;
	if (s1 == 0 || s2 == 0)
		return (NULL);
	s1_len = (int)ft_strlen(s1);
	s2_len = (int)ft_strlen(s2);
	if (!(str = (char *)malloc(sizeof(char) * (s1_len + s2_len + 1))))
		return (NULL);
	while (i < s1_len)
	{
		str[i] = s1[i];
		i++;
	}
	while (j < s2_len)
		str[i++] = s2[j++];
	str[i] = '\0';
	return (str);
}

 

ft_strlcat: 문자열 dst에 문자열 src를 이어 붙이고, 그 길이를 반환하는 함수

dest의 길이가 size보다 크면 size + src의 길이를 반환하고

dest의 길이가 size보다 크지 않으면 dest의 길이 + src길이를 반환해주면 된다.

size_t	ft_strlcat(char *dst, const char *src, size_t size)
{
	size_t	i;
	int		j;
	size_t	k;

	i = ft_strlen(dst);
	j = 0;
	if (size < i)
		k = size;
	else
		k = i;
	while (src[j] && i < size - 1 && size != 0)
		dst[i++] = src[j++];
	while (src[j] != '\0')
		j++;
	dst[i] = '\0';
	return (k + j);
}

 

ft_strlcpy: size만큼 문자열 src를 문자열 dst에 복사하고, src의 크기를 반환해주는 함수

src를 size만큼만 복사해주면 된다.

strlcat 함수도 그렇고 복사해준 src의 길이가 아니라 src의 크기 그 자체를 반환하는 게 조금 아이러니한 것 같다.

size_t	ft_strlcpy(char *dst, const char *src, size_t size)
{
	size_t	i;
	size_t	src_len;

	i = 0;
	if (dst == 0 || src == 0)
		return (0);
	src_len = ft_strlen(src);
	if (size > 0)
	{
		while (i < (size - 1) && src[i])
		{
			dst[i] = src[i];
			i++;
		}
		dst[i] = '\0';
	}
	return (src_len);
}
728x90
반응형
728x90
반응형

이번 함수들은 다 출력과 관련된 함수들이다. write 함수를 잘 사용해주면 된다.

ft_putchar_fd: c문자를 fd파일 디스크립터 형식으로 출력해주는 함수

write 함수의 파라미터는 순서대로 디스크립터, 문자, 크기이므로 그냥 아래와 같이 짜주면 된다.

 

void	ft_putchar_fd(char c, int fd)
{
	write(fd, &c, 1);
}

 

ft_putstr_fd: 문자열을 fd파일 디스크립터 형식으로 출력해주는 함수

write 함수로 문자열을 모두 출력하려면 그 길이를 알 필요가 있기에 문자열의 길이를 반환해주는 ft_strlen 함수를 사용했다. 

void	ft_putstr_fd(char *s, int fd)
{
	if (s == 0)
		return ;
	write(fd, s, ft_strlen(s));
}

 

ft_putendl_fd: 끝에 '\n'이 추가된 문자열을 fd파일 디스크립터 형식으로 출력해주는 함수

ft_putstr_fd 함수에서 마지막에 '\n'을 출력하는 부분만 추가해주면 된다.

void	ft_putendl_fd(char *s, int fd)
{
	if (s == 0)
		return ;
	write(fd, s, ft_strlen(s));
	write(fd, "\n", 1);
}

 

ft_putnbr_fd: 숫자 n을 fd파일 디스크립터 형식으로 출력해주는 함수

재귀적으로 앞에 숫자부터 하나씩 출력하는 방식이다.

음수일 때는 '-'를 먼저 출력하고 양수로 바꿔서 똑같이 출력해주면 된다.

단 -2147483648의 경우 양수로 바꿨을 때 오버플로가 발생하게 되므로 따로 처리를 해줘야 한다.

'-'와 '2'를 먼저 출력한 후에 147483648을 출력하는 방식으로 처리했다. 

void	ft_putnbr_fd(int n, int fd)
{
	if (n < 0)
	{
		ft_putchar_fd('-', fd);
		n *= -1;
	}
	if (n == -2147483648)
	{
		ft_putchar_fd('2', fd);
		n = -(n % 1000000000);
	}
	if (n >= 10)
	{
		ft_putnbr_fd(n / 10, fd);
		ft_putnbr_fd(n % 10, fd);
	}
	else
		ft_putchar_fd(n + '0', fd);
}
728x90
반응형
728x90
반응형

이 함수들은 예전에 형변환을 잘못해서 귀찮았던 적이 있었던 것 같다.

구현하기 쉽고 다들 비슷한 형태이다. 

ft_memcpy: 문자열 src를 n만큼 dest에 복사해주는 함수, 마지막에 dest를 반환해준다.

void	*ft_memcpy(void *dest, const void *src, size_t n)
{
	char	*dest2;
	char	*src2;
	size_t	i;

	dest2 = dest;
	src2 = (char *)src;
	i = 0;
	if (dest2 == src2)
		return (dest2);
	while (i < n)
	{
		dest2[i] = src2[i];
		i++;
	}
	return (dest);
}

 

ft_memccpy: 문자열 src를 n만큼 dest에 복사하되 문자 c까지만 복사해주는 함수
dest에서 c문자가 위치한 위치의 다음 주소를 반환해준다.

void	*ft_memccpy(void *dest, const void *src, int c, size_t n)
{
	char			*ptr1;
	unsigned char	*ptr2;
	size_t			i;

	ptr1 = dest;
	ptr2 = (unsigned char *)src;
	i = 0;
	while (i < n)
	{
		ptr1[i] = ptr2[i];
		if (ptr2[i] == (unsigned char)c)
			return (dest + i + 1);
		i++;
	}
	return (NULL);
}

 

ft_memchr: 문자열 s에서 n 크기 안에 문자 c가 있으면 해당 포인터를, 없다면 null을 반환해주는 함수

void	*ft_memchr(const void *s, int c, size_t n)
{
	unsigned char	*s2;
	unsigned char	c2;
	size_t			i;

	s2 = (unsigned char *)s;
	c2 = (unsigned char)c;
	i = 0;
	while (i < n)
	{
		if (s2[i] == c2)
			return ((void *)s2 + i);
		i++;
	}
	return (NULL);
}

 

ft_memcmp: 문자열 s1, s2를 n만큼 비교하는 함수

비교 도중 다르다면 두 개를 뺀 값을 반환, 끝까지 똑같다면 0을 반환해주면 된다.

int		ft_memcmp(const void *s1, const void *s2, size_t n)
{
	unsigned char	*s11;
	unsigned char	*s22;
	size_t			i;

	s11 = (unsigned char *)s1;
	s22 = (unsigned char *)s2;
	i = 0;
	while (i < n && s11[i] == s22[i])
    {
		if (s11[i] != s22[i])
			return (s11[i] - s22[i]);
        i++;
    }
	return (0);
}

 

ft_memmove: 문자열 src를 n만큼 dest에 옮겨주는 함수

dest의 주소가 src의 주소보다 뒤에 있을 경우에는 주소가 겹칠 수도 있기에 이 경우에는 따로 처리를 해줘야 된다!

dest <= src 라면 그냥 0부터 n까지 (앞에부터) 옮겨주고, dest > src의 경우에는 n부터 0까지(뒤에서부터) 옮겨주면 된다.

EX) dest > src의 경우를 따로 처리하지 않았을 때 -> 앞에서 부터 처리하므로 겹쳐서 꼬일 수도 있음

s = "01234";

dest = s + 2; src = s + 1; n = 2; 라고 치면 처음엔 dest = "134", src = "1134", s = "01134"가 되고

그 다음번엔 dest = "114", src = "1114", s = "01114"가 되어 정말 꼬이게 된다.

EX) dest > src의 경우를 따로 처리했을 때 -> 뒤에서 부터 처리하므로 겹칠수가 없다.

처음엔 dest = "224", src = "1224", s = "01224"가 되고

그 다음번엔 dest = "124", src = "1124", s = "01124"가 된다.

void	*ft_memmove(void *dest, const void *src, size_t n)
{
	char	*dest2;
	char	*src2;
	size_t	i;

	dest2 = (char *)dest;
	src2 = (char *)src;
	i = -1;
	if (!dest && !src)
		return (NULL);
	if (dest2 > src2)
		while ((int)(--n) >= 0)
			dest2[n] = src2[n];
	else
		while (++i < n)
			dest2[i] = src2[i];
	return (dest);
}

 

728x90
반응형
728x90
반응형

ft_split: 문자열을 특정 문자 기준으로 쪼개서 이중 포인터에 저장하여 반환해주는 함수

ft_split("a12aaa12", 'a') = ["12", "12"]과 같은 방식으로 특정 문자가 덕지덕지 붙어있는 경우에도 잘 처리해야 한다.

 

1) 일단 이중 포인터를 할당해야 되기 때문에 문자열에서 분해되는 단어가 몇 개 일지를 미리 파악해야 한다.

이는 ft_word_count 함수에서 처리한다. 처리 방식은 아래 split 하는 방식과 거의 유사하다. 

 

2) 문자열 맨 처음부터 들어있는 c들은 무시 즉 c가 아닌 다른 문자가 보일 때까지 문자열을 넘어가야 한다. 왜냐하면 앞에 쪼개서 저장할 문자가 없기 때문이다. 

ex) ft_split("aaa11a", 'a') = ["11"] -> 보면 앞에 있는 a들은 아무 의미가 없다. '1'까지 넘어가 주면 된다.

 

3) c가 아닌 문자가 보였다면 그 위치부터 그다음에 있는 c의 위치 전까지의 문자들을 이중 포인터에 차례대로 저장해주면 된다. 

그 후에 문자열 끝까지 2와 3을 반복해주면 된다.

ex)  ft_split("aaa12aa345a", 'a') = ["12", "345"] -> 12, 345가 이중 포인터에 차례대로 저장된다.

 

쪼갠 단어를 이중 포인터에 저장하는 건 ft_word_make 함수, 실제로 문자열을 쪼개는 건 ft_split2 함수이다. 

int		ft_word_count(char const *s, char c)
{
	int	i;
	int	cnt;

	i = 0;
	cnt = 0;
	while (s[i])
	{
		if (s[i] == c)
			i++;
		else
		{
			cnt++;
			while (s[i] && s[i] != c)
				i++;
		}
	}
	return (cnt);
}

char	*ft_word_make(char *word, char const *s, int k, int word_len)
{
	int		i;

	i = 0;
	while (word_len > 0)
		word[i++] = s[k - word_len--];
	word[i] = '\0';
	return (word);
}

char	**ft_split2(char **result, char const *s, char c, int word_num)
{
	int		i;
	int		k;
	int		word_len;

	i = 0;
	k = 0;
	word_len = 0;
	while (s[k] && i < word_num)
	{
		while (s[k] && s[k] == c)
			k++;
		while (s[k] && s[k] != c)
		{
			k++;
			word_len++;
		}
		if (!(result[i] = (char *)malloc(sizeof(char) * (word_len + 1))))
			return (NULL);
		ft_word_make(result[i], s, k, word_len);
		word_len = 0;
		i++;
	}
	result[i] = 0;
	return (result);
}

char	**ft_split(char const *s, char c)
{
	int		word_num;
	char	**result;

	if (s == 0)
		return (NULL);
	word_num = ft_word_count(s, c);
	if (!(result = (char **)malloc(sizeof(char *) * (word_num + 1))))
		return (NULL);
	ft_split2(result, s, c, word_num);
	return (result);
}

 

github.com/kimjinho1/Libft

 

kimjinho1/Libft

42 Seoul Libft. Contribute to kimjinho1/Libft development by creating an account on GitHub.

github.com

728x90
반응형
728x90
반응형

ft_atoi: 문자열을 정수로 바꿔주는 함수

ex) ft_atoi("   -1234") = -1234

구현 과정은 간단하게 3가지로 설명 가능할 것 같다. 

1) 입력으로 주어진 문자열에서 앞부분 공백들을 모두 넘어가야 함

2) 공백을 전부 넘어간 다음에 - or + 가 있다면 다음칸으로 넘어감. 만약 - 였다면 sign 변수를 1에서 -1로 바꾼다

3) 0~9 이외의 문자가 나올 때까지 숫자를 계속 업데이트함(n = 10*n + nptr[i])

     업데이트 중에 숫자가 int 타입의 범위를 넘어버리는 경우는 따로 처리를 해줘야 한다(while 문 안에 if, else if 문)

int	ft_atoi(const char *nptr)
{
	int			i;
	int			sign;
	long long	n;

	i = 0;
	sign = 1;
	n = 0;
	while (nptr[i] == ' ' || nptr[i] == '\n' || nptr[i] == '\t' ||
			nptr[i] == '\v' || nptr[i] == '\f' || nptr[i] == '\r')
		i++;
	if (nptr[i] == '-')
		sign *= -1;
	if (nptr[i] == '-' || nptr[i] == '+')
		i++;
	while (nptr[i] && nptr[i] >= '0' && nptr[i] <= '9')
	{
		if (n * sign > 2147483647)
			return (-1);
		else if (n * sign < -2147483648)
			return (0);
		else
			n = n * 10 + (nptr[i] - '0');
		i++;
	}
	return (n * sign);
}

 

ft_itoa: 정수를 문자열로 바꿔주는 함수

atoi의 반대 버전이지만 구현하는 데 아주 조금 힘이 더 든다. 포인트는 배열의 뒷부분부터 채운다는 점인데

ex) n = 123이면 [][][] -> [][][3] -> [][2][3] -> [1][2][3]과 같은 방식으로 문자열에 채워진다.

입력이 음수일 때는 맨 앞에 '-'가 들어가야 하므로 기본 문자열보다 1개 큰 문자열을 할당해줘야 하고 숫자를 채울 때도 주의해줘야 한다.

ex) n = -123이면 [][][][] -> [-][][][] -> [-][][][3] -> [-][][2][3]-> [-][1][2][3]

long long	ft_abs(int n)
{
	long long n2;

	n2 = n;
	if (n < 0)
		return (n2 * -1);
	return (n2);
}

int			ft_intlen(int n)
{
	int			len;
	long long	n2;

	len = 1;
	n2 = ft_abs(n);
	while (n2 /= 10)
		len++;
	return (len);
}

char		*ft_itoa(int n)
{
	char		*str;
	long long	n2;
	int			i;
	int			len;

	n2 = ft_abs(n);
	i = 1;
	len = ft_intlen(n);
	if (n < 0)
		len++;
	if (!(str = (char *)malloc(sizeof(char) * (len + 1))))
		return (NULL);
	if (n < 0)
		str[0] = '-';
	while (i <= len)
	{
		if (str[len - i] != '-')
			str[len - i] = (n2 % 10) + '0';
		n2 /= 10;
		i++;
	}
	str[len] = '\0';
	return (str);
}

 

github.com/kimjinho1/Libft

 

kimjinho1/Libft

42 Seoul Libft. Contribute to kimjinho1/Libft development by creating an account on GitHub.

github.com

 

728x90
반응형
728x90
반응형

여기 부분은 함수들이 정말 간단하고 거의 똑같다.

ft_isalpha: 입력 c가 알파벳이면 참, 아니면 거짓 을 반환해주는 함수

아스키코드를 사용했는데 그냥 문자로 구현해도 똑같다. (65 = 'A' ,90 = 'Z', 97 = 'a', 122 = 'z')

int	ft_isalpha(int c)
{
	return ((c >= 65 && c <= 90) || (c >= 97 && c <= 122));
}

 

ft_isdigit: 입력 c가 숫자이면 참, 아니면 거짓을 반환해주는 함수

isalpha와 똑같은 방식으로 구현했다. (48 = '0', 57 = '9')

int	ft_isdigit(int c)
{
	return (c >= 48 && c <= 57);
}

 

ft_isalnum: 입력 c가 알파벳 or 숫자이면 참, 아니면 거짓을 반환해주는 함수

구현에 isalpha와 isdigit을 사용했다.

int	ft_isalnum(int c)
{
	return (ft_isalpha(c) || ft_isdigit(c));
}

 

ft_isascii: 입력 c가 아스키코드 내 값이면 참, 아니면 거짓을 반환해주는 함수

int	ft_isascii(int c)
{
	return (c >= 0 && c <= 127);
}

 

ft_isprint: 입력 c가 출력될 수 있는 문자면 참, 아니면 거짓을 반환해주는 함수

32부터 126까지가 출력 가능한 아스키코드 값이다(32 = space, 126 = '~') 

참고로 마지막 아스키코드인 127은 Delete

int	ft_isprint(int c)
{
	return (c >= 32 && c <= 126);
}

 

github.com/kimjinho1/Libft

 

kimjinho1/Libft

42 Seoul Libft. Contribute to kimjinho1/Libft development by creating an account on GitHub.

github.com

728x90
반응형

+ Recent posts