거북이와 개구리의 끄적끄적

3일차_C언어(printf,연산자,제어문[조건문,반복문]) 본문

공부/팁스

3일차_C언어(printf,연산자,제어문[조건문,반복문])

거북이가개굴개굴 2018. 7. 5. 01:59


C언어 printf & 연산자 & 제어문(조건문,반복문)


이 글은 Tips 3일차 강의를 듣고 정리한 글입니다.

잘못된내용, 보충할내용, 궁금한내용 있으시면 댓글 달아주시면 감사하겠습니다


3일차 강의에서는 지난 시간에 배운 표준출력함수인 printf와 연산자와 제어문에 대한 강의였습니다.

연산자 중에서도 비트연산자를 제외한 기본 연산자를 진행하였으며

제어문에서는 goto문을 제외한 조건문,반복문에 대해 정리하겠습니다.


1.표준출력함수

가.printf(printf format)

  format이 있는 문자열 출력 함수입니다.


<기본적인 format>

format

정의

예시

결과

%c

단일 문자 출력

printf(“%c”, ‘A’);

A

%s

문자열 출력

printf(“%s”, “반갑습니다”);

반갑습니다

%d

signed 정수 출력(4byte)

printf(“%d”, 1);

1

%u

unsigned 정수 출력(4byte)

printf(“%u”, 1);

1

%x

데이터를 16진수로 출력

printf(“%x”, 293012);

47894

%o

데이터를 8진수로 출력

printf(“%o”, 293012);

1074224

%f

실수 소수점 6자리까지 출력

printf(“%f”, 1.23);

1.230000

%g

소수점에서 의미없는 0을 버리고 실수 출력

printf(“%f”, 1.23);

1.23

%e

실수를 지수형태로 출력

printf(“%e”, 1.23);

1.230000e+01

%E

%e의 출력에서 eE로 변경

printf(“%E”, 1.23);

1.230000E+01

*float형 데이터를 %d로 출력하면?

  이상한 값이 출력되며 이는 printf 내부에서 발생하는 에러코드.

  float형 데이터를 int형으로 casting 후 출력하면 정수형으로 출력 됨.

*char형으로 저장한 데이터를 %d,%u로 출력하면?

  8번비트부터 31번 비트까지 모두 1로 채워지므로 아래의 사진과 같은 결과가 나온다.


<출력 칸 수 조절>

format

정의

예시

결과

%nd

n>0, n만큼 오른쪽 정렬
n<0, n
만큼 왼쪽 정렬

printf(“%3d”, 7);
printf(“%-3d”, 7);

[  7]
[7  ]

%0nd

%nd에서의 공백을 0으로 채움.

printf(“%03d”, 7);
printf(“%0-3d”, 7);

[007]
[7  ]

  디지털은 아날로그보다 직관적이지 못하다는 단점이 있음. 이를 보완한 것.

  전체 칸수를 시각적으로 표현함으로써 직관성을 높임.


<실수의 소수점 자리 수 format>

format

정의

예시

결과

%.nf

n>0, 소수 n자리까지 출력
n+1
자리에서 반올림

printf(“%.3f”, 3.14159);

3.142


<제어코드>

  콘솔과 관련되어 있으므로 OS가 제공함.
캐럿이란 콘솔에서 현재 입출력의 위치를 알려주는 커서

format

정의

예시

결과

\n

캐럿을 다음 줄로 이동

printf(“abc\nd”);

  abc
d

\r

캐럿을 현재 줄의 처음으로 이동

printf(“hello”);

printf(“\rbye”);

byelo

\t

, 캐럿을 오른쪽으로 8칸마다 이동

printf(“1234567812345”);
printf(“Tips\tSoft.”);

 123456781234
Tips            Soft.

\b

캐럿을 앞칸으로 이동

printf(“hello”);

printf(“\b\bbye”);

helbye

\a

beep

 

 

\”

출력

printf(“\” \””);

“ ”

\’

출력

printf(“\’ \’”);

‘ ‘

\\

\출력

printf(“\\100”);

\100

*\r vs \n

 원래의 \n은 캐럿의 행 위치만 아래로 내리는 것.

 \n\r보다는 \n만 타이핑하는게 편하므로 printf에서는 \n\r의 기능을 \n으로 사용함.

*\b

 백스페이스라 부르긴 하지만 캐럿만 앞으로 이동하는 것.
 글자를 지우고싶다면 \b를 출력하고 스페이스바를 출력해서 overwrite해야 함.

