프로그래밍/C 9

execve() argv 포인터 배열 확인

execve의 시스템 콜 표이다. 레지스터는 rax, rdi, rsi, rdx순이다. rax는 59, rdi에는 파일 이름의 포인터, rsi에는 argv 포인터 배열이 온다. rdx는 시스템 환경 변수가 담긴다는데, 스킵하겠다. execve함수도 어차피 내부적으로는 sys_execve로 구현이 되어있고, 파라미터도 똑같이 받아준다. /bin/ls를 통해서 한번 어떤 식으로 구현이 되는지 확인을 해보겠다. 다음과 같은 코드를 짜서 gdb로 까보겠다. 1 2 3 4 5 6 7 8 #include #include int main(){ char * arr[3]={"/bin//ls","-al",NULL}; execve(arr[0],arr,NULL); return 0; } cs argv[0]은 항상 자기 자신이 들..

프로그래밍/C 2022.05.24

C 함수 포인터, void 포인터 차이

C에서는 괄호 없이 함수를 적어줌으로써 함수의 주소 얻을 수 있다. 그리고 그 주소를 함수 포인터와 void 포인터에 넣을 수 있다. 하지만 int 같은 형을 가진 포인터들에 주소를 넣어주려 하면, 에러를 뱉어낸다. 기본적으로 주소에서 몇 바이트만큼 읽어올 것인지와 포인터에 대한 연산이 포인터의 형에 따라 달라지기 때문에 당연하다고 볼 수도 있다. 예를 들어서 int형 포인터는 +1 하면 4바이트씩 더해져서 읽어진다. void 포인터는 일반적으로 포인터 타입 캐스팅한 뒤에 사용된다. 함수 포인터와 void 포인터 둘다 함수의 주소를 넣어줄 수는 있지만 함수를 실행시키는 것은 함수 포인터만 가능하다. 내부적으로는 둘다 차이가 없는지 궁금해서 gdb로 뜯어보겠다. gcc -g -o C C.c로 컴파일해주고 ..

프로그래밍/C 2022.05.22

C 배열 포인터 array VS &array 차이, 디버깅을 통한 확인

1 2 3 4 5 6 7 #include int main(){ int arr[10]={1,2,3,4,5,6,}; printf("%p %p",arr,&arr); return 0; } cs 위와 같은 코드를 입력하고 돌려보면, 위와 같은 주소를 확인할 수 있다. 여기서 둘 다 같은 주소를 얻을 수 있으니, 같다고 생각하기 쉽다. 1 2 3 4 5 6 #include int main(){ int arr[10]={1,2,3,4,5,6,}; printf("%p %p",arr+1,&arr+1); return 0;} } cs 조금 수정해서 다시 돌려보면, 차이를 알 수 있다. 기본적으로 C언어에서 포인터에 연산을 해주면, 형에 따라서 가중치만큼 더하거나 빼준다. int형의 경우에는 +1을 하면 일반적으로 4바이트를 ..

프로그래밍/C 2022.05.22

size_t, pid_t,ssize_t...

size_t, pid_t같은 자료형들은 primitive 자료형이라고 한다. primitive 자료형들은 sys/types.h 헤더파일에 정의되어있다. int나 long long int 같은 자료형을 사용한다면, 나중에 크기나 데이터 표현 방식이 달라지면서 코드를 사용할 수 없게 될 수 있다. int형은 16bit 운영체제 시절에는 2byte의 크기를 가졌었고 32bit 운영체제에선 4byte의 크기를 가진다. 이렇게 자료형의 크기가 바뀌게 되면 전체 코드 수정이 불가피 했었다. 그래서 헤더파일에 typedef를 사용해서 미리 unsigned int는 size_t처럼 미리 정의해두고, 만약 수정이 필요하면 헤더파일만 수정해서 자료형을 재정의해주면 된다. 결과적으로 소스 전체를 수정할 필요가 없게 된다. ..

프로그래밍/C 2022.05.03

open, read 시스템콜을 이용한 파일 입출력

