새벽을 밝히는 붉은 달

2. 두번째 C언어 과제 본문

Develop/C

2. 두번째 C언어 과제

자윰 2019. 5. 26. 02:24

아, 상상만 해도 끔찍하다.

2번째 과제는 개인적으로 너무 어려웠다.

1번째에 비해 난이도가 많이 뛴 느낌이었다.

 

또한, 상당히 많은 자괴감을 느꼈었다.

나는 3일 째 첫번째 문제를 풀고 있는 중인데,

다른 동기는 2시간씩이나(!) 걸려서 풀었다고 하니..

 

그리고 처음으로 소스를 제출하면 자동으로 테스트 케이스를 돌리는 프로그램으로

과제를 제출하게 되었다.

근데, 이게 정말 환장할 노릇인게

어디서 케이스를 빼먹은건지 모르겠다는거다!

(그리고 아직도 모름)

 

다음에 기회가 된다면 조교님께 예시 코드를 올려달라고 할 생각이다.

원래, 배우는 시기엔 남이 해놓은 것만 봐도 많은 걸 배운다고 하니 말이다.


 1. Calculate frequency of all elements in an array

 

첫번째부터 난관에 봉착했다.

아니, 배열 크기도 안 정해졌는데 숫자를 막 입력받으라고?

 

그래서 수업시간에 배운 variable-length array를 쓰려고 했는데, 

아뿔싸.

얘는 array[size]라고 선언했으면 앞에 size를 입력받고 그 size를 집어넣는 형식이다.

근데 나는! size도 입력 안 받고 바로 출력해야 하는 것이다!

환장할 노릇이었다.

 

그래서 생각한 방법은, 문자열로 숫자를 입력받는 것이었다.

 

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

#define FLUSH while(getchar() != '\n')

void frefunction(int* input, int len);

int main()
{
	char arr[400];
	scanf("%[^\n]s", arr);
	FLUSH;

	for (int i = 0; arr[i] != '\0'; i++) {
		if ((arr[i] < 48 || arr[i] > 57) && arr[i] != 32) {
			printf("Invalid input. Try again.\n");
			return 0;
		}
	}
	int len = 0;
	for (int k = 0; arr[k] != '\0'; k++) {
		if (arr[k] == ' ')
			len++;
	}

	int* int_num = (int*)malloc(sizeof(int)*len);
	char* ptr = strtok(arr, " ");

	int j = 0;
	while (ptr != NULL) {
		int_num[j] = atoi(ptr);
		ptr = strtok(NULL, " ");
		j++;
	}
	for (int Err_idx = 0; Err_idx <= len; Err_idx++) {
		if (int_num[Err_idx] < 0 || int_num[Err_idx] > 200) {
			printf("Invalid input. Try again.\n");
			return 0;
		}
	}
	frefunction(int_num, len);
	return 0;
}

void frefunction(int* input, int len) {
	printf("Number  Frequency\n");

	for (int num = 0; num <= 200; num++) {
		int frequency = 0;

		for (int Fre_idx = 0; Fre_idx <= len; Fre_idx++) {
			if (input[Fre_idx] == num)
				frequency++;

		}
		if (frequency > 0)
			printf("%6d  %9d\n", num, frequency);
	}
}

먼저 %[^\n]s를 써서 스페이스까지 입력을 받게 만든다.

 

만약 숫자가 아닌 다른 문자가 입력이 되었다면 에러 문구를 출력하게 했다.

 

배열의 index를 하나씩 증가해가며 스페이스의 개수를 센 후,

스페이스의 개수를 크기로 하는 배열을 동적할당해준다.

 

strtok함수를 통해 스페이스를 기준으로 숫자를 끊은 후

atoi함수를 이용해 문자열에 저장된 수를 정수형으로 변환시켜 배열 한칸에 몰아넣어준다.

(이건 친한 언니가 도와줬다. 언니 고마워!)

 

만약, 배열에 저장된 수가 0~200의 범위를 넘어가면 에러문구를 출력한다.

(얘는 과제에 주어진 조건이다.)

 

2개의 error를 handling한 후 frequency를 세는 함수를 실행한다.

숫자 0일 때 배열을 처음부터 끝까지 비교해서 frequency > 0 이라면

숫자와 빈도수를 출력하게 했다.

이 과정을 숫자 200 까지 반복하게 했다.

 