*제어코드의 아스키코드

 windows와 linux의 아스키코드는 10,13번이 뒤바뀌어있음.

 직접적인 통신시 주의해야 함.


*printf의 format

 printf는 워낙 많이 쓰이므로 printf에서 사용되는 format은 다른 윈도우 함수나 그 외의 출력함수들의 format의 표본이 됨.


*scanf의 format

 pritnf의 경우 이미 저장되어있는 데이터를 format에 맞춰 출력하는것

 그러므로 format과 데이터의 자료형이 달라도 크게 문제는 없음.

 하지만 scanf의 경우 자료형에 맞춰 데이터를 저장해야 하므로 데이터의 자료형과 scanf의 format이 일치해야 함.


2.연산자
컴퓨터는 연산자 하나에 의미 하나밖에 부여 못함.
그래서 수학에서 하나의 연산자는 여러 의미가 있을 수 있지만 c언어는 전부 나눠 놓음.
ex) =(
대입), ==(같다) : 확률적으로 대입이 많이 쓰이므로 짧게 쓰자.
       /(
나눗셈), %() : 나눗셈을 더 많이 쓰니 전통적의미인 /로 몫은 %로 하자.


가.기본연산자

<대입 연산자>

기호

정의

예시

결과

A = B

대입 연산자.
B
A에 대입한다.

int data1 = 1, data2 = 2;

data1의 값 : 1
data2
의 값 : 2

data1이라는 변수에 1을 대입했으므로 data1에 저장된 값은 1이다.
data2
라는 변수에 2를 대입했으므로 data2에 저장된 값은 2.


<산술 연산자>

기호

정의

예시

결과

A + B

덧셈 연산자.
A,B
의 값의 합.

int res1 = data1 + data2;

res1의 값 : 3

A - B

뺄셈 연산자.
A
에서 B를 뺌.

int res2 = data1 – data2;

res1의 값 : -1

A * B

곱셈 연산자.
A,B
의 값의 곱.

int res3 = data1 * data2;

res3의 값 : 2

A / B

몫 연산자.
A
B로 나눈 몫.

int res4 = data1/data2;
double res4 = (double)data1/(double)data2;

int res4의 값 : 0
double res4
의 값 : 0.5

A % B

나머지 연산자.
A
B로 나눈 나머지.

실수의 나눗셈은 나머지가 없음.
정수연산에만 사용 가능.
int res5 = data1%data2;

res5의 값 : 1

산술연산자에는 +, -, *, /, %가 있다.
연산자 기준 좌,우로 A,B처럼 값이 2개가 필요하므로 이항연산자라고 한다.
특히 나머지연산자(%)는 잘 쓰면 여러 방면으로 활용이 가능하다.

*생략형
i = i + 1의 형태를 i += 1의 형태로 고칠 수 있고, 고쳐진 형태를 생략형이라고 한다.


<증감 연산자>

기호

정의

예시

A++(후위)
++A(
전위)

증가 연산자.
A
의 값을 1 증가시킴.

data2++; // 2
data2; // 3

++data2; // 3
data2; // 3

A--(후위)
--A(
전위)

감소 연산자.
A
의 값을 1 감소시킴.

data2--; // 2
data2; // 1

--data2; // 1
data2; // 1

++,--는 각각 변수의 값을 1 증가,감소.
전위형(++A)과 후위형(A++)으로 나뉨.

*산술연산과 증감연산의 속도
산술연산은 이항 연산자이므로 메모리 2개 필요(add 명령어)
증감연산은 단항 연산자이므로 메모리 1개 필요(inc 명령어) : cpu 내의 메모리에서 연산 됨.
증감연산이 더 빠르긴 한데 요즘 컴파일러는 최적화가 잘 해줌.
, i = i + 1(add operation) i++(increasement operation)으로 알아서 바꿔 줌.

*전위형과 후위형의 속도차이
전위형,후위형 둘다 inc명령어로 바뀌므로 속도의 차이는 없다.

*주의사항
컴파일러가 해석하는 기준이 계속 바뀌므로 조금만 복잡하게 수식이 들어가도 OSvisual 버전에 따라서 결과가 다름.
무조건 simple하게 쓰자.
ex)Sum
이라는 함수가 int형 인자 2개를 매개변수로 넘겨받는다 가정할 때,
   Sum(i++, ++i)
처럼 쓰지 말자.


