본문 바로가기
studylog

231020 TIL

by Nam Kyeongmin 2023. 10. 20.

오늘은 튜터링 모임시간에 시험대비 문제를 풀어보고 오답을 했다.
그걸 바탕으로 오늘의 TIL를 작성해보려 한다.
 

중간고사 대비 문제지.pdf
0.08MB

기본적인 문제는 위 파일을 참고하면 된다.

문제를 오답하면서 메모하듯 기억해야 할 부분을 정리했지만, 다시 TIL을 통해 찬찬히 되짚어보려 한다.
.
.
.

문제 1-1. printf 안에 n>5, 0&&1, 0 || 1이 들어가 있을 때,

보통 출력문은 printf("%d\n", number); 이기 마련인데, 문제 1에서는 그 형태가 조금 달랐다.

 printf("%d\n", number>5);
 printf("%d\n", 0&&1);
 printf("%d\n", 0 || 1);

이었던 것. 즉 콤마 다음에 들어온 수식의 참 거짓을 판별하고 참이면 1, 거짓이면 0을 출력하는 것이었다. 기억하기!
위 출력문대로라면 number=7일때, 1 0(참과 거짓을 동시에 충족할 수 없기 때문에) 1(참 아니면 거짓이기 때문에) 출력.
 

문제 1-2. 전역변수와 지역변수

 보통 main 함수에서 변수를 선언하고 main 함수 밖에서 새로운 함수를 정의해 Call by Value 형태로 값을 불러 연산 및 조작한다면, main 함수에서 변수를 출력할 때 연산 및 조작과정이 반영되지 않은, 그 이전의 값으로 출력된다. 이러한 경우에 연산 및 조작과정을 반영하여 출력하고 싶다면 Call by reference를 사용하는 것인데, 전역변수의 경우는 다르다.
 앞서 말한 경우는 main 함수에서 변수를 선언, 즉 지역변수를 선언한 것이고, main 함수 밖에서 변수를 선언하면 이는 곧 전역변수가 된다. 전역변수의 경우 Call by reference를 하지 않아도 main 함수 외에 다른 함수에서 연산 및 조작과정을 거친 것이 변수에 반영되기 때문에 출력했을 때도 반영된 값이 출력된다.
 

문제 1-3. 구조체를 Call by reference로 받았을 때

구조체를 Call by reference로 받게 되는 경우로 아래와 같은 예시를 들 수 있다.

