F.R.I.D.A.Y.

[TIPS 19TH] 03 : 2018.07.02. (월) 본문

외부활동/TIPS 19th

[TIPS 19TH] 03 : 2018.07.02. (월)

F.R.I.D.A.Y. 2018. 7. 3. 09:22
반응형

2018/06/29 - [외부활동/TIPS 19th] - [TIPS 19TH] 2018.06.28. (목) 이전에 이어서.


printf에서 실수형을 출력할 때, %f %lf를 주로 사용하는데, %lf의 문제점은 불필요한 자릿수까지 출력해주는 것이다. 이를 보완하기 위해 %g를 사용해 실수를 출력한다.


printf("%lf, %f", 3.14, 2.71f);
// 출력 : 3.140000, 2.710000

printf("%g, %g", 3.14, 2.71f);
// 출력 : 3.14, 2.71


이런 식으로 출력되기 때문에 요새는 %f, %lf보다 %g를 더 많이 사용하는 추세라고.

다만, 불필요한 자릿수를 출력하지 않도록 연산을 하는 과정을 거치기 때문에,  출력시엔 %f, %lf보다 속도가 상대적으로 떨어진다고 한다.


printf("%c %d", 65, 65);
// 출력 : A 65


똑같이 65를 출력하지만, printf는 값보다 어떻게 출력한것인지에 더 집중하기 때문에 전자는 char 형식의 'A'로, 후자는 int의 숫자 65를 출력한다.



Q. "65를 출력할 때 서식지정자를 달리 해서 출력해도 상관없다면 [ 3.14 ]를 %c나 %d 등으로 출력해도 되지 않을까?"


안된다. [ %c ], [ %d ] 등은 어떤 방식으로 출력하는지가 차이가 있지만, 기본 출력 대상은 정수라는 공통점이 있다. 그러나 [ 3.14 ]의 경우 넓게 보면 [ 65 ]와 같이 숫자이지만 조금만 아래로 내려가면 실수 VS 정수라는 큰 차이가 존재한다.

즉 실수계는 실수계끼리, 정수계는 정수계끼리의 호환은 가능하지만 실수계-정수계끼리의 혼합은 불가능한것이다.


printf("[%5d]", 5);   // 출력1 : [    5]
printf("[%05d]", 7);  // 출력2 : [00007]
printf("[%-5d]", 4);  // 출력3 : [4    ]


차례대로 오른쪽 정렬, 빈공간 채움, 왼쪽 정렬을 하는 코드이다. 단, %-05d와 같이 ( - ) 뒤에 0은 존재할 수 없다[각주:1].

참고로 대괄호는 어떻게 나타난다를 보여주기 위해 작성한 것일 뿐, printf의 사용방법이 아니다.

정렬과 관련된 자세한 사항은 다음을 참고할 것.


참고

>> printf에서 출력상 오류가 발생하면 [ 0xCXXXXXXX[각주:2] ] 정도의 값이 나온다.

>> [ %d ] 에서 [ % ]와 [ d ] 사이에 [ * ]을 작성하면, 즉 [ %*d ]와 같은 형식으로 작성하게 되면 몇 칸으로 정렬할것인지 사용자에게 직접 받을 수 있다.



ex)


int space = 5;

printf("%*d\n", space, 5);



1. 제어코드

운영체제에서 제공하는 것으로, printf의 [ % ]로 시작하는 서식지정자와 함께 자주 사용되어서 printf 사용방법으로 오해하는 경우가 많은데, printf가 제공하는 것이 아니라 운영체제 자체에서 제공하는 코드이다. ASCII에 지정되어있다.

 제어코드

 의미

 ASCII

\n

라인 피드(다음줄의 현재 위치로 이동)

13

\r

캐리지 리턴(현재 줄의 처음으로 이동)

10

\t

탭키

9

\b

백스페이스(콘솔에서는 캐럿[각주:3]만 한칸 뒤로)

8

\a

경고음

7

\"