<관계 연산자>

기호

정의

예시

결과

A > B

AB보다 크면 참
아니면 거짓

int res1 = data1 > data2
int res2 = data2 > data1

거짓, res11
, res20

A < B

BA보다 크면 참
아니면 거짓

int res3 = data1 < data2
int res4 = data2 < data1

, res31
거짓, res40

A >= B

AB보다 크거나 같으면 참
아니면 거짓

int res5 = data1 >= data2
int res6 = data2 >= data1

거짓, res50
, res61

A <= B

BA보다 크거나 같으면 참
아니면 거짓

int res7 = data1 <= data2
int res8 = data2 <= data1

, res71
거짓, res82

A == B

AB가 같으면 참
아니면 거짓

int res9 =data1 == data2

거짓, res90

A != B

AB가 다르면 참
아니면 거짓

int res10 = data1 != data2

, res101

두 수치의 값을 비교하여 참이면 연산결과는 1을 거짓이면 연산결과는 0이다.

*관계연산자 또한 연산자다.
관계연산자 또한 연산자이므로 조건문, 반복문에만 쓰는게 아니다
예시처럼 int res1 = data1 > data2; 이런 형태로도 쓰임. 이 때 res1의 값은 0이다.
그러므로 조건문을 관계연산자를 통해 표현 가능.


<논리 연산자>

기호

정의

예시

결과

A && B

논리 곱. AND연산.

A=1, B=1일 때, A && B

1

A=1, B=0일 때, A && B

0

A=0, B=1일 때, A && B

0

A=0, B=0일 때, A && B

0

A || B

논리 합. OR연산

A=1, B=1일 때, A || B

1

A=1, B=0일 때, A || B

1

A=0, B=1일 때, A || B

1

A=0, B=0일 때, A || B

0

!A

논리 부정. NOT연산

A = 1일 때, !A

0

A = 0일 때, !A

1

*논리연산자의 특성
1.A && B
 A가 참이면 B는 실행 됨.
 A
가 거짓이면 B는 실행 안됨.

  2.A || B
 A
가 참이면 B는 실행 안됨.
 A
가 거짓이면 B는 실행 됨.

   위의 두가지 특징을 이용하면 if문처럼 사용이 가능.


<연산자 우선순위>

순위

종류

연산자

연산방향

1

괄호, 배열, 구조체

() . [] ->

2

단항 연산자

*(간접) &(주소) ! ~ -- ++ +(부호) –(부호) sizeof

3

구조체 결합 연산자

.* ->*

4

승제 연산자

* / %

5

가감 연산자

+ -

6

시프트 연산자

<< >>

7

비교 연산자

< <= > >=

8

등가 연산자

== !=

9

비트 연산자 AND

&

10

비트 연산자 XOR

^

11

비트 연산자 OR

|

12

논리 연산자 AND

&&

13

논리 연산자 OR

||

14

조건 연산자

?:

15

대입 연산자

= *= /= += -= %= <<= >>= &= ^= |=

16

나열 연산자

,

연산자 우선순위란 하나의 수식에서 연산자가 여러 개 사용됐을 때, 연산자가 처리되는 순서
컴퓨터는 정수 연산이므로 같은 우선순위더라도 연산 순서에 따라 값이 달라질 수 있다
ex) (3*2) / 5  vs  3 * (2/5)
        (3*2) / 5
1이 되며, 3 * (2/5)0이 된다.
그래서 나온게 연산방향.
*와
 /는 연산방향이
→이므로 3*2가 계산된 후 /5가 계산된다.

*주의사항
시프트연산자보다 가감연산자가 우선순위가 높으므로 시프트 연산한 결과에 가감할 때 주의.
자세한건 나중에 비트연산자에서 설명.
simple
하게 구성해야 의도와 다른 계산을 방지할 수 있음.


 

3.제어문
일반적인 흐름이 아닌 흐름을 바꾸는걸 모두 제어문이라고 부름.
조건문,반복문,goto문이 있다.
근데 goto문은 c언어의 특징인 체계화 된 언어를 깨트리므로 쓰지 말자.
그러므로 조건문,반복문에 대해서 공부할 것.
제어문은 전개와 관련됐으므로 순서도를 그려 볼 것. 결국 순서도가 프로그램임.
제어문 중에서 조건문부터 봐보자.


가.조건문