void change(struct Brith *a, struct Birth *b) {
   b->year=2023;
   a->year=b->year;
   (*b).year=2024;

 이런 식으로 작성할 때, b->member 이나 (*b).member 이런 식으로 접근하여 값을 할당하면, 그 값이 그대로 변수에 입력되어 main 함수에서 출력할 때 반영된 상태로 출력된다. 구조체 변수명 그 자체가 reference로 사용될 수 있으니까!
대신 b.member으로 접근할 때는, Call by value와 마찬가지로 main 함수에서 반영되지 않는다는 점. 주의하자
 

문제 4-(a). array로 선언된 구조체의 member에 접근할 때,
struct PERSON {
   char name[10];
   int age;
   float salary;
}

 위와 같은 구조체가 선언되고 struct PERSON person[3];으로 array가 선언될 때, scanf를 통해 구조체 안 멤버들의 값을 초기화하게 된다면, .member를 통해 접근하여 값을 초기화할 수 있다. 이때 그 명령문을 scanf("%s %d %f", person[i].name, &person[i].age, &person[i].salary); 로 작성하는데, 주의할 것은 name은 문자열 배열 변수 형태로 이름 그 자체가 주소로 사용될 수 있어 & 기호가 필요없지만, int와 float 형태의 age, salary는 주소를 연결해야 하기 때문에 & 기호를 작성해야 한다.
 

문제 4-(b). Call by reference로 swap 함수 쓰기

흔히 swapping의 방법으로는 아래와 같은 수식을 떠올릴 수 있다.

int a,b,temp;
temp=a;
a=b;
b=temp;

 이 방법을 활용해 Call by value 대신 Call by reference를 사용하고, int형의 변수들 대신에 struct 포인터를 이용할 때 코드를 살펴보자.

void swap(struct PERSON *a, struct PERSON *b) {
struct PERSON temp;
temp=*a;
*a=*b;
*b=temp;

 여기서는 구조체 멤버 중 age 값을 비교하여 오름차순으로 정열할 때 call되어 사용되기 때문에 구조체 배열의 전체 멤버를 swap 해야 한다. 따라서 구조체 포인터 변수 a와 b를 저장할 temp 변수도 구조체 변수 형태로 선언되어야 한다.
 

문제 4-(c). bubble sorting - 중첩 for문의 조건

앞서 언급한 swap을 call할 sorting 함수도 만들어야하는데, swap을 call할 조건을 생각하고 코드를 작성해야 한다.

void sorting(struct PERSON* person) {
   for (int i = 0; i < NUM; i++) {
      for (int j = i + 1; j < NUM; j++) {
         if (person[i].age > person[j].age) {
            swap(&person[i], &person[j]); }
         }
      }
   }

특히 중첩 for문을 사용할 때, for문의 조건을 잘 생각하고 작성해야 한다.

 그림에서 처럼, 그려진 박스의 개수가 array의 인덱스 수라 가정했을 때, 비교되어야 하는 경우의 수는 그림과 같다. 이러한 개념을 기억하고 for문의 조건을 잘 작성하여 bubble sorting 할 수 있도록 하자.
 

#define _CRT_SECURE_NO_WARNINGS
#define NUM 3
#include <stdio.h>

struct PERSON {
	char name[10];
	int age;
	float salary;
};
void init(struct PERSON* person) {
	for (int i = 0; i < NUM; i++) {
		scanf("%s %d %f", person[i].name, &person[i].age, &person[i].salary);
	}
}
void swap(struct PERSON* a, struct PERSON* b) {
	struct PERSON temp;
	temp = *a;
	*a = *b;
	*b = temp;
}
void sorting(struct PERSON* person){
	for (int i = 0; i < NUM; i++) {
		for (int j = i + 1; j < NUM; j++) {
			if (person[i].age > person[j].age) {
				swap(&person[i], &person[j]);
			}
		}
	}
}
void print(struct PERSON* person) {
	printf("\nPrinting the result\n");
	for (int i = 0; i < NUM; i++) {
		printf("%s %d %f\n",person[i].name, person[i].age, person[i].salary);
	}
}
void main() {
	struct PERSON person[NUM];
	init(person);
	sorting(person);
	print(person);
}

=> 문제 4의 코드를 종합적으로 작성한 코드다. 헷갈린 부분들을 다시 한번 되짚어보고 헷갈리지 않도록 익혀두자.
 

문제 5. Call by reference를 이용한 int 변수값 연산하기

이 부분은 먼저 코드를 작성하고, 헷갈렸던 부분을 다시 살펴보도록 하자.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

void calculate(int *N_1, int *N_2, int *r_1, int *r_2, int *r_3, int *r_4) {
   *r_1=(*N_1)*(*N_2);
   *r_2=(*N_1)/(*N_2);
   *r_3=(*N_1)%(*N_2);
   *r_4=(*N_1)+(*N_2);
   // 메모리에 접근해서 값을 가져와 연산해야 하기때문에, 포인터 변수로 연산식 작성
}
int main() {
   int *pn1, *pn2;
   int N1, N2, r1, r2, r3, r4;
   scanf("%d %d", &N1, &N2);
   *pn1=N1;
   *pn2=N2;
   calculate(&pn1, &pn2, &r1, &r2, &r3, &r4);
   printf("%d %d %d %d", r1, r2, r3, r4);
   return 0;
}

 먼저 연산한 결과값을 저장할 변수를 main에서 선언하고, 함수에 입력할 때 주소값으로 입력한다. 왜냐하면 Call by reference를 사용할 거니까! 그리고 함수 안에서 입력된 값(포인터 변수)을 사용할 때, 주석처럼 메모리에 접근해서 연산에 필요한 값을 가져와 연산과정을 거치기 때문에, 포인터 변수 그대로 작성한다.
 

문제 6. 또 나왔다 exact match

 또한, 마지막 문제는 exact match로 저번 TIL에서도 다룬 내용이라, 간략하게 내가 작성한 코드를 공유하며 오늘의 TIL을 마무리하려 한다. 이번 exact match는 특히 wildcard를 search string으로 받는 경우를 제외했기 때문에 코드가 간결했다. 그래도 wildcard를 입력받을 때, 필요한 코드식도 다시 봐둬야겠다.

// for와 if를 사용하는 main code를 작성하겠다
for (int i=0; i<text_length; i++) {
   if (text[i] == sub[0]) {
   int sum=0;
      for (int j=0; j<sub_length; j++) {
         if (text[i+j] != sub[j]) {
            sum++;
            break;
         }
      if (sum != 0) {
         continue;
      }
      else {
         count++;
      }
   }
   return count;
}

 text string과 sub string의 일치/불일치를 확인할 수 있는 sum의 0 초기화를 잊지 말아야 하고, j 변수를 이용한 for문을 돌릴 때 text와 sub을 비교할 때, text와 sub의 인덱스 모두 j를 포함하여, 인덱스를 각각 증가시켜 비교할 수 있도록 작성하는 것에 주의를 기울이자.
 
(+) 추가학습
<for - if 구조문에서>
break;
-> for문을 탈출하여, 그다음 명령문으로 넘어간다.
continue;
-> 그 이후의 명령은 모두 거치지 않고, 바로 for문(i++한 상태)으로 넘어가서 for문을 진행한다.
=> 급 이 개념들이 헷갈려서 유튜브 영상을 찾아보고 정리했다. 앞으로도 헷갈리는 개념이 있으면 묵혀두지 말고 유튜브 강의나 정보를 찾아 헷갈리는 부분을 없애도록 노력하겠다!!
 

'studylog' 카테고리의 다른 글

~240502 백준 review - [입출력과 사칙연산]  (0) 2024.05.02
240419 백준1008번 A/B  (0) 2024.04.19
231018 TIL  (1) 2023.10.18
231017 TIL  (0) 2023.10.18
231015~16 TIL  (2) 2023.10.16