#include <iostream>
using namespace std;

int main()
{
    char a[] = "leeseungwoo";
    char* first = (char*)a;
    char* last = (char *)a + strlen(a) -1;

    while (first < last)
    {
        char temp = *first;
        *first = *last;
        *last = temp;
        
        first++;
        last--;
    }

    cout << a << endl;

}

 

'C' 카테고리의 다른 글

해쉬테이블 기초(간단)  (0) 2020.02.28
C언어 스택, 큐 자료구조 소스 코드  (0) 2020.02.26
C언어 빌드과정(컴파일)  (0) 2020.02.25
C언어 배열 삽입, 삭제  (2) 2020.01.03
C언어 void*  (0) 2019.12.20
해쉬 테이블

검색, 삽입, 삭제 모두가 다 O(1)인 엄청난 녀석이다.

삽입, 삭제가 O(1)이면 검색이라도 O(N)이 되어야 되는데 어떻게 모두가 다 O(1)일까?

검색이 O(1)이란 말은 인덱싱을 사용할 수 있다는 말이다.

 

C언어 포큐아카데미

가장 작은값 0부터 가장 큰 값 793까지의 배열을 만들고(총 794개) 값을 입력하면 1로 비어있으면 0으로 세팅한다.

이렇게 되면 입력할때 O(1)이 되고 검색할때도 인덱스를 사용할 수 있으니 O(1)이 된다. 이렇게 사용하지도 않은 메모리를 너무 많이 사용한다는 단점이 있다. 말그대로 무식 그자체다.

 

 

덜 무식하게

C언어 포큐아카데미

무식하게 모든 범위의 메모리를 잡아 두지말고 임의의 숫자에 메모리 인덱스를 매핑 시키면 좀 더 아름다워진다.

배열의 크기가 10이라고 가정하고 숫자에 메모리 인덱스를 매핑 시키는 방법은 간단하게 10으로 나눈후 나머지를 인덱스값으로 세팅하면 된다.

 

여기서 중요한 점은 중복이 발생한다는 점이다. 703이나 793이나 나머지는 3으로 똑같다. 이미 3번 인덱스에는 703이 들어가있는데 793의 값이 들어오면 덮어 쓸수도 없고 난감해진다. 중복을 제거해보자.(물론 불가능)

 

배열의 크기도 충분히 늘리고(2배) 나누는 값을 소수로 두면 중복을 최소화 할 수 있다. 하지만 중복을 막을 순 없다 중복이 하나라도 발생한다면 데이터의 신뢰성이 보장 될 수 없다. 어떻게 하면 좋을까?

 

 

중복 제거(불가능)

원칙은 나머지값의 인덱스에 값을 넣으면 되지만, 이미 그 인덱스에 값이 존재한다면(충돌) 가장 인접해 있는 빈 공간에 값을 넣으면 된다. 즉 색인을 1씩 증가 시키면서 빈 공간을 찾다가 비어 있으면 넣는다는 말이다. 물론 이때는 자신의 나머지 값을 인덱스로 같지 않는다.

 

일단 배열을 만들면 절대 사용하지 않을 값 INT_MIN으로 초기화 한 뒤 자신의 인덱스에 해당하는 배열에 값을 넣는다.

 

1. 793을 11인덱스에 넣는다

2. 724를 11인덱스에 넣으려고 한다. 하지만 793이 이미 있다

3. 인덱스를 1증가 시키면서 빈공간을 찾은뒤 가장 가까운 빈공간에 넣는다.(12 인덱스에 넣음)

4. 계속 반복

 

 

코드
#include <limits.h>

#define	BUCKET_SIZE (23)
int s_numbers[BUCKET_SIZE];

void init_hashtable(void)
{
	size_t i;

	for (i = 0; i < BUCKET_SIZE; i++)
	{
		s_numbers[i] = INT_MIN;
	}
}

