프로그래밍 언어/[ C ] 18

[ C ] 17. 연결리스트 구조체 ( Linked List )

[ 연결리스트 구조체 ] 프로그래밍에서 빼놓을 수 없는 자료구조인 연결 리스트(linked list)에 대해 구현해보겠다. 연결리스트는 데이터가 담긴 노드(메모리 공간)을 일렬로 연결해놓았다고 해서 연결리스트라고 부르며 특징은 다음과 같다. 리스트의 중간 지점에 노드를 손쉽게 추가하거나 삭제할 수 있다. 특정 노드를 찾으려면 노드를 모두 검색해야 한다. 크기가 고정되어 있지 않다. 다음은 다른 노드를 가리키는 포인터가 하나씩만 있는 단일 연결 리스트(singly linked list)이다. 지금부터는 구조체, 포인터, 함수, 메모리 할당을 사용하여 단일 연결리스트를 구현하는 방법을 알아보겠다. 참고로 연결리스트는 기본적인 자료구조 이지만 포인터를 사용하다 보니 많은 사람들이 어려워하는 부분이니 너무 걱정..

[ C ] 16. volatile 이용하기

[ volatile ] 변수를 선언할 때 앞에 volatile을 붙이면 컴파일러는 해당 변수를 최적화해서 제외하여 항상 메모리에 접근하도록 만든다. volatile int num1 = 10; // 변수를 최적화해서 항상 메모리에 접근하도록 만듦 volatile로 선언한 변수는 사용할 때 항상 메모리에 접근한다. 즉, 이 변수는 언제든지 값이 바뀔 수 있으니까 항상 메모리에 접근하라고 컴파일러에게 알려주는 것이다. 예를 들면 다음과 같은 반복문이 있다. int i = 0; while(i 컴파일러는 이 코드를 최적화하여 while 반복문을 없애버리고 i에 그냥 10을 할당해 버린다.(Visual Studio의 /02옵션, GCC의 -03옵..

[ C ] 15. 공용체

[ 공용체 ] 공용체는 구조체와 정의 방법이 같지만 멤버를 저장하는 방식이 다르다. 즉, 다음과 같이 멤버들이 각각 공간을 차지하지만 공용체는 모든 멤버가 공간을 공유한다. 즉, 공용체는 멤버 중에서 가장 큰 자료형의 공간을 공유한다. 현실에서 예를 들자면 물건이 하나 들어있는 선물상자와 비슷하다. 같은 크기의 상자지만 들어있는 물건의 종류가 다른 것 처럼. 공용체 만들고 사용하기 union 공용체이름{ 자료형 멤버이름; }; 공용체는 정의만 해서 사용할 수 없고 따로 변수로 선언해서 사용해야한다. #define _CRT_SECURE_NO_WARNINGS // strcpy 보안 경고로 인한 컴파일 에러 방지 #include #inlcude // strcpy 함수 union Box{ short candy;..

[ C ] 14. goto에 관하여

[ goto에 관해 ] goto는 별다른 제약 조건 없이 원하는 부분으로 이동할 수 있기 때문에 초보 때는 goto를 남발하는 경우가 많다. 그러다 보니 처음에는 goto는 가급적 사용하지 말라고 한다. 하지만 goto를 적절히 활용하면 중복되는 코드를 없애고 코드를 좀 더 간결히 만들 수 있을 것이다. 특히 에러처리에 매우 유용하기 때문에 리눅스커널에서도 자주 쓰이며 for, switch 등 중첩 반복문에 많이 쓰이는 것을 알아두자. # 스파게티 코드 스파게티 코드는 `goto`를 과도하게 사용해서 프로그램의 흐름이 마치 스파게티 면발처럼 꼬여있다는데서 붙여진 이름이다. 그래서 스파게티 코드는 가독성이 떨어지고 유지보수가 매우 힘들다. goto와 레이블 #define _CRT_SECURE_NO_WARN..

[ C ] 13. 함수 포인터 배열 활용하기

[ 함수 포인터 배열 활용 ] 함수 포인터 배열 사용하기 #define _CRT_SECURE_NO_WARNINGS #include int add(int a, int b){ return a + b; } int sub(int a, int b){ return a - b; } int mul(int a, int b){ return a * b; } int div(int a, int b){ return a / b; } int main(){ int funcNumber; // 함수 번호 int num1, num2; int (*fp)(int, int) = NULL; // int형 반환값, int형 매개변수 두 개가 있는 함수 포인터 선언 printf("함수 번호와 계산할 값을 입력하세요: "); scanf("%d %d %..