유닉스 시스템에선 모든 것이 파일이다. 장치도 파일로 취급된다. /dev/had1 같은 하드디스크 상의 파일도 존재한다. 이렇게 장치도 파일로 인식을 하면, 파일을 다루는 것처럼 장치에 접근할 수 있다. 당연히 파일의 종류도 나뉘어 있다. 예를 들어 d가 디렉토리, c가 장치인 것처럼 말이다. 파일을 다루기 위한 기본적인 흐름은 아래와 같다. 1) 파일을 연다. 2) 파일을 읽거나 쓴다. 3) 파일을 닫는다. 기본적으로 프로세스는 유저 모드에서 직접적으로 시스템에 접근할 수 없다. 때문에 시스템 콜을 통해서 운영체제에게 자원을 요청한다. 키보드 입력이나 메모리 공간을 요청하는 것들은 모두 자원을 요청하는 것의 예시이다. 그리고 이런 작업들은 커널 모드에서 이루어지게 된다. 아래 사진을 보면 커널이 프로세..

프로그래밍/C 2022.05.03

scanf, gets, fgets 차이

scanf()는 문자열 입력시, 엔터전까지 문자열을 가져온다. 공백이 나오면 공백 이전까지 저장되고 마지막에 \0가 붙는다. 공백을 입력받기 어렵다보니 잘 안쓴다. abc를 입력했으니 실질적으로는 abc\0 이런식으로 저장된다. gets()는 \n까지 가져오고 \n을 \0으로 바꾼다. 결론적으로 abc\0가 저장된다. fgets()는 \n까지 가져오고 뒤에 \0를 덧붙여서 저장해준다. abc\n\0으로 저장된다. C는 입출력을 버퍼를 통해서 한다. 키보드로 입력을 할때 문자열이 버퍼에 들어간다. 그리고 함수들이 그 버퍼에서 가져오게 된다. scanf같은 함수가 문자들을 가지고 올때, \n을 버퍼에 남기고 가져가기 때문에, 다음에 문자열 입력을 받을 때 어려움이 생길 수 있다. 또한 우리가 일반적으로 입력..

프로그래밍/C 2022.04.01

EOF(End Of File)

EOF는 End Of File의 약자로 -1로 정의되어있다. 파일의 끝을 탐지하는 방법은 운영체제마다 다르지만, C언어에서는 EOF를 통해서 파일의 끝을 탐지하고 키보드를 통한 입력의 끝도 알려준다. 함수 호출시 에러가 있는지 없는지에 대한 반환값을 검사할 때 주로 사용되고 이는 신뢰성있는 코드를 작성하는데 도움을 준다. 키보드를 대상으로 하는 getchar나 scanf같은 함수들은 두가지 경우에 EOF를 반환한다. 1) 함수 호출을 실패할때 2) 유닉스는 Ctrl + D, 윈도우는 Ctrl + Z를 통해서 EOF를 발생시킬때 키보드의 입력에서 엔터도 결국은 문자라서 끝을 알려줄 방법이 없다. 때문에 위와같은 특정 키를 입력해서 EOF를 발생시킨다. 외외로 평소에 반환 값이 있는지 몰랐던 함수들이 꽤 있..

프로그래밍/C 2022.03.31

함수 호출 방식

함수를 호출하는 형태에 따라서 Call-by-reference와 Call-by-value를 구분한다. 일반적으로 함수에 인자를 넘겨줄 때, 단순히 그 값이 복사되어 전달이 이루어지는 형태가 Call-by-value이다. 때문에 여기서 pp에게 a를 인자로 넘겨줄때, 값만 a에서 매개변수 n으로 복사가 되기 때문에, 결과적으로는 변수 a의 값이 변하지 않는다. 반면 Call-by-reference는 메모리 접근에 이용되는 주소 값을 전달하는 형태이다. 이렇게 수정하면, pp 함수가 직접 메모리에 접근해서 a의 값을 증가시킬 수 있다. 이러한 이유 때문에 scanf도 &연산자를 사용하여 값을 저장시킨다.

프로그래밍/C 2022.03.29

C 배열 접근

문제를 풀다가 모르는 부분을 발견했다. 검색을 해도 안나와서 영어로 끄적거렸더니 확실한 답을 찾을 수 있었다. 배열을 []를 통해서 접근하는 것과 주소 그 자체로 접근하는 것의 차이를 잘 모르고 있었다. 배열 그 자체는 주소를 가리키고, []는 *처럼 그 주소가 가리키는 값을 가리킨다. https://www.programiz.com/c-programming/examples/access-array-pointer C Program to Access Array Elements Using Pointer www.programiz.com data[0] is equivalent to *data and &data[0] is equivalent to data data[1] is equivalent to *(data + ..

프로그래밍/C 2022.03.25