int has_value(int value)
{
	int i;
	int start_index;

	start_index = value % BUCKET_SIZE;
	i = start_index;

	do
	{
		if (s_numbers[i] == value) {
			return true;
		}
		else if (s_numbers[i] == INT_MIN) {
			return false;
		}

		i = (i + 1) % BUCKET_SIZE;

	} while (i != start_index);

	return false;
}

int add(int value)
{
	int i;
	int start_index;

	start_index = value % BUCKET_SIZE;
	i = start_index;
	do
	{
		if (s_numbers[i] == value || s_numbers[i] == INT_MIN)
		{
			s_numbers[i] = value;
			return true;
		}
		i = (i + 1) % BUCKET_SIZE;
	} while (i != start_index);
	
	return false;
}

int main()
{
	init_hashtable();

	add(703);
	add(793);
	add(724);
	add(441);
	add(219);
	add(1);
	add(81);
	add(546);
	add(777);
	add(747);

	return 0;
}

'C' 카테고리의 다른 글

문자열 뒤집기(포인터 사용)  (0) 2020.03.12
C언어 스택, 큐 자료구조 소스 코드  (0) 2020.02.26
C언어 빌드과정(컴파일)  (0) 2020.02.25
C언어 배열 삽입, 삭제  (2) 2020.01.03
C언어 void*  (0) 2019.12.20
스택
#include <stdio.h>
#include <cassert>

enum { MAX_NUMS = 8};
int s_nums[MAX_NUMS];
size_t s_num_count = 0;

void push(int arr[], int n)
{
	//시간 복잡도 O(1)
	assert(s_num_count < MAX_NUMS);
	arr[s_num_count++] = n;
}

int is_empty(void)
{
	return s_num_count == 0;
}

int pop(void)
{
	//시간 복잡도 O(1)
	assert(is_empty() == false);
	return s_nums[--s_num_count];
}

int search(int n)
{
	//시간 복잡도 O(N)
	//제일 위부터 찾을 때까지 검색
	int result = -1;
	size_t temp_count = s_num_count;
	while (s_num_count > 0)
	{
		int value = pop();
		if (value == n) 
		{
			result = s_num_count;
		}
	}
	
	s_num_count = temp_count;
	return result;
}

int main()
{
	push(s_nums, 88);
	push(s_nums, 44);
	push(s_nums, 22);
	printf("%d\n", search(22));
	push(s_nums, 11);
	push(s_nums, 5);
	for (size_t i = 0; i < s_num_count; i++)
	{
		printf("%d\n", s_nums[i]);
	}
	printf("%d\n", search(5));

	return 0;
}

push와 pop으로 이루어진 스택 자료구조. 간단하다 

주의할점은 search할때 pop으로 망가진 배열을 다시 원상 복구 해줘야한다는점..

 

 

#include <stdio.h>
#include <cassert>

enum { MAX_NUMS = 8};
int s_nums[MAX_NUMS];
size_t s_front = 0;
size_t s_back = 0;
size_t s_num_count = 0;


int is_empty(void)
{
	return s_num_count == 0;
}

void enqueue(int n)
{
	assert(s_num_count < MAX_NUMS);

	s_nums[s_back] = n;

	s_back = (s_back + 1) % MAX_NUMS;

	++s_num_count;
}

int dequeue(void)
{
	int ret;

	assert(is_empty() == false);

	ret = s_nums[s_front];

	--s_num_count;
	s_front = (s_front + 1) % MAX_NUMS;

	return ret;
}


int search(int n)
{
	//시간 복잡도 O(N)
	int result = -1;
	for (size_t temp = s_front; temp < s_back; temp++)
	{
		if (s_nums[temp] == n)
		{
			result = temp - s_front;
		}
	}
	return result;
}

int main()
{
	enqueue(10);
	enqueue(20);
	enqueue(30);
	enqueue(40);
	enqueue(50);

	dequeue();
	dequeue();
	printf("%d\n", search(30));
	return 0;
}