[ C ] 12. 함수와 가변인자

[ 함수와 가변인자 ] printf, scanf와 같이 매개변수의 개수가 정해지지 않은 개수가 있다. 이렇게 매번 함수에 들어가는 인수(argument)의 개수가 변하는 것을 가변 인자(가변 인수, variable argument)라고 한다. 함수에서 가변인자를 정의할 때는 고정 매개변수가 한 개 이상 있어야 하며 고정 매개변수 뒤에 ...을 붙여 매개변수의 개수가 정해지지않았다는 표시를 해준다. 단, ...뒤에는 다른 매개변수를 지정할 수 없다. 반환값 자료형 함수이름(자료형 고정매개변수, ...){ } #include // args는 고정 매개변수 void printNumbers(int args, ...){ printf("%d ", args); } int main(){ printNumbers(1, 1..

[ C ] 11. 함수와 배열

[ 함수와 배열 ] 함수에서 배열을 매개변수로 사용하면 연속된 값을 전달할 수 있꼬, 전달한 배열의 요소를 함수 안에서 변경할 수 있다. 1차원배열 매개변수 함수에서 배열을 매개변수로 사용하려면 ( )(괄호)안에서 매개변수 이름 뒤에 [ ](대괄호)를 붙이거나 매개변수를 포인터로 지정해줍니다. 대괄호를 포인터로 지정하기 반환값자료형 함수이름(자료형 매개변수[]){ } 반환값자료형 함수이름(자료형 *매개변수){ } #include void printArray(int arr[], int count){ // 배열의 포인터와 개수를 받음 for(int i=0; i

[ C ] 10. 함수와 포인터

[ 함수와 포인터 ] void 포인터 매개변수 사용하기 void 포인터 매개변수를 사용하면 자료형 변환을 하지 않아도 모든 자료형을 함수에 넣을 수 있다. 이번에는 char, int, float형을 매개변수로 받아서 값을 서로 바꿔보겠다. #include enum TYPE { TYPE_CHAR, TYPE_INT, TYPE_FLOAT }; void swapValue(void *ptr1, void *ptr2, enum TYPE t){ switch(t){ case TYPE_CHAR:{ // 문자면 char *로 변환한 뒤 역참조하여 값을 서로 바꿈 char temp; temp = *(char *)ptr1; *(char *)ptr1 = *(char *)ptr2; *(char *)ptr2 = temp; break..

[ C ] 09. 전처리기 ( if, ifdef, elif, ifndef, define, endif )

[ 전처리기 ] 일반적인 if조건문과의 차이는 일반 if조건문이 FALSE일 경우, 실행이 되지 않을 뿐 컴파일은 된다면, #if가 0이라면 컴파일 자체가 되지않는다. #include int main(){ if(0){ printf("실행되지는 않지만 컴파일은 됨\n"); } #if 0 printf("컴파일 자체가 안됨\n"); #endif return 0; } => 전처리기의 특성을 이용한 것이다. 전처리기는 실제 컴파일이 수행되기 전에 실행되어 소스에 치환되는 데 #if가 0일 경우에는 소스에 삽입 되지 않는다. 즉 #if는 if와 다르게 조건에 따라 소스코드를 삽입하거나 삭제하기 위해 사용되는 지시자이다. #include #define NUM -3 int main(){ #if NUM printf("i..

[ C ] 08. 구조체

[ 구조체 ] 구조체는 struct 키워드로 정의한다. struct 구조체이름{ 자료형 멤버이름; ... }; // 구조체는 정의만 해서 사용할 수가 없다. 구조체도 변수로 선언해서 사용한다. struct 구조체이름 변수이름; ex. #define _CRT_SECURE_NO_WARNINGS // strcpy 보안 경고로 인한 컴파일 에러 방지 #include #include // strcpy 함수가 선언된 헤더 파일 struct Person{ // 구조체 정의 char name[20]; // 구조체 멤버 1 int age; // 구조체 멤버 2 char address[100]; // 구조체 멤버 3 } int main(){ struct Person p1; // 구조체 변수 선언 // 점으로 구조체 멤버에..