<if-else 조건문>
if-else조건문의 경우 if,else를 묶어서 하나의 문장으로 본다.
if
는 생략이 불가능하지만 else는 생략이 가능하다.
1.
단일 if-else

  if(조건) 명령문 else 명령문의 형태로 가장 간단하게 쓰면 아래와 같음.

       명령문에는 단일문,복합문이 올 수 있으므로 조합이 아래처럼 4가지가 나옴.

    1).if 조건문(else생략)
    
괄호()안의 조건 값이 참이면 그 이후의 하나의 명령문을 실행함.
  2).if-else
조건문
    
조건 비교 횟수가 적을수록 더 효율적.
    
첫번째 조건을 비교하는 if에 참이 될 확률이 좀 더 높은 조건을 적는게 효율적.
2.
중첩 if-else
  중첩 if-else문을 간단하게 써보면 다음과 같다.

      else문 안의 if-else는 하나의 문장으로 취급되므로 else의 중괄호는 생략 가능.

        그리고 이걸 적절히 개행시키면 아래와 같다.

       그러나 중첩이 많아질수록 버그 발생 확률이 높아지므로 좋지 않음.

     조건문을 사용하는 것보다 좋은 건 조건문을 안 쓰는 것.

*조건문 대신 table을 사용하자.
근데 이것보다 더 좋은건 조건문 안쓰는 것.
table
을 활용하자.
ex) 90~100
A, 80~90B, 70~80C, 60~70D, 나머지는 F일 때, 아래처럼 구현 가능

    0~100점을 10으로 나누면 0~10이 나오므로 배열 11개 필요.
  char table[11] = {‘F’, ’F’, ‘F’, ‘F’, ‘F’, ‘F’, ‘D’, ‘C’, ‘B’, ‘A’, ‘A’};
로 선언해놓고
  grade = table[ score/10 ]
하면 끝남.
  메모리면에서는 조금 손해지만 속도면에서는 많은 이득이 생김.


<switch>

  case조건에 상수만 가능.
예전엔 상수 비교일 경우 switch가 더 빨랐으나 이제는 컴파일러가 최적화를 잘 해주기 때문에 비슷함.
break
는 제어문을 빠져나오기 위한 문장.
위의 예제에서 switch 조건이 상수1에 걸린다면?
명령문1을 실행하고 break를 만나므로 switch문을 빠져나옴.
case
상수1case 상수2break가 없다고 가정하고 switch 조건이 상수1에 걸린다면?
명령문1,2,3을 실행하고 break를 만나고 switch를 빠져나옴.
default
if-else에서 else와 같음. 항상 마지막에 사용 됨. 단지 위치는 상관 없음.
switch
if는 성능상 크게 차이가 없으므로 쓰고 싶은 거 쓰면 됨.


<조건 수식 연산자(삼항연산자)>

  사실 이놈은 멀티프로세싱을 위해 나온 연산자임.
연산자라서 조건문보다 조금 더 빠르지만 컴파일러의 최적화가 좋아서 딱히 별 차이 없음.

*단점
수식을 단일문으로만 구성 가능하고, 복합문을 못 씀.
, 나중에 기능이 추가되어 수식을 복합문으로 바꿔야 한다면 if-else 구조로 다 뜯어 고쳐야 됨.

*조건문보다 산술문!
조건문보다는 연산자를 이용한 산술문의 속도가 더 빠르므로 가능하면 산술문으로 바꿀 것.


*오류 종류

<구문 오류(Syntax Error)>
문법오류로 컴파일러가 좋아져서 개발 툴에서 알아서 알려줌.
컴파일 전 오류가 난 부분에서 빨간 밑줄로 표시해 주는 것.


<의미 오류(Semantic Error)>
문법적으로 맞지만 의미가 틀린 것.
흔히 말하는 Bug.
Bug
에도 두가지가 있음.
1.
프로그램이 죽는 경우
 
프로그램이 죽는 경우 죽는 지점에 가서 디버깅 하면 됨.
2.
프로그램은 살아있지만 원치 않는 결과를 냄.
 
죽지 않고 원치 않는 결과는 어디서부터 디버깅 해야되는지 알 수 없음.
 
월화수목금금금의 시작.

*Bug를 개선하는 방법
나쁜 스타일이 버그를 만드는 것. 스타일을 고쳐야 함.

*Bug 줄이는 습관 1(상수먼저)

 왼쪽은 조건을 만족할 때만 명령문이 실행되고, 오른쪽은 항상 명령문 실행 됨.