'C' 카테고리의 다른 글

문자열 뒤집기(포인터 사용)  (0) 2020.03.12
해쉬테이블 기초(간단)  (0) 2020.02.28
C언어 빌드과정(컴파일)  (0) 2020.02.25
C언어 배열 삽입, 삭제  (2) 2020.01.03
C언어 void*  (0) 2019.12.20
기본사항

포큐아카데미 C언어

 

포큐아카데미 C언어

 

전처리기

입력값: 내가 작성한 소스코드

출력값: 트랜스레이션 유닛

설명: 전처리기는 주석을 제거하고,  include header 파일을 복붙한다.

예를들면 #include "adder.h" 라는 코드가 있으면 adder.h로 가서  int add(int a, intb);와 같은 선언을 복사 붙이기 해준다.  include <stdio.h> 하면 약 2천5백줄의 선언들을 복붙해줌.

 

 

 

컴파일러

입력값: 트랜스레이션 유닛

출력값: 어셈블리 코드

설명: 트랜스레이션 유닛 소스코드를 어셈블리어로 바꿔준다.(아직은 기계어가 아님) 여기서 핵심은 구멍이다. 똥구멍....

포큐아카데미 C언어

핵심은 일단 헤더(선언)만 보고 컴파일한다. 원형(내용)은 신경쓰지 않는다.

임의의 함수를 CALL하면(main에서 add 함수 호출) 링커가 그 구멍을 메워줄테니 일단은 컴파일하자!

 

 

어셈블러

입력값: 어셈블리 코드

출력값: 오브젝트 코드 : 기계어, 바이너리코드, 이진코드

설명: 어셈블러와 마찬가지로 구멍이 있음

 

포큐아카데미 C언어

 

 

링크

입력값: 모든 오브젝트 코드

출력값: 실행파일(.exe 파일)

설명: 모든 오브젝트 코드를 모아다 구멍을 메꾼 뒤 실행파일로 저장

좀 더 자세히(서술): 링커가 오브젝트 파일을 다 모아서 하나의 이진(binary)파일로 만들고 있었다. 그러다가 main 함수에서 add를 호출하는 것을 보았다. 현재 main함수에 add call은 선언만 있지 정의(내용)은 없다 즉 구멍이 뚫려있다. 이것을 본 링커는 똥구멍을 긁으며 "내가 오브젝트 파일을 다 검토하고 있는데 add 함수의 정의(내용)는 XX주소에 있는걸 확인했어 그러니 그 구멍에 XX주소로 점프하는 코드를 넣어줄게!

 

왜 링킹하냐?

 

'C' 카테고리의 다른 글

해쉬테이블 기초(간단)  (0) 2020.02.28
C언어 스택, 큐 자료구조 소스 코드  (0) 2020.02.26
C언어 배열 삽입, 삭제  (2) 2020.01.03
C언어 void*  (0) 2019.12.20
C 언어 변수에 함수 할당  (0) 2019.12.20
배열은 O(N)이다.

배열은 삽입, 삭제, 검색을 할 때 O(N) 즉 loop를 한번 돈다. 아주 기본 중 기본..

C언어로 삽입, 삭제의 알고리즘 코드를 작성해보자.

 

 

배열의 삽입(우로밀착)
int nums[8];
size_t max_count = 0;

void insert_at(size_t index, int number)
{
	size_t i;

	// 우로밀착 코드
	for(i = max_count; i > index; i--)
	{
		nums[i] = nums[i-1];
	}

	nums[index] = number;
	++max_count;
}

void main()
{
	insert_at(0, 1);
	insert_at(0, 2);
	insert_at(0, 3);
	insert_at(0, 4);

	delete_at(0);
	size_t i;
	for (i = 0; i < MAX_NUMS; i++)
	{
		printf("%d\n", nums[i]);
	}
}