[ C ] 07. 열거형

[ 열거형 ] 열거형은 정수형 상수에 이름을 붙여 코드를 이해하기 쉽게 해준다. 만약 여러개 정수형 상수를 선언한다면 두세 개까지는 괜찮지만 개수가 많아지면 귀찮아 질 것이다. 열거형을 사용하면 정수형 상수를 좀 더 편하게 정의할 수 있다. enum 열거형이름{ value1 = 초깃값, value2, value3 }; 열거형은 정의로만 사용이 안되고 변수로 선언해서 사용해야 한다. enum 열거형이름 변수이름; #include enum DayOfWeek{ // 열거형 정의 Sunday = 0; // 초깃값 할당 Monday, Tuesday, Wednesday, Thursday, Friday, Saturday }; int main(){ enum DayOfWeek week; // 열거형 변수 선언 week ..

[ C ] 06. 포인터연산

[ 포인터연산 ] 포인터로 선언한 변수에는 메모리 주소가 들어있다. 이 포인터 변수에서 연산을 할 수 있다. 마찬가지로 메모리 주소에 일정 숫자를 더하거나 빼면 메모리 주소가 증가,감소 한다. 즉, 포인터 연산을 하면 다른 메모리 주소에 접근할 수 있으며 메모리 주소를 손쉽게 옮겨 다니기 위해서 사용한다. 여기서 메모리 주소가 커지는 상황을 순방향 이동(forward), 메모리 주소가 작아지는 상황을 역방향이동(backward)라 하겠다. 포인터연산으로 메모리 주소 조작하기 #include int main(){ int numArr[5] = { 11, 22, 33, 44, 55 }; int *numPtrA; int *numPtrB; int *numPtrC; numPtrA = numArr; // 배열 첫 번..

[ C ] 05. 메모리와 포인터의 사용

[ 메모리와 포인터의 사용 ] #include #include // malloc, free 함수 int main(){ int *numPtr; // int형 포인터 선언 numPtr = malloc(sizeof(int)); // int의 크기 4바이트만큼 동적 메모리 할당 *numPtr = 10; // ****포인터를 역참조 한뒤 값 할당**** printf("%d\n", *numPtr); // 10 , 포인터를 역참조하여 메모리에 저장된 값 출력 free(numPtr); // 동적 메모리 해제 return 0; } malloc함수에 실제로 값을 저장할 때는 시스템의 한계(RAM, 디스크)이상 저장할 수 없다. 메모리 내용을 한꺼번에 설정하기(memset) memset함수를 사용하면 메모리의 내용을 원하는..

[ C ] 04. 포인터의 형변환

[ 포인터의 형변환 ] #include #incldue // malloc, free 함수가 선언된 헤더파일 int main(){ int *numPtr = malloc(sizeof(int)); // 4바이트만큼 메모리 할당 char *cPtr; *numPtr = 0x12345678; cPtr = (char *)numPtr; // int 포인터 numPtr을 char 포인터로 변환. 메모리 주소만 저장됨 printf("0x%x\n", *cPtr); // 0x78 : 낮은 자릿수 1바이트를 가져오므로 0x78 free(numPtr); // 동적메모리 해제 return 0; } # 실행 결과 0x78 numPtr에 메모리를 할당하고 역참조하여 0x12345678을 저장했다. 그리고 cPtr = (char *)n..

[ C ] 03. 포인터와 역참조 연산자

[ 포인터와 역참조 연산자 ] 포인터 변수에는 메모리 주소가 저장되어 있다. 이 때 메모리 주소가 있는 곳으로 이동해서 값을 가져오고 싶다면 역참조 연산자 *를 이용한다. #include int main(){ int *numPtr; // 포인터 변수 선언 int num1 = 10; // 정수형 변수를 선언하고 10 저장 numPtr = &num1; // num1의 메모리 주소를 포인터 변수에 저장 printf("%d\n", *numPtr); // 10 // 역참조 연산자로 num1의 메모리 주소에 접근하여 값을 가져온다 return 0; } # 실행 결과 10 역참조 연산자 *는 포인터 앞에 붙인다. 다음과 같이 numPtr 앞에 *를 붙이면 numPtr에 저장된 메모리 주소로 가서 값을 가져온다. 여기..