왼쪽은 관계연산자를 써야하는 상황에서 대입연산자를 쓴 경우 -> 버그발생!
이런 버그를 잡기 위해선 상수 먼저 쓰는 습관을 들여야 함.

  왼쪽에서 if(data = 3)은 변수에 상수를 대입하는 상황. 가능함
오른쪽에서 if(3 = data)은 상수에 변수 대입 불가능. 문법 오류. -> 좋은 습관
그러므로 상수를 먼저 쓰는 습관을 들일 것.

*Bug 줄이는 습관 2(과한 세미콜론)

  왼쪽은 if 조건문은 조건 참,거짓 여부와 상관없이 항상 data++이 실행 됨.
가운데와 오른쪽은 if 조건이 참일 경우에만 data++이 실행 됨.
이런 버그를 잡기위해선 조건문이 참일 때 실행되는 명령문을 올려쓰자.
명령문이 복합문이라면 가운데처럼 중괄호를 올려쓰거나
명령문이 단일문이라면 명령문 자체를 올려쓰면 됨.


나.반복문
여기서부터 전개능력과 생략된 걸 풀어 쓰는 능력이 많이 필요 .
원문을 생각하다 보면 여기는 나중에 어떻게 수정될 것인지 이런 것도 고려해야 함.
반복문에는 3가지 요소가 필요함.(시작조건, 조건변화수식, 종결조건)


1.반복문
순서도 그리는게 정말 도움이 많이 됨
꼭 연습해보는 것을 권장.
반복문 작성 방법
1)
반복되는 걸 직접 적기
2)
적어놓은 것을 보고 변수,상수의 개수와 값을 찾기
3)
시작,종결조건을 찾기.
4)
불필요한 변수 제거하기.
5)
반복문 구성하기.


<for>

  정규화 된 반복문(3요소를 한번에 표현 가능하므로 제일 명쾌함). : 위 예제는 5회 반복.
무한루프는 어떻게?

  3요소들 모두 생략.(무한루프)
정규화 되지 않은 반복문은 whileif를 이용하게 됨.


<while>

  위의 예제처럼 for(;종결조건;) 이렇게 쓰면 while(종결조건)과 같은 반복문.
두 반복문이 똑같은데 왜 있는가?
사람마다 스타일이 달라서 이것 저것 다 가능하게 해 놓은 것.
무한루프는 어떻게?

  위의 예제처럼 while(1)이면 무한루프


<do-while>

  앞의 두 반복문과 조금 다름.
무조건 최초 한번은 수행 됨.
근데 무조건이란 건 거의 없으므로 잘 안 쓰는 놈.


<중첩 반복문>
중첩 조건문처럼 반복문 안에 반복문이 있는 형태.


<break continue>
1.break

   자신이 포함된 제어문 하나 탈출.
 위의 예제에서 i는 0~9까지 출력되어야 하지만, i==5에서 break되므로 0~4만 출력된다.

2.continue

   자신이 포함된 제어문을 1회 취소함.
 위의 예제에서 i는 0~9까지 출력되어야 하지만, i==5에서 continue되므로 skip하고 0~4, 6~9가 출력된다.


<무엇을 써야할까>
for, while, do-while의 성능은 비슷하므로 원하는 방식으로 쓰면 됨.
마지막으로 중첩반복문과 조건문을 이용한 예제 2개로 마무리 하겠습니다.


<예제1> 중첩반복문 & 조건문 & break & continue

  위의 예제1 코드에 대한 순서도를 그려보면 아래와 같습니다.


<예제2> 구구단 세 단씩 출력하기

  아래 사진과 같이 구구단을 세줄씩 출력해보겠습니다.

  먼저 세줄씩 출력하기 위해서는 아래와 같이 구상합니다.

  print 함수를 빨간색 네모라고 하면

  빨간색 네모로 주황색 네모를 만들고

  주황색 네모로 노란색 네모를 만들고

  노란색 네모로 파란색 네모를 만듭니다.

  이를 의사코드로 구현하면 아래와 같습니다.

  이번에는 변수명을 아래와 같이 정합니다.

  위에서 정한 변수명과 반복문 횟수를 이용하여 순서도를 그리면 아래와 같습니다.

  이를 코드로 구현아면 아래와 같습니다.


이해가 안되는 부분이나 틀린 부분 있으면 댓글달아주시면 감사하겠습니다.

긴 글 읽어주셔서 감사합니다.