배열의 삽입이 0번째 인덱스 즉 가장 첫번째 자리에서 이뤄진다면 0번째 인덱스를 기준(현재 인덱스 포함)으로 오른쪽에 있는 모든 요소들은 우로밀착! 오른쪽으로 한 칸 이동 해야한다. 그래서 for loop가 필요하다.O(N)

 

 

배열의 삭제(좌로밀착)
void delete_at(size_t index)
{
	size_t i;
	for (i = index; i < max_count; i++)
	{
		nums[i] = nums[i + 1];
	}
	--max_count;
}

배열의 삭제가 0번째 인덱스에서 이뤄진다면 0번째 인덱스를 기준으로 좌로 한칸씩 밀착해야 한다.

'C' 카테고리의 다른 글

C언어 스택, 큐 자료구조 소스 코드  (0) 2020.02.26
C언어 빌드과정(컴파일)  (0) 2020.02.25
C언어 void*  (0) 2019.12.20
C 언어 변수에 함수 할당  (0) 2019.12.20
C언어 얕은복사 vs 깊은복사  (0) 2019.12.18

int*  는 포인터 변수인데 그 가르키는 주소의 type이 int, char* 는 포인터 변수인데 가르키는 주소의 type이 char

그렇다면 void* 는 가르키는 주소의 type이 void 라는 말인데.. void 라는 애매모호한 type이 도대체 뭘까?

 

void*

void*는 바로 범용적 포인터 즉 일단 가르키고 본다. 어떤 포인터라도 대입이 가능하다 이말이다.

매개 변수로 void*를 사용하면 모든 포인터를 일단 받고 보겠다 이말이다.

 

하지만 역참조나 포인터 산술 연산을 할 때는 반드시 다른 포인터로 캐스팅해서 써야한다

void* var로 받고 나중에 역참조할때는 *(int* var) 이런식으로 해줘야한다!

 

 

C 언어 포큐아카데미

 

1. void* p에 float 포인터를 담았다. printf로 값을 출력할 때는 반드시 float 포인터로 캐스팅 해줘야한다.

2. 함수 add의 op1, op2는 void*로 일단 담았다. 역참조 할떄는 반드시 *(int*)op1, *(int*)op2 처럼 캐스팅 해줘야한다.

 

이렇게 캐스팅해주지 않으면 컴퓨터 입장에서 몇 바이트 만큼 읽으라는지 알 수가 없다!!

 

포큐아카데미 C언어 복습

'C' 카테고리의 다른 글

C언어 빌드과정(컴파일)  (0) 2020.02.25
C언어 배열 삽입, 삭제  (2) 2020.01.03
C 언어 변수에 함수 할당  (0) 2019.12.20
C언어 얕은복사 vs 깊은복사  (0) 2019.12.18
C언어 구조체  (0) 2019.12.18
함수를 변수에 저장?

JS나 python을 보면 파라미터로 함수를 던지거나 변수에 함수를 할당하거나 이런 일들을 많이 하는데 처음에는 너무나 골때려서 헷갈렸다. 어떻게 저렇게 함수를 넘길수있지? 함수는 실행하는 놈인데.. 이런걸 First Class Function 이라고 하라나 뭐라나.. 

 

C언어 포큐아카데미

위의 예제처럼 op1, op2를 받고 operator로 함수를 받고 싶은 함수가 있다. 그러니깐 함수를 파라미터로 던져야된다..

도대체 어떻게 할까?

 

 

함수 호출은 주소로의 Jump다

C언어 포큐아카데미

Sub 함수를 호출하는 것은 내부적으로 컴파일 될때 지정된 주소로 Jump 하는 것과 같다. 

 

00E211A6 메모리 주소에 저장된 명령어 call은 sub 함수 호출 즉 0E21040 주소로 jump 하라는 명령어다.