쌍따옴표( " )

34

\'

따옴표( ' )

39

 \\

\

92


이스케이프 시퀀스라고도 부른다는데, 더 많이 존재하며 자세한 사항은 다음 참고.


원래대로라면 [ \n - \r ][각주:4]을 사용해야 다음줄의 처음으로 넘어가겠지만, printf에서는 [ \n ]만 사용하는것으로 다음줄 개행을 지원하게 되었다. 하지만, 이게 여러개의 운영체제를 사용하게 되면서 문제가 발생한다.

Windows계열은 [ \n - \r ]을 사용함으로써 줄개행이 되는 반면, Linux 계열의 일부는 [ \r - \n ] 형식이, 또 어떤 운영체제는 [ \r ]만 사용하면 줄개행이 돼버려서 여러 OS에서 돌아가는 프로그램을 만들 때에는 이것을 주의 해야한다.



2. 연산자

기본 연산자(이항 연산자)

 프로그래밍에서는 기본적으로 수학에서 사용하는 사칙연산을 포함한 몇가지 연산자를 제공한다.

연산자

의미

예시 [식 - 값]

+

더하기

1 + 2

3

-

빼기

1 - 2

-1

*

곱하기

2 * 2

4

/

나누기

4 / 2

2

%[각주:5]

나머지 구함

5 % 2

1


 여기서 예의주시해야할 것은 나머지를 구하는 [ % ] 연산자인데, 기본적인 사칙연산과 별개로 모듈러 연산자를 이용함으로써 얻을 수 있는 이익이 굉장히 많으므로 사용 예시를 찾아보는 것을 추천한다.



단항 연산자

 항이 하나라서 단항 연산자로 불린다.

연산자

의미

예시 [초기화 - 식]

[ i ]값

 [ n ]값

i++

[ i ][각주:6]값에 1을 더함

i = 4;

n = i++;

5

4

++i

n = ++i;

5

5

i--

[ i ]값에서 1을 뺌

i = 7;

n = i--;

6

7

--i

n = --i;

6

6


 같은 단항 연산자인데, 둘로 나눈 이유는 연산자의 위치에 따라 값이 달라지기 때문이다. 변수명 앞에 연산자를 작성하면 해당 변수에 연산이 진행된 후에 값을 대입하고, 변수명 뒤에 연산자를 작성하면 값을 대입한 후에 해당 변수값이 증감된다. 일반적으로 변수명 앞에 단항 연산자를 작성하면 해당 방식을 전위형[각주:7], 변수명 뒤에 단항 연산자를 작성하면 후위형[각주:8]으로 불린다.

 단항 연산자의 계산 값은 컴파일러의 종류에 따라서 달라지며, 같은 컴파일러더라도 버전에 따라 계산 방식(순서)에 차이를 보여서 다른 값을 보일 수 있기 때문에, 단순 계산에만 사용해야 코드의 의미를 명확히할 수 있다.

 함수의 매개변수에서 단항 연산자의 사용은 앞서 설명한(볼드체) 이유 등으로 오류를 초래할 수 있기 때문에 웬만하면 단항 연산자와 같은 작업을 하는 이항 연산자[각주:9]를 활용해 고치는 것이 좋다.


참고

>> 대입의 경우가 아니라면 전위형이나 후위형 연산자는 같은 작업을 하므로 연산 순서에 따른 고민을 할 필요는 없다.

>> 이항 연산자보다 단항 연산자가 속도가 더 빠르다. 그러나 요즘 컴파일어는 똑똑해서 [ i = i + 1 ]정도는  단항 연산자처럼 번역한다고..


관계 연산자

 양측의 값에 대해 비교를 시행하는 연산자로 보면 된다.

 연산자

 의미

 A > B

 A가 B보다 크다

 A < B

 A가 B보다 작다

 A >= B

 A가 B와 같거나 크다

 A <= B

 A가 B와 같거나 작다

 A == B

 A와 B가 같다

 A != B

 A와 B가 같지 않다


 연산자 의미대로 되면 참으로 1을 반환하고 거짓이면 0을 반환[각주:10]한다.

 일반적으로 조건식과 연계하여 자주 사용하지만, 굳이 조건식과 함께 사용하지 않더라도 양측의 관계를 파악해 값을 반환해주기 때문에 잘 사용하면 조건문을 대체할 수 있다.


int a = 2;

if(a > 3) a = 0;         // 일반적인 상황에서의 사용
//equal
a = a * ((a > 3) == 0);  // 관계 연산자를 통해 조건문은 If를 대체함. 퍼포먼스가 더 좋다.


논리 연산자

 양측, 혹은 단일 항의 값을 판단해 참과 거짓을 반환하는 연산자이다.


 연산자

의미 

A && B

  AND : A, B의 값 모두 참일경우에만 1을 반환

A ||[각주:11] B

  OR : A, B의 값 중 하나라도 참일경우 1을 반환

!A

  NOT : A의 값을 반전[각주:12]



 참고할 것은 이 논리 연산자도 관계 연산자처럼 조건문을 대체할 수 있다.

이유는 논리 결과표에 나온 것처럼 이전 조건을 판단 했을 때 확실하게 결과를 판단할 수 있다면 B의 조건은 판단하지 않기 때문이다.


int data = 5;
data > 3 && data++;

data > 3 && (data = 8);


 이 코드를 분석하면, [ data ]변수의 값이 3 초과라면 뒤의 것도 판단을 해야 참/거짓을 판별할 수 있겠지만, 3 이하라면 [ data > 3 ]이라는 조건부터 거짓으로 뒷 조건을 판단하지 않더라도 거짓임이 분명하기 때문에 C언어에서는 거짓에서는 data++을 하지 않고 넘어간다. 다만, 대입연산은 할 수 없었다. 대입연산의 경우에는 세번째 줄처럼 대입할 변수와 대입값을 괄호로 묶어주어야한다. 

 값

 조건

결과 

A : int data = 5; 

 data > 3 && data++

[ data ] 변수에는 6이라는 값이 저장됨 

B : int data = 3; 

[ data ] 변수에는 3이라는 값이 저장됨 


왼쪽은 [ && ]뒤를 수행, 오른쪽은 [ && ] 앞까지만



3. 연산 순서

 컴퓨터는 명령을 명확하게 내려주지 않으면 오류를 발생시킨다. 연산 순서에 따라 값이 달라질 수 있기 때문이다. 따라서 컴퓨터는 정확한 연산순서를 요구한다. [ 연산순서 확인 ]


 컴퓨터에서도 사칙연산은 일반 수학에서의 사칙연산 순서와 동일하게 작동한다. 프로그래밍에서는 이러한 사칙연산과 더불어 다양한 연산자를 지원하는데 일반적으로 연산순서를 일부러 외우려고 들지는 않는다. 대부분 예상대로 작동하거니와, 프로그래밍을 공부하면서 자연스레 외워지기 때문인데, 가끔 연산 순서가 헷갈리는 연산자들이 있다. 이럴 때에는 찾아보더라도 연산 순서에 헷갈리는 경우가 있는데 이럴 때에는 주저하지 말고 괄호를 사용하면 된다. 아무리 연산 순서가 높다고 하더라도 괄호를 치면 괄호 내의 연산자들끼리만 우선순위를 겨뤄 연산을 한 후 괄호 밖과 연산을 한다.

 다만, 자신도 읽는데 시간이 걸리는 정도로 연산 수식을 작성하는것은 옳지 않다[각주:13]. 되도록 짧게 짧게 끊어서 수식을 작성하는 것이 좋다.

 결론은

연산 순서가 헷갈리면 괄호를 쓰자



4. 조건문[각주:14] - If

 주로 관계 연산자와 논리연산자와 함께 사용하는데 해당 식이 참인지 거짓인지 판별하여 프로그램을 분기할 때 사용한다.

 기본적인 구조는 다음과 같다.


if(조건문){
    명령문1;
}else{
    명령문2;
}

 조건문의 조건이 참일경우 [ 명령문1 ]을, 거짓이라면 [ 명령문2 ]를 수행한다.


 또한, if의 여러 조건을 만족하더라도 위에서부터 가장 먼저 만족하는 조건의 명령만 수행하고 분기가 끝난다.


int i = 5;

if( i > 1) i = 8;      // 조건 1
else if(i > 2) i = 10; // 조건 2
else if(i > 4) i = 14; // 조건 3
else if(i > 6) i = 16; // 조건 4
else i = -1;           // 조건 5


[ i ]는 5이므로 [ 조건 1, 조건 2, 조건 3 ]을 만족하지만 [ 조건 1 ]이 가장 위에서 만나는 만족하는 조건이므로 [ i ]는 8이 되고 분기가 끝나게 된다. 따라서 [ ~보다 크다 ]라는 조건을 검색하려면 큰 수부터 검색하고 [ ~보다 작다 ]라는 조건을 검색하려면 작은 수부터 검색해야한다.


  If문은 조건이 맞을 때까지 계속 조건을 비교하기 때문에 속도가 상당히 떨어진다. 따라서 퍼포먼스가 필요한 프로그램을 작성한다면 If문의 사용은 지양해야한다.


참고

>> C언어의 If, else if, else에서 else if는 if와 else가 결합된 구조라서 하나의 문법이 아니다. 주의할 것.

>> 두마리 토끼[각주:15]를 모두 잡기엔 실력이 부족할 수 있다. 둘 중 우선순위를 두어서 버릴것은 버리고 실익을 챙기자.



5. 조건문 - Switch

 If와 비슷하지만 다른 조건문이다.


int n = 5;

switch (n[각주:16]) {
case 1:

	break;
case 2:
		
	break;
case 3:

	break;
case 4:

	break;
default:

	break;
}

기본 구조는 이렇게 된다. 가장 큰 특징이자 단점은 상수로만 비교가 된다는 것이다.


 If문과 비교하면


int n = 5;

if (n == 1) ;
else if (n == 2);
else if (n == 3);
...
else;


이런 구조로 변경할 수 있다.



6. 반복문

for(시작 조건; 종결 조건; 조건 변화 수식)

 가장 일반적인 형태의 반복문으로 일반적으로 반복 횟수가 정해졌을 때 사용한다.

 시작 조건과 조건 변화 수식에는 다음과 같이 여러 명령을  ' , ' 를 통해 구분해 적용할 수 있다.


int i, n;
for(i = 5, n = 3; ....; i++, n++)

종결 조건에서도 충분히 ' , '를 사용해 여러개를 만들 수 있지만 이는 잘못된 형태로, 원래 계획했던 것과 다르게 작동한다.

다음은 [ n 이 5 미만이면서 i 가 7 이하일 때 ]까지 작동하는 반복문이다.

 

#include <stdio.h>

int main(void) {
	
	
	int i, n;
	for (i = 0, n = 3; n < 5 , i <= 7; i++, n++) {
		printf("i = %d\tn = %d\n", i, n);

	}

	return 0;
}


 


비정상 작동


#include <stdio.h>

int main(void) {
	
	
	int i, n;
	for (i = 0, n = 3; n < 5 && i <= 7; i++, n++) {
		printf("i = %d\tn = %d\n", i, n);

	}

	return 0;
}


 


정상 작동


종결 조건에 ' , '를 사용하면 가장 마지막에 있는 종결조건이 실제 조건으로 작용하게 된다. [ n < 5 ]가 종결의 판단 근거가 아니라 [ i <= 7 ]이 종결의 판단의 근거가 되는 것이다.


상황에 따라서는 for( ) 안에 아무 조건을 넣지않은 for( ; ; ) 형식으로 이용할 수 있다. 이렇게 사용하면 무한 반복이 된다.


while(종결 조건)

while이라는 반복문도 있다. 이전 for에서 시작 조건과 조건 변화수식을 다른 곳으로 옮긴 식으로, for와 문법이 살짝 다를 뿐이지 어느 것을 사용하더라도 큰 문제는 없다. 취향의 문제로 받아들이면 되겠다.


do-while(종결 조건);

while과 같은 방식으로 작동하지만, 한가지 차이는 do, 일단 한번은 꼭 실행해본다는 것이다. while은 처음 진입점에서 참/거짓을 판단하지만, do-while은 실행해보고 참/거짓을 판단해 지속할지를 결정한다. 일반적으로 거의 사용하지 않는다.


while(종결 조건){

}

do{

}while(종결 조건);


break;

반복문의 종결 조건을 무시하고 무조건 반복을 중단하고 다음 시행으로 넘어간다.


int n = 1;

while(n <= 7){
    if(n == 4) break;
    i++;
}

printf("%d\n", n);


[ i ]값이 4가 되면 break에 의해 [ 5, 6, 7 ]을 시행하지 않고 그대로 종료한다. 실제로 break를 사용하지 않았다면 8이 출력돼야 한다.


continue;

break문과 비슷하지만, 다른점은 반복문을 1회성으로 취소한다. 즉, continue를 사용하면 사용한 시점에서 continue 아래의 코드는 시행하지 않고 조건 변화 수식을 적용하고 종료 조건을 판단한 후, 해당 반복을 반복한다.



[ i ]가 4인 경우, 출력되지 않고 조건 변화 수식을 적용한 후, 다시 반복문을 시행한다.


이중 반복문 나가기

break; 는 해당 break가 존재해는 가장 안쪽의 반복문을 나간다. 그러나 실제 프로그래밍을 하면 단일 반복문 뿐만 아니라 N중 반복문을 사용하기도 한다. 이 때, N중 반복문을 나가기 위해서는  break; 하나만으로는 해결할 수 없다.

1. 내부 반복문의 조건을 그대로 밖의 반복문에 집어넣기


int i, k;

for (i = 0; i < 7; i++) {
	for (k = 0; k < 3; k++) {
		if (k == 2 && i == 4) break;
		printf("i = %d\t k = %d\n", i, k);

	}
	if (k == 2 && i == 4) break;
}


2. 변수 하나를 생성해 해당 변수를 밖의 반복문에서 판단후 나가기


int i, k, flag = 0;

for (i = 0; i < 7; i++) {
	for (k = 0; k < 3; k++) {
		if (k == 2 && i == 4) {
			flag = 1;
			break;
		}
		printf("i = %d\t k = %d\n", i, k);

	}
	if (flag) break;
}


3. 외부 반복문의 종결조건을 만족시키도록 변수값을 변경


int i, k;

for (i = 0; i < 7; i++) {
	for (k = 0; k < 3; k++) {
		if (k == 2 && i == 4) {
			i = 6;            // 외부 for의 종결 조건이 i가 7 미만인경우이므로 i를 6으로 맞추면 알아서 나간다.
			break;
		}
		printf("i = %d\t k = %d\n", i, k);

	}
}



7. 오류

인텔리센스 오류

 정상인데 오류가 난다 싶으면 인텔리센스를 다시 빌드한다. 모르겠으면 다른 사람에게 물어보는 것도 한 방법. VS 2017의 인텔리센스가 완벽한 것이 아니라서 코드를 작성하다보면 가끔씩 오류가 난다.


신텍스 오류(구문 오류)

 사용중인 프로그래밍 언어의 문법대로 코드를 작성하지 않았을때 발생한다. 문법상 잘못된 부분이 없는지 확인이 필요하다. Visual Studio 2017 기준으로 [ Ctrl + \ ] [ Ctrl + E ]를 차례로 누르면 에러 목록이 나오므로 에러 목록을 확인하는 것도 도움이 된다.


시맨틱 오류(의미상 오류)

 어려워서 버그라는 단어로 순화되었다. 시맨틱 오류가 발생하면 일반적으로 "버그가 발생했다"라고 한다. 문법상 오류가 있는 신텍스 오류와는 달리 문법상의 오류는 없으나 실제 예상한 작업을 정상적으로 수행하지 못했을 때 시맨틱 오류라고 말한다.

 예로 [ i ] 가 3일 때 "안녕하세요"를 출력하는 작업을 해야하는데, [ i ] 가 4일때 해당 작업이 시행된다는 등의 문제가 이에 해당한다.

 일반적으로 비교 연산[각주:17]에서 대입 연산[각주:18]을 넣어서 생기는 등의 상황에도 발생한다. 이런 상황을 예방하기 위해서는 비교 연산에서 상수를 먼저 작성하는 것이다.


if(data = 3) //시맨틱 오류, IDE에서 컴파일 오류가 발생하지는 않음

if(3 = data) //신텍스 오류, IDE에서 컴파일 오류를 일으킴

프로젝트의 규모가 커지면 찾기 힘든 오류가 있는데 If 문 바로 뒤에 ' ; '를 찍는 것이다.


if(3 == data);
    printf("Hello\n");

 이런 식의 코드는 차후에 오류를 찾기 힘들어진다. 따라서 위와같은 단일 문장일경우에는 조건문과 수행문을 한줄에 나타내는 식으로 작성함으로써 오류를 예방할 수 있다[각주:19].

 복합문의 경우에는 K&R 스타일로 코드를 작성하면서 예방한다.


if(3 == data){
    printf("Hello\n");
}



Etc. 기타

삼항연산자( A > B ? True : False)

If문을 쓰기에는 규모가 커지는 느낌이고 판단을 해야겠다는 둥의 생각이 들때 주로 사용한다.


int n = 4;

n = n < 5 ? 7 : 8;


여기에서 [ n ]이 5보다 작음이 확인되므로 True에 해당하는 7이 [ n ]에 저장된다.

삼항연산자는 간단한 판단에는 사용이 용이하지만 다음의 단점이 있어 크게 사용되지 않는다.

>> 단일 문장만 사용 가능

>> 중괄호를 사용하는 복합문의 사용 불가

>> 단순수식표현만 사용 가능



Request.

한 줄에 3개의 단씩 구구단 출력 (9단까지)



  1. 만일 %-05d가 허용된다면 5를 출력할 때 [50000]가 될텐데, 그렇게 되면 의미 자체가 5 -> 50,000으로 변질되기 때문이다. [본문으로]
  2. X는 임의의 값을 의미한다. [본문으로]
  3. 일반적으로 텍스트 '커서'라고 불리는 것의 실제 명칭이다. [본문으로]
  4. 혹은 [\\r - \\n ] [본문으로]
  5. 모듈러(Modular) 연산자라고도 한다. [본문으로]
  6. 여기서 [ i ]는 변수명으로, i에 해당하는 부분에 값을 증감시킬 변수의 이름을 작성하면 된다. [본문으로]
  7. [ ++i ], [ --i ] [본문으로]
  8. [ i++ ], [ i-- ] [본문으로]
  9. [ ++i ]인 경우에는 [ i + 1 ]등으로 [본문으로]
  10. 컴퓨터는 0은 거짓, 0 이외의 수는 참으로 판단한다. 1은 참의 대표값이다. [본문으로]
  11. 키보드의 [ \\ ]키를 SHIFT키와 함께 눌렀을 때 나오는 글자이다. "파이프"라고 불리기도 한다. [본문으로]
  12. 참은 거짓으로, 거짓은 참으로 [본문으로]
  13. 컴파일러도 최적화를 하기 힘들기 때문 [본문으로]
  14. 강의 때는 제어문이라는 이름으로 배웠는데 강의정리 때에는 나에게 맞게 세부 항목의 조건문으로 변경해 작성하였다. [본문으로]
  15. 속도(퍼포먼스), 메모리 [본문으로]
  16. 이곳에는 비교하고싶은 대상이 들어간다. [본문으로]
  17. i == 3; 이 예시 [본문으로]
  18. i = 3; 등의 연산 [본문으로]
  19. 코드가 부자연스럽기 때문에 눈에 보인다. [본문으로]
  20. 정확히는 정의/알려지지 않았다라고 한다지만, 보편적으로 없다고 하니까 [본문으로]
728x90
반응형

'외부활동 > TIPS 19th' 카테고리의 다른 글

[TIPS 19TH] 06 : 2018.07.13. (목)  (2) 2018.07.13
[TIPS 19TH] 05 : 2018.07.09. (월)  (2) 2018.07.10
[TIPS 19TH] 04 : 2018.07.05. (목)  (2) 2018.07.06
[TIPS 19TH] 02 : 2018.06.28. (목)  (4) 2018.06.29
[TIPS 19TH] 01 : 2018.06.25. (월)  (2) 2018.06.26
Comments