그런데, 테스트 케이스 10개 중 8개만 맞았다.

아직도 어디서 틀린건지는 잘 모르겠다.

 

이 1번 과제를 하면서, 상당히 다양한 함수에 대해 알아보고 공부하게 되었다.


2.  Add two numbers in string

 

이 문제는 int형 범위를 넘어가는 경우 덧셈을 해야 할 때

문자열로 받아 덧셈을 하는 문제이다.

 

참고로 이건 코드가 좀...! 길다....!

함수로 정리를 하려고 했으나 힘들어서 도저히 못하겠더라.

 

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define MSG4ERR "Invalid input. Try again.\n"

void sum(char* arr_first, char* arr_second, int len);

int main(void) {
	char arr1[100], arr2[100];
	char arr_first[100], arr_second[100];
	int len_result = 0;

	scanf("%s", arr1);
	int len1 = strlen(arr1);
	if (len1 > 99) {
		printf(MSG4ERR);
		return 0;
	}
	for (int i = 0; arr1[i] != '\0'; i++) {
		if (arr1[i] < 48 || arr1[i] > 57) {
			printf(MSG4ERR);
			return 0;
		}
	}

	scanf("%s", arr2);
	int len2 = strlen(arr2);
	if (len2 > 99) {
		printf(MSG4ERR);
		return 0;
	}
	for (int i = 0; arr2[i] != '\0'; i++) {
		if (arr2[i] < 48 || arr2[i] > 57) {
			printf(MSG4ERR);
			return 0;
		}
	}

	if (len1 > len2) {
		arr_first[0] = '0';
		len_result = len1;

		for (int i = 0; i < len1; i++)
			arr_first[i + 1] = arr1[i];
		arr_first[len_result + 1] = '\0';

		for (int k = 0; k <= len1 - len2; k++)
			arr_second[k] = '0';
		for (int j = 0; j < len2; j++)
			arr_second[len1 - len2 + 1 + j] = arr2[j];
		arr_second[len_result + 1] = '\0';
	}
	if (len1 == len2) {
		arr_first[0] = '0';
		arr_second[0] = '0';
		len_result = len1;

		for (int m = 0; m < len1; m++)
			arr_first[m + 1] = arr1[m];
		arr_first[len_result + 1] = '\0';

		for (int n = 0; n < len1; n++)
			arr_second[n + 1] = arr2[n];
		arr_second[len_result + 1] = '\0';
	}
	if (len2 > len1) {
		arr_second[0] = '0';
		len_result = len2;

		for (int i = 0; i < len2; i++)
			arr_second[i + 1] = arr2[i];
		arr_second[len_result + 1] = '\0';

		for (int k = 0; k <= len2 - len1; k++)
			arr_first[k] = '0';
		for (int j = 0; j < len1; j++)
			arr_first[len2 - len1 + 1 + j] = arr1[j];
		arr_first[len_result + 1] = '\0';
	}
	sum(arr_first, arr_second, len_result);
	return 0;
}
void sum(char* arr_first, char* arr_second, int len) {
	int carry = 0;
	char* result = (char*)malloc(sizeof(char)*(len + 1));

	for (int u = len; u >= 0; u--) {
		int sum = 0;
		sum = (arr_first[u] - '0') + (arr_second[u] - '0') + carry;

		if (sum > 9) {
			carry = 1;
			sum -= 10;
		}
		else
			carry = 0;

		result[u] = sum + 48;
	}

	if (result[0] != '0') {
		result[len + 1] = '\0';
		printf("The sum of these number is: %s\n", result);
	}
	if (result[0] == '0') {
		char* result_new = (char*)malloc(sizeof(char)*(len + 1));
		for (int t = 0; t <= len; t++) {
			result_new[t] = result[t + 1];
		}
		result_new[len] = '\0';
		printf("The sum of these number is: %s\n", result_new);
		free(result_new);
	}
	free(result);
}

큰 수의 덧셈. 처음엔 아, 이거 그냥 덧셈만 출력하면 되는거 아냐?

이렇게 생각을 했는데... 다음이 문제다.

999 + 1 = 1000

자릿수를 넘어가버리는 것이다!

근데 배열을 999에 맞춰서 하면 1이 출력이 되지 않으므로

덧셈의 결과가 000으로 출력되는 일이 발생한다.

 

그래서 나는 입력된 두 수 중 더 긴 숫자의 배열크기보다 1 더 큰 배열을 선언하는 방법을 선택했다.