그러니 00E21040과 같은 함수 시작 주소를 담는 변수만 있으면 된다는 말이다. 어?!! 주소를 담는 변수? 그래 바로 포인터다. 함수 포인터!!

 

 

함수 포인터 문법

C언어 포큐아카데미

함수 포인터니깐 function * 이렇게 쓰면 귓빵맹이를 맞는다 

 

C언어 포큐아카데미

함수 포인터는 <반환형> (* 변수명)<매개변수 목록> 이런식으로 사용해야된다!

 

calculate에서 받는 매개변수 x, y는 쉬위니깐 PASS하고 함수를 받을때는 double (* func)(double, double) 이런식으로 해야된다!

 

C언어 포큐아카데미 좋다.

'C' 카테고리의 다른 글

C언어 배열 삽입, 삭제  (2) 2020.01.03
C언어 void*  (0) 2019.12.20
C언어 얕은복사 vs 깊은복사  (0) 2019.12.18
C언어 구조체  (0) 2019.12.18
C언어 포인터 배열  (0) 2019.12.13
얕은복사

실제 데이터가 아니라 주소를 복사하는 것을 얕은복사라 한다.

#include <stdio.h>

typedef struct name{
	char* lastname;
	char* firstname;
} name_t;

void main()
{
	char fistname[] = "Lulu";
	char lastname[] = "Lee";

	name_t name;
	name_t clone;

	name.lastname = lastname;
	name.firstname = fistname;

	clone = name;
	name.lastname[0] = 'N';

	printf("origin: %s %s\n", name.firstname, name.lastname);
	printf("clone: %s %s\n", clone.firstname, clone.lastname);

}

// 출력값
// origin: Lulu Nee
// clone: Lulu Nee

골때리게도 name의 lastname만 N으로 바꿨는데 clone의 lastname까지 변경됐다.

clone = name의 실행이 바로 얕은복사 이기 때문이다.

 

name의 구조체에서 char* lastname은 포인터다. 포인터는 주소를 변수로 저장하는 놈이다.

그러므로 구조체 char* lastname은 main함수의 lastname 배열의 주소를 값으로 가지고 있다.

따라서 mian 함수에서 name.lastname[0] ='N'을 하게되면 main 함수의 lastname의 원본 L이 N으로 변경되고 그 주소를 참조하는 변수 clone, name은 둘다 변경 된 것처럼 된다.

 

 

얕은복사 메모리

C언어 포큐아카데미

name과 clone의 lastname 값을 보면 105번 주소를 저장하고 있고 105번에는 L이 저장돼있다.

 

 

C언어 포큐아카데미

name.lastname[0]은 원본을 바꾼다. 나머지 포인터들은 그냥 가르키는 값을 참조 할 뿐이니 둘다 바뀐것 처럼 느껴진다.

 

 

깊은복사

별도의 메모리 공간에 실제 데이터를 복사한다.

//typedef struct name{
//	char* lastname;
//	char* firstname;
//} name_t;

typedef struct name{
	char lastname[64];
	char firstname[64];
} name_t;

void main()
{

	name_t name = {"Lulu", "Lee"};
	name_t clone;


	clone = name;
	name.lastname[0] = 'N';

	printf("origin: %s %s\n", name.firstname, name.lastname);
	printf("clone: %s %s\n", clone.firstname, clone.lastname);
}

// 출력값
// origin: Lee Nulu
// clone: Lee Lulu

얕은복사 코드를 위의 코드로 변경하면 origin 즉 원본의 값만 변경되고 clone 값은 유지된다. 왜냐? 별도의 메모리 공간을 확보한 뒤 거기에 값을 복사했기 때문이다.

 

'C' 카테고리의 다른 글

C언어 void*  (0) 2019.12.20
C 언어 변수에 함수 할당  (0) 2019.12.20
C언어 구조체  (0) 2019.12.18
C언어 포인터 배열  (0) 2019.12.13
C 포인터 const  (0) 2019.12.12

+ Recent posts