이렇게 하면 자릿수가 넘어가더라도, 출력을 할 수 있게 된다!

만약 자릿수가 넘어가지 않는다면, 배열에 저장된 수를 한칸씩 앞당긴 후 출력하도록 했다.

 

여기서는 sum 함수 부분이 중요하다고 생각한다.

일단 덧셈의 경우, 문자열로 입력을 받았기 때문에 '0'를 빼주어

우리가 생각하는 숫자로 덧셈이 진행되게 하였다.

carry를 선언하여 이 carry가 덧셈의 결과가 10을 넘어가 자릿수를 넘어갈 경우

그 다음 자리에서 올린게 더해질 수 있도록 하였다.

 

의외로 애를 많이 먹었던 건 출력부분이었는데,

Visual Studio로 코드를 작성하여 실행했을 때는 정상적으로 작동하였으나

과제제출 사이트에서 테스트 케이스를 실행하니

10개의 케이스 모두 Segmentation Error가 뜨는 것이었다!

 

구글링을 통해, 보통 동적할당에서 메모리 관련 문제가 발생할 경우 이런 문제가 뜬다는 것을 알았다.

어... 근데 난 buffer, stack 이런게 뭔지 잘 모르는데?

그래서 열심히 free를 위치를 한줄씩 옮겨가며 일일히 다 실행해보았고,

바로 지금 위치에서 아무런 에러없이 잘 작동되는 것을 알게되었다.

물론 이번엔 잘 몰라서 이렇게 한 거였지만,

앞으로 메모리 부분에 대해서도 많은 공부를 한 후

그 땐 제대로! 문제를 파악하고 해결 방법을 찾을 것이다.

 

얘도 10점 만점 중 8점.

뭐가 틀렸는지는 아직도 잘 모른다.


3.  Text encryption and decryption

 

이건 진짜 환장할 문제였다.

암호화는 의외로 쉬웠는데,

복호화가 정말... 욕 나올 정도로 너무 힘들었다.

 

오죽하면 동기들 만나면

"너 3번 풀었어?"

묻는게 일상이 되었을정도로ㅋㅋㅋ

 

나도 하루를 꼬박 생각하다가 잘 모르겠어서

고등학교 친구들한테

'이건 코딩 문제가 아니니까, 그냥 같이 고민해줘.'

이러면서 물어보고 그랬다.

역시 답을 준 친구는 없었지만, 그래도 같이 고민해줘서 큰 힘이 되었다.

고맙고, 사랑해 친구들아!

 

그래도 내가 누군가. 한다면 하는 사람아닌가.

결국 의지로 풀었다. 의지로.

 

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

#define FLUSH while(getchar() != '\n')

void encrypt(char *PT);
void decrypt(char *ET);

int main() {
	int carry = 0;
	char num[100];
	char input[100];

	scanf("%s", num);
	FLUSH;

	if ((num[0] == '1' && num[1] == '\0') || (num[0] == '2' && num[1] == '\0') || (num[0] == '3' && num[1] == '\0')) {
		carry = num[0] - '0';
	}
	else {
		printf("Invalid input. Try again.\n");
		return 0;
	}

	switch (carry) {
	case 1:
		scanf("%[^\n]s", input);
		if (strlen(input) > 25) {
			printf("Invalid text. Try again.\n");
			return 0;
		}
		encrypt(input);
		break;
	case 2:
		scanf("%[^\n]s", input);
		if (strlen(input) > 25) {
			printf("Invalid text. Try again.\n");
			return 0;
		}
		decrypt(input);
		break;
	case 3:
		printf("Program terminating. Bye!\n");
	}
	return 0;
}
void encrypt(char *PT) {
	char arr[5][25] = { 0 };
	int len = strlen(PT);

	for (int i = 0, T = 0, count = 0; i < len; i++) {
		if (count == 0) {
			arr[T][i] = PT[i];
			T++;
			if (T == 4)
				count = 1;
			continue;
		}
		if (count == 1) {
			arr[T][i] = PT[i];
			T--;
			if (T == 0)
				count = 0;
			continue;
		}
	}
	printf("Encrypted text: ");
	for (int j = 0; j < 5; j++) {
		for (int m = 0; m < 25; m++) {
			if (arr[j][m] > 0)
				printf("%c", arr[j][m]);
		}
	}
	printf("\n");
}
void decrypt(char *ET) {
	char result[50];

	int l1 = strlen(ET);
	int first = l1 / 8;
	int remainder = l1 % 8;
	int l2 = first;

	if (remainder >= 1) {
		for (int a1 = 0; a1 < first + 1; a1++) {
			result[0 + a1 * 8] = ET[a1];
		}
		l2++;
	}
	else {
		for (int a1 = 0; a1 < first; a1++) {
			result[0 + a1 * 8] = ET[a1];
		}
	}

	if (remainder == 0 || remainder == 1) {
		for (int a2 = 0; a2 < first; a2++) {
			result[4 + 8 * a2 - 3] = ET[l2];
			result[4 + 8 * a2 + 3] = ET[l2 + 1];
			l2 += 2;
		}
	}
	else {
		int a2 = 0;
		for (; a2 < first; a2++) {
			result[4 + 8 * a2 - 3] = ET[l2];
			result[4 + 8 * a2 + 3] = ET[l2 + 1];
			l2 += 2;
		}
		result[4 + 8 * a2 - 3] = ET[l2];
		l2++;
	}

	if (remainder >= 3 && remainder < 7) {
		for (int a3 = 0; a3 < 2 * first + 1; a3++) {
			result[2 + 4 * a3] = ET[l2];
			l2++;
		}
	}
	else if (remainder == 7) {
		for (int a3 = 0; a3 < (first + 1) * 2; a3++) {
			result[2 + 4 * a3] = ET[l2];
			l2++;
		}
	}
	else if (remainder < 3) {
		for (int a3 = 0; a3 < first * 2; a3++) {
			result[2 + 4 * a3] = ET[l2];
			l2++;
		}
	}

	if (remainder == 4 || remainder == 5) {
		int a4 = 0;
		for (; a4 < first; a4++) {
			result[4 + 8 * a4 - 1] = ET[l2];
			result[4 + 8 * a4 + 1] = ET[l2 + 1];
			l2 += 2;
		}
		result[4 + 8 * a4 - 1] = ET[l2];
		l2++;
	}
	else if (remainder < 4) {
		for (int a4 = 0; a4 < first; a4++) {
			result[4 + 8 * a4 - 1] = ET[l2];
			result[4 + 8 * a4 + 1] = ET[l2 + 1];
			l2 += 2;
		}
	}
	else if (remainder > 5) {
		for (int a4 = 0; a4 < 1 + first; a4++) {
			result[4 + 8 * a4 - 1] = ET[l2];
			result[4 + 8 * a4 + 1] = ET[l2 + 1];
			l2 += 2;
		}
	}

	if (remainder >= 5) {
		for (int a5 = 0; a5 < first + 1; a5++) {
			result[4 + a5 * 8] = ET[l2];
			l2++;
		}
	}
	else {
		for (int a5 = 0; a5 < first; a5++) {
			result[4 + a5 * 8] = ET[l2];
			l2++;
		}
	}
	result[l2] = '\0';
	printf("Plain text: %s\n", result);
}

암호화 과정은 쉬웠다.

지그재그로 배열한 후 순서대로 출력하면 되는 것이었기 때문이다.

 

근데 하... 저 decrypt 함수 길이를 보면 정말...

아직도 이것만 보면 이런 문제를 내신 교수님이 원망스럽다.

 

decrypt를 어떻게 구현한 것이냐면,

암호화 과정을 쭉 써놓고 관찰을 해보니

8을 단위로 규칙이 반복되는 것이 보였다.

 

그래서 8로 나누었을 때의 나머지에 위치하는 배열에 복호화하려는 문자를 넣고,

이걸 순서대로 읽는 과정을 선택했다.

 

이건 내가 소중하게 여겼던 종이가 없으면

설명하기 좀 어려울 것 같은데, 아무튼 어떻게 풀긴 했다!

 

그리고, 의지로 푼 만큼 10점 만점 중에 10점이 나왔다.

 

그런데, 솔직히 내가 한 방법보다 더 좋은 방법이 있을 것 같다.

나는 거의 노가다 방식으로 풀은 것이나 다름없기 때문에,

좀 더 깔끔하게 푼 것을 보고 싶다.

 

그러니까 조교님, 예시 답안 좀 올려주세요!

'Develop > C' 카테고리의 다른 글

1. 나의 첫번째 C언어 과제  (0) 2019.05.26
Comments