while 반복문은 조건식이 만족하지 않으면 반복을 하지 않고 넘어간다. do while 반복문은 조건이 만족하지 않아도 코드를 최소 한 번은 실행한다.
do while 반복문은 초기식이 반복문 바깥에 있고, do로 시작하여 중괄호 안에 반복할 코드와 변화식이 들어가고, 중괄호가 끝나는 부분에 조건식을 지정한다.
초기식
do
{
반복할 코드
변화식
} while (조건식);
초기식의 값과는 상관없이 중괄호 안의 코드와 변화식을 한번 실행하고, 조건식을 판단하여 참이면 코드를 계속 반복하고, 거짓이면 반복문을 끝낸 뒤 다음 코드를 실행한다.
29.1 do while 반복문 사용하기
#include <stdio.h>
int main()
{
int i = 0;
do
{
printf("Hello, world! %d\n", i);
i++;
} while (i < 100);
return 0;
}
반복문에 사용할 변수를 선언한 뒤 0으로 초기화 한다. do 아래에 중괄호를 열고, 반복할 코드와 변화식을 넣는다. 중괄호를 닫은 뒤 while에 조건식을 지정하고 세미클론을 붙여준다.
do 다음에 오는 코드는 조건식과 관계 없이 무조건 한 번은 실행한다. 따라서 printf가 실행되고 i++가 실행된다. 이후 조건식에서 i가 100보다 작은지 검사한다. 100보다 작으면 계속 반복하고, 100이되면 i < 100은 거짓이 되므로 반복문이 종료된다.
중괄호 안에는 반드시 변화식이 있어야 한다. 변화식이 없으면 무한루프 현상이 발생하므로 주의해야 한다.
29.2 초깃값을 1부터 시작하기
#include <stdio.h>
int main()
{
int i = 1;
do
{
printf("Hello, world! %d\n", i);
i++;
} while (i <= 100);
return 0;
}
i를 1로 초기화 했기 때문에 조건식을 i <= 100으로 지정하여 100번 반복하도록 하였다. i가 101이 되면 i<=100이 거짓이 되므로 반복문이 종료된다.
29.3 초깃값을 감소시키기
#include <stdio.h>
int main()
{
int i = 100;
do
{
printf("Hello, world! %d\n", i);
i--;
} while (i > 0);
return 0;
}
i를 100으로 초기화하고, i--로 i를 1씩 감소시키면서 i > 0으로 100번 반복시켰다. i가 0 이되면 반복문을 끝낸다.
29.4 입력한 횟수대로 반복하기
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int count;
scanf("%d", &count);
int i = 0;
do
{
printf("Hello, world! %d\n", i);
i++;
} while (i < count);
return 0;
}
scanf 함수로 입력값을 받아 count변수에 저장하여 조건식에서 i < count로 count의 값만큼 반복하도록 하였다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int count;
scanf("%d", &count);
do
{
printf("Hello, world! %d\n", count);
count--;
} while (count > 0);
return 0;
}
위 코드와 같이 입력 받은 값을 초기식으로 사용할 수 도 있다. 변화식을 count--로 하여 반복할 때마다 count값을 1씩 감소시키고, 조건식에서 count 가 0보다 클때까지 반복하여 count에 들어있는 값만큼 반복하였다.
29.5 반복 횟수가 정해지지 않은 경우
while 반복문처럼 do while 반복문도 반복 횟수가 정해지지 않았을 경우에 주로 사용한다. do while반복문은 최소 한번은 반드시 실행한다는 점에서 차이가 있다.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
srand(time(NULL));
int i = 0;
do
{
i = rand() % 10;
printf("%d\n", i);
} while (i != 3);
return 0;
}
위 코드는 0~9까지 무작위로 생성되는 숫자중 조건식 i != 3에 따라 3이 생성되면 반복을 종료하고 3이 생성되지 않으면 계속 반복하는 코드이다.
29.6 do while 반복문으로 무한루프 만들기
#include <stdio.h>
int main()
{
do
{
printf("Hello, world!\n");
} while (1);
return 0;
}
do 다음에 반복할 코드를 작성하고 조건식 부분에 1을 지정하면 된다. 조건식이 없으므로 변화식도 필요 없다.
stdbool.h 헤더를 사용하고, 조건식에 true를 지정해도 된다.
29.7 코드를 한 번만 실행하기
#include <stdio.h>
int main()
{
do
{
printf("Hello, world!\n");
} while (0);
return 0;
}
조건식에 0을 지정하면 do부분의 코드가 한 번만 실행된다. 조건식이 거짓이므로 반복을 하지 않고 끝내기 때문이다.
stdbool.h 헤더를 사용하고, 조건식에 false를 지정해도 된다.
29.8 퀴즈
정답은 b, c, e이다.
정답은 c 이다.
정답은 c이다.
29.9 연습문제 : do while 반복문 사용하기
정답은 c1 != 'q' 이다. 입력값이 q가 아니면 코드를 계속 반복한다.
29.10 심사문제 : 숫자의 합 구하기
정답은 다음 코드와 같다.
sum += i;
i++;
Unit 30. break, continue로 반복문 제어하기
break는 for,while,do while,switch문법에서 제어 흐름을 벗어나기 위해 사용한다. switch의 경우로 예를 들면 다음과 같다.
continue는 break와 비슷하지만 break는 제어 흐름을 중단하고 빠져나오지만, continue는 제어 흐름을 유지한 상태에서 코드의 실행만 건너뛰는 역할을 한다.
30.1 break로 반복문 끝내기
#include <stdio.h>
int main()
{
int num1 = 0;
while (1)
{
num1++;
printf("%d\n", num1);
if (num1 == 100)
break;
}
return 0;
}
while에 1을 지정하여 무한루프를 만들고 반복하는 코드에서 num1의 값을 1씩 증가시킨후 num1의 값을 출력하고, if를 이용하여 num1 이 100이 되면 break로 반복문을 종료시킨다. break를 실행하게 되면 반복문이 바로 종료된다.
다음과 같이 for문에서도 반복문의 동작은 같다.
#include <stdio.h>
int main()
{
int num1 = 0;
for (;;)
{
num1++;
printf("%d\n", num1);
if (num1 == 100)
break;
}
return 0;
}
반복 횟수가 정해져 있어도 break를 사용하면 반복문은 바로 끝난다.
30.2 continue로 코드 실행 건너뛰기
#include <stdio.h>
int main()
{
for (int i = 1; i <= 100; i++)
{
if (i % 2 != 0)
continue;
printf("%d\n", i);
}
return 0;
}
1 부터 100까지 반복하며 if를 사용하여 i가 홀수면 continue를 실행해서 아래 코드를 건너뛰고 다음 반복을 시작한다.
while에서도 continue의 동작은 같다.
#include <stdio.h>
int main()
{
int i = 1;
while (i <= 100)
{
i++;
if (i % 2 != 0)
continue;
printf("%d\n", i);
}
return 0;
}
무한 루프에서 사용하면 위 코드의 경우 짝수만 계속 출력되고, 반복문은 끝나지 않는다.
30.3 입력한 횟수대로 반복하기
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int count;
scanf("%d", &count);
int i = 1;
while (1)
{
printf("%d\n", i);
if (i == count)
break;
i++;
}
return 0;
}
무한 루프인 반복문 상태에서 i의 값을 출력하고, i의 값이 입력한 값과 같으면 반복문을 종료하고, 다르면 i의 값을 1씩 증가시킨다.
30.4 입력한 숫자까지 짝수만 출력하기
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int count;
scanf("%d", &count);
for (int i = 1; i <= count; i++)
{
if (i % 2 != 0)
continue;
printf("%d\n", i);
}
return 0;
}
입력 받은 값 만큼 반복하고, 반복문 안에서 i 가 홀수면 continue를 실행하여 다음 코드를 실행하지 않고 건너뛰고, i가 짝수면 printf로 숫자를 출력한다.
i를 10으로 나눴을때 나머지가 7이면 아래 코드를 건너뛰고, i가 num2 보다 커지면 반복문을 종료한다.
Unit 31. 계단식으로 별 출력하기
31.1 중첩 루프 사용하기
콘솔은 2차원 평면이기 때문에 별을 일정한 모양으로 출력하려면 반복문을 두 개 사용하는 것이 편하다.
반복문 안에 반복문이 들어가는 구조를 중첩 루프라 한다. 반복문의 루프 인덱스 변수는 index의 첫 글자를 따서 i를 사용하고 반복문 안에 반복문이 들어갈 때는 i, j, k 처럼 i 부터 알파벳 순서대로 사용한다. 2중 for문은 다음과 같은 구조이다.
#include <stdio.h>
int main()
{
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
printf("j:%d ", j);
}
printf("i:%d\\n", i);
printf("\n");
}
return 0;
}
바깥쪽 루프에서 시작하여 안쪽 루프가 가로 방향으로 j 값을 출력하고, 가로 방향 출력이 끝나면 바깥쪽 루프에서 i값과 개행문자를 출력하여 세로 방향을 처리한다.
중첩루프는 2차원 평면을 다룰 수 있으므로 이미지 처리, 영상처리 , 좌표계 처리 등에 사용된다.
31.2 사각형으로 별 출력하기
#include <stdio.h>
int main()
{
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
printf("*");
}
printf("\n");
}
return 0;
}
안쪽의 반복문에서 printf로 별을 출력하면 가로 방향으로 별이 하나씩 출력된다. printf에서 제어문자를 사용하지 않으면 줄바꿈을 하지 않는다. 바깥쪽 반복문에서 안쪽 반복문이 끝나면 제어문자 \n을 출력함으로 줄을 바꾸고 다시 안쪽 반복문으로 별을 출력하여 사각형을 그릴 수 있다.
#include <stdio.h>
int main()
{
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 7; j++)
{
printf("*");
}
printf("\n");
}
return 0;
}
위와 같이 코드를 수정하면 안쪽 반복문은 3번 반복하고, 바깥쪽 반복문은 7번 반복하므로 가로 방향으로 별을 7개 그리고, 세로 방향으로 3줄을 출력한다.
31.3 계단식으로 별 출력하기
위와 같이 계단식으로 별을 출력하려면 별을 출력하지 않는 부분이 있으므로 조건문으로 출력을 제어해야 한다.
첫번째 줄에는 별이 한개, 두번째 줄에는 별이 두 개 와 같은 규칙으로 줄의 위치와 별의 개수가 동일하다. 따라서 가로 줄을 출력할 때 세로 줄의 크기 만큼 반복하여 출력하도록 하면 된다. 이 조건을 고려하여 소스코드를 작성하면 다음과 같다.
#include <stdio.h>
int main()
{
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
if (j <= i)
printf("*");
}
printf("\n");
}
return 0;
}
안쪽 반복문에서 별을 줄 위치 숫자 만큼 출력하기 위해 조건문으로 if(j <= i)를 사용하였다.
31.4 대각선으로 별 출력하기
별을 대각선으로 출력하는 코드는 다음과 같다.
#include <stdio.h>
int main()
{
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < 5; j++)
{
if (j == i)
printf("*");
else
printf(" ");
}
printf("\n");
}
return 0;
}
가로 방향 변수와 세로 방향 변수가 같을 경우 별을 출력하고 같지 않으면 공백을 출력하여 대각선으로 별이 출력되도록 하였다. 콘솔에서는 문자 출력을 하지 않으면 항상 가장 왼쪽에서 시작하므로 별을 출력하지 않는 부분은 공백을 출력해야 별이 원하는 위치에 오게 된다.
31.5 퀴즈
정답은 a,d,e 이다.
정답은 b이다.
31.6 연습문제 : 역삼각형 모양으로 별 출력하기
정답은 다음 코드와 같다.
if(j < i)
{
printf(" ");
}
else
{
printf("*");
}
첫줄에는 별 5개로 시작하여 하나씩 줄어들기 때문에 i가 늘어날 때 마다 출력되는 공백의 수가 하나씩 늘어나서 역삼각형 모양의 삼각형이 출력된다.
31.7 심사문제 : 산 모양으로 별 출력하기
정답은 다음 코드와 같다.
#include <stdio.h>
int main()
{
int height;
scanf("%d", &height);
for (int i = 0; i < height; i++)
{
for (int j = height - 1; j >= 0; j--)
{
if (j > i)
printf(" ");
else
printf("*");
}
for (int j = 0; j < height - 1; j++)
{
if (j < i)
printf("*");
}
printf("\n");
}
return 0;
}
입력받은 높이로 세로 방향 반복문을 만들고, 가로 방향은 출력할 값들을 반으로 나눠서 반복문을 두개로 처리한다. 첫번째 안쪽 반복문에서는 j의 초기값을 입력값-1로 하고, j를 1씩 감소시키면서 j가 세로줄 위치보다 크면 공백을 출력하고, 아니면 별을출력하여 별이 다음과 같은 모양인 삼각형을 출력한다.
두번째 안쪽 반복문은 반대로 j가 세로줄 위치보다 작으면 별을 출력하여 다음과 같은 형태의 삼각형을 출력하여 위의 삼각형과 붙여지도록 한다.
삼각형이 붙여지는 것은 전체가 붙여지는 것이 아니라 한 줄씩 붙여지는 것이고, 안쪽의 두 반복문이 마치면 개행문자를 출력하므로 줄을 구분한다.
Unit 32. goto로 프로그램의 흐름을 원하는대로 바꾸기
goto의 동작은 다음과 같이 goto로 레이블을 지정하고 코드 안에서 지정한 레이블이 있는 위치로 코드를 건너뛴다.
goto를 남발하는 것은 코드의 가독성도 떨어뜨리고 유지보수를 힘들게 하여 좋지 않지만, goto를 적절히 활용하면 중복되는 코드를 없앨 수 있고, 코드를 좀 더 간결하게 만들 수 있다. 특히 에러처리에 유용하기 때문에 리눅스 커널에서 자주 사용된다.
32.1 goto와 레이블 사용하기
goto는 레이블을 지정하여 사용한다. 레이블은 :(클론)을 붙이고, 레이블 이름을 짓는 규칙은 변수와 같다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int num1;
scanf("%d", &num1);
if (num1 == 1)
goto ONE;
else if (num1 == 2)
goto TWO;
else
goto EXIT;
ONE:
printf("1입니다.\n");
goto EXIT;
TWO:
printf("2입니다.\n");
goto EXIT;
EXIT:
return 0;
}
goto에 레이블을 지정하면 중간에 있는 코드는 무시하고 레이블로 즉시 이동한다. 위 코드에서는 레이블로 이동하고 다시 goto로 EXIT레이블로 이동하여 프로그램을 끝낸다. 위 코드를 if 문으로 표현하면 다음과 같다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int num1;
scanf("%d", &num1);
if (num1 == 1)
printf("1입니다.\n");
else if (num1 == 2)
printf("2입니다.\n");
return 0;
}
if문을 이용한 코드가 더 간결하기 때문에 이와 같은 경우 goto문을 쓰는 것은 좋지않다.
32.2 중첩 루프 빠져나오기
#include <stdio.h>
#include <stdbool.h>
int main()
{
int num1 = 0;
bool exitOuterLoop = false;
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
if (num1 == 20)
{
exitOuterLoop = true;
break;
}
num1++;
}
if (exitOuterLoop == true)
break;
}
printf("%d\n", num1);
return 0;
}
위 코드는 num1이 20이 되면 반복문을 끝내는데 num1은 안쪽 반복문에서 증가시키고 있다.
break는 현재 루프만 끝내기 때문에 안쪽 루프에서 break를 사용하면 안쪽 루프만 끝내고, 반대쪽 루프는 계속 반복한다. 그렇기 때문에 바깥쪽 루프도 끝내려면 break를 두 번 실행해야 한다.
이럴 때는 다음과 같이 goto문을 사용하여 간단하게 루프를 빠져나올 수 있다.
#include <stdio.h>
int main()
{
int num1 = 0;
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
if (num1 == 20)
goto EXIT;
num1++;
}
}
EXIT:
printf("%d\n", num1);
return 0;
}
위 코드는 num1이 20이 되면 EXIT레이블로 즉시 이동하여 반복문을 한 번에 빠져나온다.
이와 같이 goto는 다중 루프를 빠져나올 때 유용하다.
32.3 goto와 에러처리 패턴
다음은 가상의 통지서를 집집마다 방문하여 자가주택을 소유한 30대 남자에게 전달하는 코드이다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int gender; // 성별: 남자 1, 여자 2
int age; // 나이
int isOwner; // 주택 소유 여부: 자가 1, 전월세 0
scanf("%d %d %d", &gender, &age, &isOwner);
printf("안녕하세요.\n");
printf("문을 연다.\n");
if (gender == 2) // 여자
{
printf("안녕히계세요.\n");
printf("문을 닫는다.\n");
return 0;
}
if (age < 30) // 30세 미만
{
printf("안녕히계세요.\n");
printf("문을 닫는다.\n");
return 0;
}
if (isOwner == 0) // 전월세
{
printf("안녕히계세요.\n");
printf("문을 닫는다.\n");
return 0;
}
return 0;
}
문을 열고 자가주택을 소유한 30대 남자가 아니라면 문을 닫고 나온다. 위와 같은 경우 주어진 조건에 해당하지 않으면 인사를 하고 문을 닫는 코드가 경우마다 중복되고 있다. 만약 조건이 더 늘어나면 중복되는 코드도 더 많아지게 된다.
이때 다음과 같이 goto문을 사용하면 에러 상황때 실행하는 중복코드를 하나로 모을 수 있다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int gender; // 성별: 남자 1, 여자 2
int age; // 나이
int isOwner; // 주택 소유 여부: 자가 1, 전월세 0
scanf("%d %d %d", &gender, &age, &isOwner);
printf("안녕하세요.\n");
printf("문을 연다.\n");
if (gender == 2)
goto EXIT;
if (age < 30)
goto EXIT;
if (isOwner == 0)
goto EXIT;
EXIT:
printf("안녕히계세요.\n");
printf("문을 닫는다.\n");
return 0;
}
에러가 발생하면 EXIT로 이동시켜 에러 코드를 처리하게 한다.
위와 같이 종료 직전에 항상 실행해야 하는 에러 코드들을 한 곳에 모아두고 goto를 사용하면 중복 코드를 없앨 수 있고, 코드의 의도도 명확해진다.
32.4 퀴즈
정답은 d,e 이다. 변수 생성 규칙과 동일하다.
정답은 b, d, e이다.
32.5 연습문제 : switch에서 반복문 빠져나오기
2번에 레이블을 만들고 1번에 goto문으로 바로 2번으로 이동하도록 하여 정답은 다음과 같다.
goto EXIT;
EXIT:
32.6 심사문제 : 중첩 루프 빠져나오기
200과 300이 출력되므로 EXIT2 레이블로 이동해야 하므로 EXIT2;가 답이다.
Unit 33. FizzBuzz
FizzBuzz는 간단한 프로그래밍 문제로 규칙은 다음과 같다.
1에서 100까지 출력
3의 배수는 Fizz 출력
5의 배수는 Buzz 출력
3과 5의 공배수는 FizzBuzz 출력
33.1 1부터 100까지 숫자 출력하기
#include <stdio.h>
int main()
{
for (int i = 1; i <= 100; i++)
{
printf("%d\n", i);
}
return 0;
}
위 코드는 for 반복문을 이용하여 1부터 100까지 숫자를 출력하는 코드이다.
33.2 3의 배수일때와 5의 배수일 때 처리하기
다음은 1부터 100까지 숫자를 출력하면서 3의 배수일 때는 Fizz, 5의 배수 일때는 Buzz를 출력하는 코드이다.
#include <stdio.h>
int main()
{
for (int i = 1; i <= 100; i++)
{
if (i % 3 == 0)
printf("Fizz\n");
else if (i % 5 == 0)
printf("Buzz\n");
else
printf("%d\n", i);
}
return 0;
}
i를 3으로 나눴을 때 나머지가 0이면 3의 배수이기 때문에 Fizz를 출력하고, 5로 나눴을 때 나머지가 0이면 5의 배수 이므로 Buzz를 출력한다. 아무것도 해당하지 않으면 숫자를 출력한다.
33.3 3과 5의 공배수 처리하기
3과 5의 공배수를 처리할 때는 다음과 같이 논리연산자 &&를 사용하면 된다.
#include <stdio.h>
int main()
{
for (int i = 1; i <= 100; i++)
{
if(i % 3 == 0 && i % 5 == 0)
printf("FizzBuzz\n");
else if (i % 3 == 0)
printf("Fizz\n");
else if (i % 5 == 0)
printf("Buzz\n");
else
printf("%d\n", i);
}
return 0;
}
i를 3으로 나눴을 때 나머자 0 이고, 5로 나눴을 때도 나머지가 0이면 3과 5의 공배수 이므로 FizzBuzz를 출력한다.
3의나 5의 배수인지를 먼저 검사하면 그 조건문에서 처리하고 아래 조건식은 검사하지 않으므로 공배수를 처리하는 조건식은 가장 위인 if에 들어가야 한다. 공배수를 먼저 검사하고 3의 배수인지, 5의 배수인지를 검사해야 한다.
33.4 논리연산자를 사용하지 않고 3과 5의 공배수 처리하기
3 과 5의 최소공배수는 15이므로 15로 나눴을 때 나머지가 0이면 3 과 5의 공배수이다.
#include <stdio.h>
int main()
{
for (int i = 1; i <= 100; i++)
{
if(i % 15 == 0)
printf("FizzBuzz\n");
else if (i % 3 == 0)
printf("Fizz\n");
else if (i % 5 == 0)
printf("Buzz\n");
else
printf("%d\n", i);
}
return 0;
}
15가 3과 5의 최소공배수라는 것은 코드를 읽는 사람이 값 속의 뜻을 알아내야 하므로 실무에서는 i % 3 == 0 && i % 5 == 0 처럼 계산식과 논리연산자를 사용하여 뜻을 명확하게 드러내는 것이 좋다.
33.5 코드 단축하기
다음과 같이 삼항연산자를 사용하여 코드를 매우 단축할 수 있다.
#include <stdio.h>
int main()
{
for (int i = 0; ++i <= 100;)
printf(i % 3 ? i % 5 ? "%d\n" : "Buzz\n" : i % 5 ? "Fizz\n" : "FizzBuzz\n", i);
return 0;
}
반복문의 조건식 안에 변화식도 함께 작성하였다. 반복할 때 마다 ++를 수행하여 i의 값을 1씩 증가시킨 후 100보다 작거나 같은지 검사한다.
삼항연산자를 사용하여 각 조건들을 모두 처리하여 출력한다.
i % 3이 0이 아니면 3의 배수가 아니고, i % 5로 간다. i % 5가 0이 아니면 5의 배수도 아니므로 %d로 숫자를 출력한다.
i % 5 가 0 이면 Buzz를 출력한다.
i % 3이 0이면 다시 i % 5를 계산하여 0이 아닌 값이 나오면 Fizz를 출력하고 0이 나오면 3과 5의 공배수 이므로 FizzBuzz를 출력한다.
~ 연산자는 비트 NOT연산자로 0은 1로, 1은 0으로 비트를 뒤집는다. 또는 비트 반전 이라고도 한다.
1010 1010의비트를 뒤집으면 0101 0101이 되고 이는 10진수로 93이다.
23.3 시프트 연산자 사용하기
C에서는 비트의 논리 연산 뿐 아니라 >>, << 연산자로 각 자리를 이동시킬 수도 있다.
#include <stdio.h>
int main()
{
unsigned char num1 = 3; // 3: 0000 0011
unsigned char num2 = 24; // 24: 0001 1000
printf("%u\n", num1 << 3); // 24: 0001 1000: num1의 비트 값을 왼쪽으로 3번 이동
printf("%u\n", num2 >> 2); // 6: 0000 0110: num2의 비트 값을 오른쪽으로 2번 이동
return 0;
}
시프트 연산은 변수 << 이동할 비트수 또는 변수 >> 이동할 비트수 형식으로 사용한다. 비트를 이동시키고 남는 공간은 0으로 채운다.
0000 0011 (3)의 경우 num1 << 3 으로 왼쪽으로 3번 이동하여 0001 1000 (24) 가 되었다.
num2 >> 2는 0001 1000 (24)가 오른쪽으로 2번 이동하여 0000 0110 (6)이 되었다.
한번씩 이동할 때 마다 2의 거듭제곱으로 곱하거나 나누면 된다. 3 << 3 은 3 x 2^3 = 24 이고, 24 >> 2 는 24 / 2^2 = 6이다.
부호있는 자료형의 첫번째 비트는 부호비트라고 하는데, 이 비트가 1이면 음수, 0이면 양수다.
부호 있는 자료형에 저장된 -125는 1000 0011로 첫번째 비트가 1이라 음수이며 10진수로는 -125 이다. 이 비트들을 오른쪽으로 5번 이동시키면 모자라는 공간은 모두 부호비트의 값으로 채워지기 때문에 1111 1100 (-4) 이 된다. 부호없는 자료형은 비트를 오른쪽으로 이동하면 모자란 공간은 모두 0으로 채워진다.
부호 있는 자료형에서 부호비트가 0인 양수에 >> 연산을 하면 모자라는 공간은 부호비트인 0으로 채운다.
부호있는 자료형에서 양수를 왼쪽으로 두번 이동시키면 부호비트 0을 1이 덮어써서 음수가 될 수 도 있다. 반대로 음수의 비트를 왼쪽으로 이동시켜도 부호비트 1을 0이 덮어쓰게 되면 양수가 될 수도 있다. 부호있는 자료형에 시프트 연산을 할 때는 부호비트에 위치한 숫자에 따라 음수, 양수가 결정되므로 의도치 않은 결과가 나올 수 있으니 주의해야 한다.
24.4 비트 연산자로 플래그 처리하기
플래그는 깃발에서 유래한 용어이다. 깃발을 위로 올리면 on, 아래로 내리면 off가 되는 것 처럼 비트가 1이면 on, 비트가 0이면 off이다.
다음과 같이 8비트 크기의 자료형은 비트가 8개 들어가므로 8가지 상태를 정할 수 있다. 다음은 두번째 비트와 8번째 비트가 켜진 상태이다.
0100 0001
int와 같은 4바이트 크기의 자료형은 32비트 이기때문에 32개의 상태를 저장할 수 있다.
플래그는 적은 공간에 정보를 저장해야 하며 빠른 속도가 필요할 때 사용한다. CPU는 내부 저장공간이 매우 작기 때문에 각종 상태를 비트로 저장한다.
비트는 다음과 같이 |=로 킨다.
#include <stdio.h>
int main()
{
unsigned char flag = 0;
flag |= 1; // 0000 0001 마스크와 비트 OR로 여덟 번째 비트를 켬
flag |= 2; // 0000 0010 마스크와 비트 OR로 일곱 번째 비트를 켬
flag |= 4; // 0000 0100 마스크와 비트 OR로 여섯 번째 비트를 켬
printf("%u\n", flag); // 7: 0000 0111
if (flag & 1)
printf("0000 0001은 켜져 있음\n");
else
printf("0000 0001은 꺼져 있음\n");
if (flag & 2)
printf("0000 0010은 켜져 있음\n");
else
printf("0000 0010은 꺼져 있음\n");
if (flag & 4)
printf("0000 0100은 켜져 있음\n");
else
printf("0000 0100은 꺼져 있음\n");
return 0;
}
플래그로 사용할 변수에 |= 연산자와 숫자로 특정 비트를 킨다. 비트를 조작하거나 검사할때 사용하는 숫자를 마스크(mask)라고 부른다. 여기서는 1,2,4가 마스크이다.
플래그 비트를 켜는것은 OR연산의 특징을 이용하여 해당 비트가 꺼져있으면(0이면) 비트를 키고(1로 만들고) 켜져있으면 유지한다. 특정 비트가 꺼져있는지 확인하려면 &연산자를 사용하면 된다. 플래그에 저장된 값 0000 0111과 1(0000 0001)을 & 연산하면 0000 0001이 된다. 연산 결과가 마스크 값이 나오면 비트가 켜져있고, if 조건을 만족시키며 연산 결과가 0이 나오면 비트가 꺼져있음을 의미하고 if 조건을 만족시키지 못한다.
다음은 플래그의 비트를 끄는 방법이다.
#include <stdio.h>
int main()
{
unsigned char flag = 7; // 7: 0000 0111
flag &= ~2; // 1111 1101 마스크 값 2의 비트를 뒤집은 뒤 비트 AND로 일곱 번째 비트를 끔
printf("%u\n", flag); // 5: 0000 0101
if (flag & 1)
printf("0000 0001은 켜져 있음\n");
else
printf("0000 0001은 꺼져 있음\n");
if (flag & 2)
printf("0000 0010은 켜져 있음\n");
else
printf("0000 0010은 꺼져 있음\n");
if (flag & 4)
printf("0000 0100은 켜져 있음\n");
else
printf("0000 0100은 꺼져 있음\n");
return 0;
}
마스크 값을 ~로 뒤집은 뒤 &= 연산자를 사용하여 특정 비트를 끈다. 2의 비트를 뒤집으면 1111 1101 이 되어 AND연산 하면 2번째 비트만 0이된다. AND연산을 하면 원하는 비트가 켜져있든, 꺼져있든 항상 끄게 된다.
다음은 비트가 켜져있으면 끄고, 꺼져있으면 키는 방법이다. 다른 말로는 토글(toggle)이라고도 한다. ^= 연산자를 이용한다.
#include <stdio.h>
int main()
{
unsigned char flag = 7; // 7: 0000 0111
flag ^= 2; // 0000 0010 마스크와 비트 XOR로 일곱 번째 비트를 토글
flag ^= 8; // 0000 1000 마스크와 비트 XOR로 다섯 번째 비트를 토글
printf("%u\n", flag); // 13: 0000 1101
if (flag & 1)
printf("0000 0001은 켜져 있음\n");
else
printf("0000 0001은 꺼져 있음\n");
if (flag & 2)
printf("0000 0010은 켜져 있음\n");
else
printf("0000 0010은 꺼져 있음\n");
if (flag & 4)
printf("0000 0100은 켜져 있음\n");
else
printf("0000 0100은 꺼져 있음\n");
if (flag & 8)
printf("0000 1000은 켜져 있음\n");
else
printf("0000 1000은 꺼져 있음\n");
return 0;
}
XOR 연산은 두 비트가 다르면 1, 같으면 0이 되기 때문에 마스크의 플래그의 비트가 1이면 마스크의 비트 1과 같아서 0이되고, 플래그의 비트가 0이면 마스크의 비트 1과 달라서 1이 된다.
24.5 퀴즈
정답은 C 이다.
정답은 a 이다.
정답은 d이다.
정답은 b이다.
정답은 b이다. 끄는것은 c 보기처럼 해야 한다.
24.6 연습문제 : 시프트 연산과 플래그 활용하기
1 << 7은 1000 0000(128) 이다. 이걸 4로 만드려면 첫번째 비트를 끄고, 여섯번째 비트를 켜야 한다. 근데 위 코드에서 마스크가 시프트로 되어있으므로 1 << 2로 여섯번째 비트를 키고, ~(1 << 7) 연산으로 첫번째 비트를 끈다.
flag2는 0000 0000으로 만들어야 하는데 1 << 3은 0000 1000(8)이므로 1 << 3을 마스크로 하면 플래그와 마스크가 같기 때문에 모든 비트가 0이된다. 따라서 정답은 1 << 2, ~(1 << 7), 1 << 3 이다.
24.7 심사문제 : 시프트 연산과 플래그 활용하기
정답은 다음 코드와 같다.
flag |= num1 << 3;
flag &= ~(num2 >> 2);
flag ^= 1 << 7;
Unit 25. 연산자 우선순위 알아보기
C언어도 수학처럼 곱셈/나눗셈이 덧셈/뺄셈보다 우선순위가 높다. 또한 다양한 다른 연산자들의 우선순위는 다음과 같다.
논리연산자의 우선순위는 높은 순서로 !, &&, ||이다. 따라서 괄호 먼저 계산하면 false && !false || false 가 되고 이후 !를 계산하면 false && true || false 가 되고, &&를 계산하면 false || false가 되서 최종 결과는 false로 0이 출력된다.
다음과 같이 비교연산자에도 우선순위가 있다.
#include <stdio.h>
int main()
{
int num1;
num1 = 5 == 5 < 10;
printf("%d\n", num1);
return 0;
}
비교 연산자는 == 보다 <가 우선순위가 높다. 따라서 5 < 10이 참이므로 1이 나오고 5 == 1은 거짓이므로 num1에는 0이 저장된다.
#include <stdio.h>
int main()
{
int num1 = 1;
int num2 = 2;
int num3;
num3 = num1 << 2 + num2 << 1;
printf("%d\n", num3);
return 0;
}
산술 연산자가 시프트 연산자보다 우선 순위가 높기 때문에 2 + num2 가 먼저 계산 된 후 num1 << 4 << 1을 계산하여 32가 나온다.
num3 = (num1 << 2) + (num2 << 1);
위 코드와 같이 시프트 연산자를 괄호로 묶으면 먼저 계산되서 num3에는 8이 저장된다.
25.5 퀴즈
정답은 c이다.
정답은 b이다.
정답은 true이다.
정답은 a이다.
25.6 연습문제 : 괄호 사용하기
주어진 순서대로 괄호로 묶으면 정답은 2*((1<<num1)+(2>>num2)) 이다.
25.7 심사문제 : 괄호 사용하기
정답은 다음 코드와 같다.
((num1 + num2) * 10) - num3
Unit 26. switch 분기문으로 다양한 조건 처리하기
switch 분기문은 조건이 많아도 손쉽게 처리할 수 있다. switch문의 형태는 다음과 같다.
switch 분기문은 항상 case와 함께 사용한다. 변수의 값이 case에 지정한 값과 일치하면 해당 코드를 실행하고, case에 일치하는 것이 하나도 없으면 default의 코드를 실행한다. switch 분기문은 형식이 균일하며 처리해야 할 조건이 많을 때 사용한다.
파이썬에서는 switch 분기문이 없다.
26.1 사용자가 입력한 값에 따라 문자열 출력하기
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int num1;
scanf("%d", &num1);
switch (num1)
{
case 1:
printf("1입니다.\n");
break;
case 2:
printf("2입니다.\n");
break;
default:
printf("default\n");
break;
}
return 0;
}
1을 입력하면 case1 에 해당하는 코드가 실행되고, case에 없는 숫자를 입력하면 default에 해당하는 코드가 실행된다.
switch의 괄호에는 값을 판단할 변수를 지정한다. case 다음에는 반드시 값(리터럴)이 와야 하며 변수나 조건식은 올 수 없다. case에 값을 지정하면 :(클론)을 붙이고, 다음줄 부터 실행할 코드를 입력한다. 코드의 마지막에는 break를 입력해야 한다. default에는 아무 case에도 해당하지 않을 때 실행할 코드를 입력한다.
조건식이 바뀌지 않고, 값만 바뀔 때는 switch문이 적합하며, 값과 조건식 모두 바뀔 때는 else if가 적합하다.
26.2 case에서 break를 사용하지 않을 때의 동작 알아보기
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int num1;
scanf("%d", &num1);
switch (num1)
{
case 1:
printf("1입니다.\n");
case 2:
printf("2입니다.\n");
default:
printf("default\n");
}
return 0;
}
위 코드와 같이 break가 없으면 1을 입력했을 때 case 1:에 해당하는 코드만 실행하는 것이 아니라 case 2:, default: 에 해당하는 코드 모두 실행되었다. case를 작성할 때는 break로 중단해야 해당 case만 실행된다. break로 중단하지 않으면 그 다음에 있는 case, default의 코드가 모두 실행된다.
26.3 case에서 break 생략 응용하기
case에서 break를 생략하는 것은 버그같지만 실제로도 의도적으로 많이 사용하는 방식이다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int num1;
scanf("%d", &num1);
switch (num1)
{
case 1:
case 2:
printf("1 또는 2입니다.\n");
break;
case 3:
case 4:
printf("3 또는 4입니다.\n");
break;
default:
printf("default\n");
}
return 0;
}
위 코드와 같이 case 1: 과 case 2:를 연달아 지정하면 num1의 값이 1일때와 2일 때 모두 코드가 실행된다. num1의 값이 3또는 4일때도 마찬가지다. case에서 break를 생략하면 여러가지 값으로 같은 코드를 실행할 수 있다.
이 코드는 다음과 같은 if 조건문을 표현할 수 있다.
if (num1 == 1 || num1 == 2)
printf("1 또는 2입니다.\n");
else if (num1 == 3 || num1 == 4)
printf("3 또는 4입니다.\n");
else
printf("default\n");
if조건문은 일일히 조건식을 나열해줘야 하므로 처리해야할 숫자가 많아지면 번거로워지기 때문에 이런 경우에는 case에서 break를 생략하는 것이 유리하다.
26.4 case 안에서 변수 선언하기
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int num1;
scanf("%d", &num1);
switch (num1)
{
case 1:
int num2 = num1;
printf("%d입니다.\n", num2);
break;
case 2:
printf("2입니다.\n");
break;
default:
printf("default\n");
break;
}
return 0;
}
위와 같은 코드는 컴파일러에 따라 에러를 발생할 수 도 있고, 발생하지 않을 수도 있다. case부분을 중괄호로 묶지 않았기 때문이다.
case안에서 변수를 선언하려면 다음과 같이 해당 case를 중괄호로 묶어야 한다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int num1;
scanf("%d", &num1);
switch (num1)
{
case 1:
{
int num2 = num1;
printf("%d입니다.\n", num2);
break;
}
case 2:
printf("2입니다.\n");
break;
default:
printf("default\n");
break;
}
return 0;
}
변수를 선언한 case를 중괄호로 묶어주면 컴파일 에러가 발생하지 않고, 변수를 선언할 수 있다. 중괄호 안에 사용한 변수는 case 1:안에서만 사용할 수 없고 case 2:나 switch바깥에서는 사용할 수 없다.
26.5 switch에서 판별할 수 있는 자료형 알아보기
switch에서 판별할 변수는 정수 자료형만 사용할 수 있고, 실수 자료형은 사용할 수 없다. 문자 자료형은 정수 자료형이므로 switch에서 사용할 수 있다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
char c1;
scanf("%c", &c1);
switch (c1)
{
case 'a':
printf("a입니다.\n");
break;
case 'b':
printf("b입니다.\n");
break;
default:
printf("default\n");
break;
}
return 0;
}
작은 따옴표를 이용하여 문자 자료형을 사용할 수 있다. 그러나 case "hello": 처럼 문자열은 사용할 수 없다.
26.6 퀴즈
정답은 e이다. case 뒤에는 클론이 붙어야 한다.
정답은 e이다.
정답은 a,e이다.
26.7 연습문제 : switch 분기문 사용하기
비트 시프트 연산을 사용하므로 정답은 1,2,3 이다.
26.8 심사문제 : 음료수 자판기 만들기
정답은 다음 코드와 같다.
case 'f':
printf("환타\n");
break;
case 'c':
printf("콜라\n");
break;
case 'p':
printf("포카리스웨트\n");
break;
default:
printf("판매하지 않는 메뉴\n");
break;
Unit 27. for 반복문으로 Hello, world! 100번 출력하기
대부분의 프로그래밍 언어는 반복되는 작업을 간단하게 처리하기 위해 반복문이라는 기능을 제공한다. 반복문은 반복 횟수, 반복 및 정지 조건을 자유 조재로 제어할 수 있다.
C의 for 반복문은 다음과 같이 괄호 안에 초기식, 조건식, 변화식을 지정하며 이 부분을 루프 선언문 이라고 부른다. 그리고 중괄호 안에 반복할 코드를 작성하는데 이 부분을 루프 본체라 부른다.
for (초기식; 조건식; 변화식)
{
반복할 코드
}
초기식 부터 시작하여 조건식을 판별하여 조건식이 참이면 반복할 코드를 실행하고, 변화식을 수행한다. 다시 조건식을 검사하여 참이면 코드를 계속 반복하고, 거짓이면 반복문을 끝낸 뒤 다음 코드를 실행한다.
조건식 -> 루프본체 -> 변화식 -> 조건식으로 순환하는 것을 루프라 한다.
파이썬에서는 for문에 in 키워드를 사용하여 지정한 숫자만큼 반복하거나, range 함수를 이용하여 지정한 범위만큼 반복한다. 초기식, 조건식, 변화식이 따로 정해져 있지 않다.
27.1 for 반복문 사용하기
다음과 같이 for 반복문으로 Hello, world!를 100번 출력할 수 있다.
#include <stdio.h>
int main()
{
for (int i = 0; i < 100; i++)
{
printf("Hello, world!\n");
}
return 0;
}
초기식은 int i = 0;으로 반복에 사용할 변수를 선언하고 0으로 초기화 했다. 조건식은 i < 100; 으로 i가 100보다 작을 때 까지만 반복하겠다는 의미이다. 변화식은 i++로 반복 할 때 마다 i를 1씩 증가시키겠다는 의미이다.
반복문이 처음 시작하면 i에 0이 들어가고, i 가 100보다 작은지 검사하여 100보다 작으면 중괄호 안의 코드를 실행하고 i를 1 증가시킨다. i가 100이 되면 조건식이 거짓이 되므로 반복문을 끝낸다.
초기식에서 변수를 선언하는 것은 C99방식이다. 이전의 방식은 초기식에서 선언할 수 없고 밖에서 초기식에 사용할 변수를 선언해야 한다. C99방식에서 초기식에 선언한 변수는 for문 안에서만 사용할 수 있고, 옛날 방식은 for문 밖에서 선언했기 때문에 for문 안과 밖에서 모두 사용할 수 있다.
27.2 초기값의 변화 알아보기
#include <stdio.h>
int main()
{
int i;
for (i = 0; i < 10; i++)
{
printf("Hello, world!\n");
}
printf("%d\n", i);
return 0;
}
i가 0부터 1씩 증가하여 10이 되면 조건이 거짓이 되어 반복문이 종료되기 때문에 반복문이 끝난 뒤 변수 i의 값을 보면 10이 나온다.
27.3 초깃값을 1부터 시작하기
프로그래밍에서 반복문의 초기값은 보통 0부터 시작하지만 원하는 반복 횟수를 채울 수 있다면 어떤 초기값을 사용해도 상관 없다.
#include <stdio.h>
int main()
{
for (int i = 1; i <= 100; i++)
{
printf("Hello, world! %d\n", i);
}
return 0;
}
위 코드에서 초기식이 1부터 시작하지만 i <= 100이므로 100보다 작거나 같을때까지 반복하여 반복 횟수는 100번이다. i가 101이 되면 조건식이 거짓이 되어 반복문을 끝낸다.
27.4 초깃값을 감소시키기
다음과 같이 초깃값을 크게 주고, 변수를 감소시키면서 반복할 수 도 있다.
#include <stdio.h>
int main()
{
for (int i = 100; i > 0; i--)
{
printf("Hello, world! %d\n", i);
}
return 0;
}
초깃값은 100이고, 변화식에서 i--로 i를 1씩 감소시켰다. 조건식이 0이므로 i가 0이 되면 반복문이 끝나서 반복 횟수는 총 100번이다.
27.5 for 반복문과 세미클론
for 반복문도 if문과 마찬가지로 루프 선언문 뒤에 세미클론을 붙이면 안된다. 세미클론을 붙이면 for문과 아래 코드는 관계 없는 코드가 되어 반복을 하지 않는다.
27.6 for 반복문에서 중괄호 생략하기
다음과 같이 반복할 코드가 한 줄이면 중괄호를 생략할 수 있다.
#include <stdio.h>
int main()
{
for (int i = 0; i < 100; i++)
printf("Hello, world!\n");
return 0;
}
for 문에서 반복할 코드가 두 줄 이상일 때 중괄호를 생략하면 첫번째 줄만 반복하게 되므로 반복할 코드가 두 줄 이상이면 반드시 중괄호를 사용해야 한다.
27.7 입력한 횟수대로 반복하기
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int count;
scanf("%d", &count);
for (int i = 0; i < count; i++)
{
printf("Hello, world! %d\n", i);
}
return 0;
}
입력값을 받아서 count에 저장하고, i가 count보다 작을때까지 반복했다. 3을 입력했으므로 Hello world!는 3번 출력된다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int count;
scanf("%d", &count);
for (int i = count; i > 0; i--)
{
printf("Hello, world! %d\n", i);
}
return 0;
}
위 코드와 같이 입력값을 초기값으로 사용할 수도 있다.
또한 다음과 같이 i를 따로 선언하지 않고 입력받은 값을 그대로 사용할 수 도 있다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int count;
scanf("%d", &count);
for (; count > 0; count--)
{
printf("Hello, world! %d\n", count);
}
return 0;
}
for 반복문에 사용할 변수와 초기값이 준비되어 있으면 초깃값 부분은 생략할 수 있다. 그리고 두 조건식과 변화식을 입력받은 변수를 기준으로 만들면 된다.
27.8 for 반복문에서 변수 두 개 사용하기
#include <stdio.h>
int main()
{
for (int i = 0, j = 0; i < 10; i++, j += 2)
{
printf("i: %d, j: %d\n", i, j);
}
return 0;
}
위 코드는 for 초기식에서 변수를 두 개 선언하고, 변화식에서 i를 1씩 증가하고, j는 2씩 증가시켜 10번 반복 하도록 하였다. 변화식에서는 ++, -- 뿐 아니라 +=, -=, *=, /=등의 연산자도 사용할 수 있다.
27.9 for 반복문으로 무한 루프 만들기
#include <stdio.h>
int main()
{
for (;;)
{
printf("Hello, world!\n");
}
return 0;
}
for문에서 초기식, 조건식, 변화식을 모두 생략하면 무한루프 이다. 무한루프는 반복문이 종료되지 않기 때문에 ctrl+c를 눌러 강제로 종료시켜야 한다.
27.10 퀴즈
정답은 c,d 이다.
정답은 c, e이다.
정답은 b이다. for문 뒤에는 세미클론이 오면 안된다.
정답은 for(;;)이다.
27.11 연습문제 : for 반복문에서 변수 두 개 사용하기
정답은 inti=2,j=5;i<=32;i*=2,j-- 이다.
27.12 심사문제 : 알파벳 순서로 출력하기
정답은 다음 코드와 같다.
#include <stdio.h>
int main()
{
char c1;
scanf("%c", &c1);
for(int i = c1; i <= 122; i++)
{
printf("%c", i);
}
printf("\n");
return 0;
}
z는 아스키코드로 122이므로 i가 122가 될때까지 1씩 더하며 반복하며 i 값을 문자로 출력하였다.
Unit 28. while 반복문으로 Hello, world! 100번 출력하기
while 반복문은 괄호 안에 조건식만 들어가고, 초기식은 반복문 바깥에 있다. 중괄호 안에는 반복할 코드와 변화식이 들어간다.
초기식
while (조건식)
{
반복할 코드
변화식
}
초기식부터 시작하여 조건식을 판별한다. 조건식이 참이면 반복할 코드와 변화식을 수행한다. 그리고 다시 조건식을 판별하여 참이면 코드를 계속 반복하고, 거짓이면 반복문을 끝낸 뒤 다음 코드를 실행한다.
파이썬에서 while 반복문은 조건식 뒤에 클론(:)을 붙이고, 반복할 코드와 변화식은 들여쓰기로 구분한다.
28.1 while 반복문 사용하기
#include <stdio.h>
int main()
{
int i = 0;
while (i < 100)
{
printf("Hello, world!\n");
i++;
}
return 0;
}
반복문애 사용할 변수 i를 선언하고, 0으로 초기화 한다. while에는 조건식만 지정하고, 변화식은 중괄호 안에 지정한다. 중괄호 안에 변화식이 없다면 반복문이 끝나지 않는 무한루프 현상이 발생한다.
위 코드는 조건식이 i < 100 이기 때문에 i가 100이 되면 반복문이 종료되고, 변화식으로 i++를 사용했기 때문에 반복할 때 마다 i의 값이 1 씩 증가하여 0부터 99까지 100번 반복하게 된다.
28.2 초깃값을 1부터 시작하기
#include <stdio.h>
int main()
{
int i = 1;
while (i <= 100)
{
printf("Hello, world!\n");
i++;
}
return 0;
}
위 코드는 i를 1부터 시작했기 때문에 조건식을 i <= 100으로 지정하여 i가 101이 되면 반복문이 종료된다.
28.3 초깃값을 감소시키기
다음과 같이 초깃값을 크게 주고, 변수를 감소시키면서 반복문을 진행할 수 도 있다.
#include <stdio.h>
int main()
{
int i = 100;
while (i > 0)
{
printf("Hello, world! %d\n", i);
i--;
}
return 0;
}
i가 100이고, 변화식에서 i--를 지정하여 반복을 할 때마다 i의 값을 1씩 감소시킨다. i > 0 이 조건식이므로 100부터 1까지 100번 반복하고, i가 0이되면 반복문이 종료된다.
28.4 while 반복문과 세미클론
while문도 끝에 세미클론을 붙이면 안된다. while 끝에 세미클론을 붙이면 while문은 중괄호 안의 반복할 코드와 변화식과는 무관한 코드가 되기 때문에 중괄호 안에 있는 변화식도 실행할 수 없어서 무한루프 현상이 발생한다. 그러나 중괄호 안의 코드는 아직 실행 전이기에 출력되는값은 아무것도 없다.
28.5 입력한 횟수대로 반복하기
다음 코드와 같이 입력한 값을 반복문에 사용할 수 있다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int count;
scanf("%d", &count);
int i = 0;
while (i < count)
{
printf("Hello, world! %d\n", i);
i++;
}
return 0;
}
scanf 함수로 값을 입력 받아서 count변수에 저장하여 count에 들어간 값만큼 반복한다. 위에서는 3을 입력했기 때문에 3번 반복한다.
다음은 초깃값을 입력받은 것이다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int count;
scanf("%d", &count);
while (count > 0)
{
printf("Hello, world! %d\n", count);
count--;
}
return 0;
}
위 코드에서는 i를 선언하지 않고 count를 바로 사용하여 변화식은 count--로 반복할 때 마다 count의 값을 1씩 감소시킨다. count가 0이 되면 반복문이 끝난다.
28.6 반복 횟수가 정해지지 않은 경우
while 반복문은 반복 횟수가 정해지지 않았을 때 논리 조건에 따라 반복 여부를 결정할 때 주로 사용된다.
다음은 while반복문 안에서 무작위로 정수를 생성한 뒤 3이 나오면 반복을 끝낸다.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
srand(time(NULL));
int i = 0;
while (i != 3)
{
i = rand() % 10;
printf("%d\n", i);
}
return 0;
}
stdlib.h는 srand, rand 함수를 사용할 수 있는 헤더파일이고, time.h는 time함수를 사용할 수 있는 헤더파일이다. 무작위로 정수를 생성하려면 srand, rand, time 함수가 필요하다.
srand함수는 난수를 발생시킬 초기값 시드(seed)를 설정한다. 보통은 현재 시간값을 사용한다. rand함수는 난수를 발생시킨다. time 함수는 정수 형태로 된 현재 시간값을 반환한다.
다음 코드로 현재 시간값을 시드로 설정한다.
srand(time(NULL));
while 반복문에서 i != 3으로 i가 3이 아니면 계속 반복한다. rand() % 10은 rand의 반환값이 크기 때문에 10으로 나눠서 나머지로 1 ~ 9까지만 사용한다. 1 ~ 9까지의 숫자가 랜덤으로 출력되다가 3이 출력되면 반복문이 종료된다. while 반복문은 반복 횟수가 정해져 있지 않을 때 유용하다.
28.7 while 반복문으로 무한루프 만들기
#include <stdio.h>
int main()
{
while (1)
{
printf("Hello, world!\n");
}
return 0;
}
while 조건식에 1을 지정하면 무한루프가 만들어 진다. 조건식 자체가 없으므로 변화식도 필요 없다. 무한루프를 종료시키려면 ctrl + c를 누르면 된다.
다음과 같이 1 대신 불 값인 true를 넣기도 한다.
#include <stdio.h>
#include <stdbool.h>
int main()
{
while (true)
{
printf("Hello, world!\n");
}
return 0;
}
1 대신 true를 사용하는 것은 코드의 의도를 좀 더 명확하게 하기 위함이다.
28.8 while 반복문에서 중괄호 생략하기
#include <stdio.h>
int main()
{
while (1)
printf("Hello, world!\n");
return 0;
}
while 반복문에서 수행할 코드가 한줄이라면 중괄호를 생략할 수 있지만, while문은 보통 본체에 변화식이 함께 들어가므로 중괄호를 생략할 일은 많지 않다.
28.9 퀴즈
정답은 a, d, e이다.
정답은 b이다. i < 10 이여야 한다.
정답은 while(1)이다.
28.10 연습문제 : while 반복문 사용하기
i 변수는 8비트 이므로 i는 1에서 1칸씩 계속 왼쪽으로 시프트하다보면 1000 0000은 128 이고, 0000 0000이 되므로 i는 0일때 반복문을 종료시키면 된다. 답은 i != 0 이다.
2 % 3 처럼 나눌 수 없더라도 나머지는 구할 수 있다.(몫은 0, 나머지는 2) 6 % 3 처럼 완전히 나누어지면 나머지 연산의 값은 0이 나온다. 그래서 나머지 연산은 특정 수의 배수인지 확인할 때 주로 사용한다. 나머지 연산은 정수에서만 사용할 수 있고 실수에서 사용하면 컴파일 에러가 발생한다. 또한 5 % 0 처럼 0으로 나눈 나머지는 구할 수 없다.
실수끼리 나누었을 때 나머지는 math.h 헤더 파일의 fmod, fmodf, fmodl 함수를 이용하여 구할 수 있다. fmod는 double, fmodf는 float, fmodl은 long double자료형일 때 사용한다.
#include <stdio.h>
int main()
{
int num1 = 7;
num1 = num1 % 2;
printf("%d\n", num1);
return 0;
}
위 연산은 num1에서 2로 나눴을 때 나머지를 num1에 다시 저장한 것이다. 나머지 연산도 할당연산자 %=를 제공한다.
#include <stdio.h>
int main()
{
int num1 = 7;
num1 %= 2;
printf("%d\n", num1);
return 0;
}
a % b의 연산을 진행할 때 두 수의 부호가 다르면 앞(a)의 부호를 따른다.
15.3 퀴즈
정답은 c이다.
정답은 b이다.
정답은 d이다.
15.4 연습문제 : 3의 배수인지 확인하기
출력 결과가 둘다 0이 나와야 하므로 3과 나눴을 때 나머지 값이여야 한다. 정답은 3이다.
16.5 심사문제 : 정수의 각 자리수를 역순으로 출력하기
정답은 다음 코드와 같다.
#include <stdio.h>
int main()
{
int num;
scanf("%d", &num);
printf("%d ", num % 10);
num /= 10;
printf("%d ", num % 10);
num /= 10;
printf("%d ", num % 10);
num /= 10;
printf("%d ", num % 10);
num /= 10;
printf("%d ", num % 10);
return 0;
}
입력 값을 10으로 나눴을 때 나머지를 출력하고 10으로 나눠서 몫만 저장하는 것으로 각 자리를 1의자리부터 출력했다.
char과 int를 연산하면 char자료형의 크기보다 큰 숫자는 저장할 수 없기 때문에 위 코드의 경우 앞의 숫자들은 버려지고 28 + 2만 저장된다. 버려지는 자릿수를 2진수로 표현하면 다음과 같다.
16.3 퀴즈
정답은 d이다.
정답은 c이다.
16.4 연습문제 : 문자 출력하기
정답은 char이다.
16.5 심사문제 : 실수를 정수로 변환하기
정답은 다음 코드와 같다.
int num2 = num1;
printf("%d\n", num2);
Unit 17. if 조건문으로 특정 조건일 때 코드 실행하기
조건문을 사용하면 조건에 따라 다른 코드를 실행할 수 있다.
17.1 if 조건문 사용하기
if 조건문은 다음과 같이 괄호 안에 조건식을 지정하여 사용한다.
if (조건식)
{
코드
}
#include <stdio.h>
int main()
{
int num1 = 10;
if (num1 == 10)
{
printf("10입니다.\n");
}
return 0;
}
C에서는 조건문을 if() 형식으로 사용하며 괄호 안에는 조건식이 들어간다. {} 중괄호 안에는 조건식이 만족할 때 실행할 코드가 들어간다.
위 코드의 경우 num1이 10과 같은지 검사하고 있다. ==은 같다를 의미하며 수학의 =(등호)와 같은 의미이다. 위 코드에서 num1은 10으로 조건식을 만족하기 때문에 아래 코드를 실행한다.
파이썬의 if문은 조건식을 괄호로 감싸지 않아도 되고 조건식 끝에 :(클론)을 붙이며, 실행할 코드는 중괄호가 아닌 들여쓰기하여 작성한다는 점에서 문법상의 차이가 있다.
17.2 if 조건문과 세미클론
if 조건문을 사용할 때는 조건식 뒤에 ;(세미클론)을 붙이면 안된다.
if (num1 == 10);
{
printf("10입니다.\n");
}
위 코드와 같이 조건식 뒤에 세미클론을 붙이면 조건식 아래 코드는 조건식과 관계 없는 코드가 된다. 따라서 조건이 만족하지 않아도 항상 아래의 printf함수를 실행하게 된다.
if (num1 == 10);
printf("10입니다.\n");
실제로는 위와 같은 형태인 것이다. C에서는 코드를 중괄호로 감싸도 에러가 발생하지 않아서 if 조건문을 무시하고 코드가 동작한다.
17.3 if조건문에서 중괄호 생략하기
if 조건문에서 수행할 코드가 한 줄이면 다음과 같이 중괄호를 생략할 수도 있다.
#include <stdio.h>
int main()
{
int num1 = 10;
if (num1 == 10)
printf("10입니다.\n");
return 0;
}
위 코드와 같은 조건식도 뒤에 세미클론을 붙이면 안된다. 코드가 두 줄 이상일 때는 중괄호를 생략할 수 없다. 중괄호를 생략하게 되면 다음과 같이 조건이 일치하지 않아도 두번째 코드는 실행되게 된다.
#include <stdio.h>
int main()
{
int num1 = 5;
if (num1 == 10)
printf("if조건문\n");
printf("10입니다.\n");
return 0;
}
중괄호가 없을 때 if 조건식의 영향을 받는 것은 한 줄 뿐이다.
17.4 if 조건문에서 실수와 문자 비교하기
다음과 같이 정수가 아닌 실수와 문자도 비교할 수 있다.
#include <stdio.h>
int main()
{
float num1 = 0.1f;
char c1 = 'a';
if (num1 == 0.1f) // 실수 비교
printf("0.1입니다.\n");
if (c1 == 'a') // 문자 비교
printf("a입니다.\n");
if (c1 == 97) // 문자를 ASCII 코드로 비교
printf("a입니다.\n");
return 0;
}
비교할 변수와 값, 변수와 변수는 자료형을 맞춰줘야 한다. 실수는 연산 한 뒤 반올림 오차가 발생할 수 있으니 이를 감안하여 비교해야 한다. 문자는 위 코드와 같이 문자로 비교해도 되고, 아스키 코드값으로 비교해도 된다.
17.5 사용자가 입력한 값에 if 조건문 사용하기
다음과 같이 scanf 함수를 사용하여 사용자의 입력을 받고 입력 받은 값을 if문으로 비교할 수 있다.
조건에 만족하는 것이 있으면 해당하는 코드를 수행하고 만족하는 것이 없으면 아무 코드도 수행하지 않는다.
17.6 퀴즈
정답은 e이다.
정답은 a, c이다.
정답은 b, d이다.
17.7 연습문제 : if조건문 사용하기
c1에 'k'가 저장되어 있으므로 조건을 만족시키려면 정답은 == 'k' 이다.
17.8 심사문제 : 청소년 콘텐츠 관람 제한하기
정답은 다음 코드와 같다.
#include <stdio.h>
int main()
{
int age;
scanf("%d\n", &age);
if(age < 18)
{
printf("청소년 관람 불가\n");
}
return 0;
}
Unit 18. else를 사용하여 두 방향으로 분기하기
if는 분기를 위한 문법이다. 분기는 "둘 이상 갈라지다" 라는 뜻으로 프로그램의 흐름을 둘 이상으로 나누는 것을 말한다.
if에 else를 사용하면 프로그램을 두 방향으로 분기하여 조건식이 만족할 때와 만족하지 않을 때 각각 다른 코드를 실행할 수 있다.
18.1 else 사용하기
else는 if 조건문 뒤에 오며 단독으로 사용할 수는 없다. 사용 형태는 다음과 같다.
if (조건식)
{
코드1
}
else
{
코드2
}
#include <stdio.h>
int main()
{
int num1 = 20;
if (num1 == 10)
{
printf("10입니다.\n");
}
else
{
printf("10이 아닙니다.\n");
}
return 0;
}
위 코드에는 num1이 20이라 if 조건식을 만족하지 않기 때문에 else의 코드가 실행되었다.
if 조건식이 만족하면 참(True), 만족하지 않으면 거짓(False)이라고 부르는데, 조건식이 참이면 if의 코드가 실행되고, 거짓이면 else의 코드가 실행된다.
18.2 else와 세미클론
if 처럼 else도 뒤에 세미클론을 붙이지 않도록 주의해야 한다. 세미클론을 붙이면 그 아래 코드는 else와는 관계없는 코드가 되어 버려서 조건식 결과와는 관계 없이 항상 실행되게 된다.
18.3 else에서 중괄호 생략하기
else도 실행할 코드가 한 줄이면 중괄호를 생략할 수 있다.
#include <stdio.h>
int main()
{
int num1 = 20;
if (num1 == 10)
printf("10입니다.\n");
else
printf("10이 아닙니다.\n");
return 0;
}
if, else로 실행할 코드가 두 줄 이상이면 컴파일 에러가 발생한다.
#include <stdio.h>
int main()
{
int num1 = 20;
if (num1 == 10)
printf("1\n");
printf("10입니다.\n");
else
printf("1\n");
printf("10이 아닙니다.\n");
return 0;
}
따라서 두 줄 이상이면 반드시 중괄호로 묶어줘야 한다.
다음과 같이 if만 중괄호로 묶고 else의 코드는 묶지 않았을 경우 컴파일 에러는 발생하지 않지만 조건과 관계없이 두번째 이하의 코드는 항상 실행된다.
#include <stdio.h>
int main()
{
int num1 = 10;
if (num1 == 10)
{
printf("1\n");
printf("10입니다.\n");
}
else
printf("1\n");
printf("10이 아닙니다.\n");
return 0;
}
18.4 if 조건문의 동작 방식 알아보기
if는 0일때 거짓, 0이 아닐때 참으로 동작한다.
#include <stdio.h>
int main()
{
if (5)
printf("참\n");
else
printf("거짓\n");
return 0;
}
조건식에 5를 넣었는데도 참으로 동작한다. 0이아닌 모든 수를 넣으면 참으로 동작하며, 실수도 0.0f가 아닌 값들은 다 참이다.
#include <stdio.h>
int main()
{
if (0)
printf("참\n");
else
printf("거짓\n");
return 0;
}
0은 거짓이기 때문에 위와 같이 else 아래의 코드가 실행되어 거짓이 출력된다.
#include <stdio.h>
int main()
{
int num1 = 5;
if(num1 = 10)
printf("10입니다.\n");
return 0;
}
위 코드에서는 조건식에 =(할당연산자)를 사용하고 있다. 할당 연산자를 사용하게 되면 num1에 10이 할당되어 10이 되고, 10은 0이 아니라 참이기 때문에 if 아래의 코드가 실행된다. 이러한 실수는 컴파일 에러가 발생하지 않아 잡아내기 힘들기 때문에 주의해야 한다. 이런 실수를 줄이기 위해 다음과 같은 코드를 사용하기도 한다.
if (10 == num1)
printf("10입니다.\n");
조건식에서 변수를 뒤에 두면 10은 리터럴이기 때문에 값을 저장할 수 없어 만약 =(할당연산자)를 사용하게 되면 컴파일 에러가 발생한다.
18.5 조건식을 여러개 지정하기
if 조건문은 논리연산을 사용하여 조건식을 여러개 지정할 수 있다.
#include <stdio.h>
int main()
{
int num1 = 10;
int num2 = 20;
if (num1 == 10 && num2 == 20)
printf("참\n");
else
printf("거짓\n");
return 0;
}
&&은 논리 연산자이며 "두 식이 모두 만족할 때"를 의미한다. 위 코드에서는 num1이 10인것과 num2가 20인것이 모두 만족하여 참을 출력했다.
이 코드는 다음과 같이 if 조건문 안에 다시 if 조건문을 넣는 구조로도 똑같이 사용할 수 있다.
#include <stdio.h>
int main()
{
int num1 = 10;
int num2 = 20;
if (num1 == 10)
if(num2 == 20)
printf("참\n");
else
printf("거짓\n");
else
printf("거짓\n");
return 0;
}
if 안에 들어와서 다시 if를 만족해야 하므로 &&와 같은 동작을 한다. 안쪽에 있는 if 조건문이 수행할 코드가 한 줄일 경우 위와 같이 바깥쪽과 안쪽의 중괄호를 모두 생략할 수 있다.
#include <stdio.h>
int main()
{
int kor, eng, math, sci, avg;
scanf("%d %d %d %d", &kor, &eng, &math, &sci);
if(kor <= 100 && kor >= 0 && eng <= 100 && eng >= 0 && math <= 100 && math >= 0 && sci <= 100 && sci >= 0)
{
avg = (kor + eng + math + sci) / 4;
if(avg >= 85)
printf("합격\n");
else
printf("불합격\n");
}
else
{
printf("잘못된 점수\n");
}
return 0;
}
Unit 19. else if를 사용하여 여러 방향으로 분기하기
프로그램을 만들다 보면 두 가지 이상의 다양한 상황이 발생하며 이러한 상황들을 처리할 필요가 있다.
else if로 조건식을 여러개 지정하여 각 조건마다 다른 코드를 실행할 수 있다
파이썬에서는 else if 와 동일한 역할을 하는 키워드로 elif를 사용했다.
19.1 else if 사용하기
else if는 else인 상태에서 조건식을 지정할 때 사용하며 단독으로 사용할 수는 없다.
if (조건식)
{
코드1
}
else if (조건식)
{
코드2
}
#include <stdio.h>
int main()
{
int num1 = 20;
if (num1 == 10)
printf("10입니다.\n");
else if (num1 == 20)
printf("20입니다.\n");
return 0;
}
위 코드는 num1 이 10인지 검사하여 맞으면 10입니다. 를 출력하고, 거짓이면 그 뒤 else if의 조건식을 검사한다. else의 조건식이 참이면 20입니다.를 출력하고 거짓이면 아무것도 출력하지 않는다. else if에 조건식을 지정하지 않으면 컴파일 에러가 발생한다. else if도 끝에 세미클론을 붙이면 안된다.
19.2 if, else if, else를 모두 사용하기
else if는 else와 함께 사용할 수 있다.
#include <stdio.h>
int main()
{
int num1 = 30;
if (num1 == 10)
printf("10입니다.\n");
else if (num1 == 20)
printf("20입니다.\n");
else
printf("10도 20도 아니다.\n");
return 0;
}
위 코드는 if 조건식과 else if 조건식이 모두 거짓일 때 else의 코드가 실행된다.
if와 else는 한번만 사용할 수 있지만 else if는 여러번 사용할 수 있다.
else if 앞에 else가 오면 컴파일 에러가 발생한다.
19.3 퀴즈
정답은 b,c,e이다.
정답은 e이다. else if에는 조건식이 있어야 한다.
19.4 연습문제 : if, else if, else 모두 사용하기
c가 출력됬으므로 정답은 'c'이다.
19.5 심사문제 : 교통카드 시스템 만들기
정답은 다음 코드와 같다.
if(7 <= age && age <= 12)
balance -= 450;
else if(13 <= age && age <= 18)
balance -= 720;
else
balance -= 1200;
지수 표기법은 아주 큰 숫자나 아주 작은 숫자를 표현할 때 사용한다. 실수e + 지수는 실수 * 10의 거듭제곱이다. 또한 실수e - 지수는 실수 * 1/10의 거듭제곱이다.
long double의 크기는 운영체제의 플랫폼에 따라 다음과 같이 다르다.
컴퓨터에서는 값을 0과 1로 저장하기 때문에 실수고 0과 1로 저장해야 한다. 실수와 소수점을 2진수로 표현하는 방식을 부동소수점 표현 방식이라 한다. 부동소수점 방식은 자료형의 일정 부분을 비트 단위로 나누어 부호, 가수(significand), 기수(base), 지수(exponent)를 저장하여 실수를 표현한다.
다음과 같이 기수(n)을 지수(p)만큼 거듭제곱한 값을 가수(m)와 곱하는 방식이다. 가수는 2로 고정되어 있으며 2 자체는 따로 저장하지 않는다.
유효자릿수는 실수를 일정 자릿수만큼 표현할 수 있는 것이다. 만약 유효자릿수가 7이면 0.123456789는 반올림하여 0.123457로 표현된다. 정수부분 1자리와 소수부분 6자리로 7자리가 표현되는 것이다.
8.1 실수형 변수 선언하기
#include <stdio.h>
int main()
{
float num1 = 0.1f; // float는 숫자 뒤에 f를 붙임
double num2 = 3867.215820; // double는 숫자 뒤에 아무것도 붙이지 않음
long double num3 = 9.327513l; // long double는 숫자 뒤에 l을 붙임
printf("%f %f %Lf\n", num1, num2, num3);
return 0;
}
float에서 .1f, 1.f처럼 소숫점 앞 또는 뒤의 0은 생략할 수 있다.
실수를 출력할 때는 서식문자로 float와 double은 %f를, long double 는 %Lf를 사용한다.
상수는 반드시 선언과 동시에 초기화 해야 하며 초기화 하지 않으면 컴파일 에러를 발생한다. 상수도 변수처럼 이름으로 저장된 값을 출력할 수 있다.
#include <stdio.h>
int main()
{
const int con1 = 1;
con1 = 2;
printf("%d\n", con1);
return 0;
}
위와 같이 상수에 값을 할당하면 이후에 값을 변경할 수 없다. const는 다음과 같이 자료형 뒤에 붙여도 된다.
int const con1 = 1;
10.3 정수 리터럴 접미사 사용하기
리터럴의 크기를 명확하게 하려면 접미사를 사용할 수 있다.
#include <stdio.h>
int main()
{
printf("%ld\n", -10L); // long 크기의 정수 리터럴
printf("%lld\n", -1234567890123456789LL); // long long 크기의 정수 리터럴
printf("%u\n", 10U); // unsigned int 크기의 정수 리터럴
printf("%lu\n", 1234567890UL); // unsigned long 크기의 정수 리터럴
printf("%lu\n", 10UL); // unsigned long 크기의 정수 리터럴
printf("%llu\n", 1234567890123456789ULL); // unsigned long long 크기의 정수 리터럴
return 0;
}
리터럴은 접미사를 사용하여 정수 자료형으로 크기를 정하는 것 처럼 리터럴의 크기를 명시적으로 표시할 수 있다. 정수 리터럴 접미사 목록은 다음과 같다.
#include <stdio.h>
int main()
{
printf("%f\n", 0.1F); // float 크기의 실수 리터럴
printf("%f\n", 0.1); // double 크기의 실수 리터럴
printf("%Lf\n", 0.1L); // long double 크기의 실수 리터럴
printf("%f\n", 1.0e-5F); // float 크기의 실수 리터럴
printf("%f\n", 1.0e-5); // double 크기의 실수 리터럴
printf("%Lf\n", 1.0e-5l); // long double 크기의 실수 리터럴
return 0;
}
실수 리터럴 끝에 f를 붙이면 float크기가 되고, l을 붙이면 long double크기가 되고, 아무것도 붙이지 않으면 double크기가 된다. 대소문자는 구분하지 않는다.
10.5 퀴즈
정답은 d이다. 문자열은 큰따옴표로 감싸야 한다.
정답은 c이다.
정답은 e이다. ull은 unsigned long long 이다.
정답은 c이다. 접미사가 붙어있지 않으면 double이다.
10.6 연습문제 : 리터럴 사용하기
정답은 다음과 같다.
'e', "world!", 8, 4.9e-302L
10.7 연습문제 : 상수 사용하기
정답은 다음과 같다.
unsigned long long int con1 = 10ULL;
float con2 = 3.2f
char con3 = 't';
10.8 심사문제 : 리터럴 사용하기
정답은 다음과 같다.
076, -2147483647LL, 4.528172L
10.9 심사문제 : 상수 사용하기
정답은 다음과 같다.
long double con1 = -2.225074e-308
Unit 11. 입력값을 변수에 저장하기
C를 공부할 때는 콘솔에 글자를 입출력 하는데, 콘솔에 입력하는 방식을 표준 입력(standard input, stdin), 출력하는 방식을 표준 출력(standard output, stdout)이라고 한다.
11.1 정수 입력 받기
scanf 함수를 사용하여 표준 입력으로 사용자의 입력을 받아온다. scanf함수는 다음과 같이사용한다.
scanf(서식, 변수의주소);
int scanf(char const * const _Format, ...);
성공하면 가져온 값의 개수를 반환, 실패하면 EOF(-1)를 반환
파이썬에서는 input함수로 입력받으면 모두 문자열 이였는데, C에서는 입력값을 저장할 변수를 미리 만들어 두고, 그 변수에 입력값을 저장하기 때문에 입력받을 값의 자료형이 정해져 있다.
#define _CRT_SECURE_NO_WARNINGS // scanf 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
int main()
{
int num1;
printf("정수를 입력하세요 : ");
scanf("%d", &num1);
printf("%d\n", num1);
return 0;
}
scanf함수의 첫번째 인수로는 큰따옴표 안에 서식지정자를 넣어 입력받을 값의 형태를 설정한다. 두번째 인수에는 입력값을 저장할 변수를 넣는데, 변수 앞에 &를 붙여야 한다. 맨 첫 줄은 윈도우 Visual studio에서 발생하는 에러를 방지하는 것으로 입력값의 길이를 설정할 수 없어서 보안에 취약하니 사용하지 말라는 경고가 출력될수 있으니 방지하는 것이다. 첫줄을 사용하지 않아도 에러가 발생하지 않으려면 visual studio전용 함수인 입력값 길이를 저장할 수 있는 함수인 scanf_s 함수가 있다.
11.2 한 번에 정수 두 개 입력받기
#define _CRT_SECURE_NO_WARNINGS // scanf 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
int main()
{
int num1, num2;
printf("정수를 입력하세요 : ");
scanf("%d %d", &num1, &num2);
printf("%d %d\n", num1, num2);
return 0;
}
입력값 두개 이상을 받을 때는 입력값의 개수 만큼 서식지정자를 지정하고, 서식지정자 개수만큼 입력받을 변수를 넣어주면 된다. 입력 받을 때도 입력값은 공백(스페이스)로 구분한다.
11.3 실수 입력받기
#define _CRT_SECURE_NO_WARNINGS // scanf 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
int main()
{
float num1;
printf("실수를 입력하세요 : ");
scanf("%f", &num1);
printf("%f\n", num1);
return 0;
}
실수를 입력받았기 때문에 1을 입력해도 1.0이 출력된다.
실수를 입력받을 것이므로 변수를 float로 선언하고, 서식지정자를 %f를 사용한다.
만약 double로 선언했으면 scanf의 서식지정자로는 %lf를 사용해야 하며, long double로 선언했으면 %Lf를 사용해야 한다.
입력받고자 하는 자료형에 맞게 변수를 선언하고, 변수에 맞는 서식지정자를 사용하면 된다.
11.4 문자 입력받기
#define _CRT_SECURE_NO_WARNINGS // scanf 보안 경고로 인한 컴파일 에러 방지
#include <stdio.h>
int main()
{
char c1;
printf("문자를 입력하세요 : ");
scanf("%c", &c1);
printf("%c\n", c1);
return 0;
}
이와 같이 증감 연산자를 변수 뒤에 사용하는 것을 후위 연산이라고 하며 다른 변수에 할당할 때는 현재 변수의 값을 다른 변수에 할당한 후 증감연산자가 수행되어 변수의 값이 1 증가, 감소 된다. 따라서 num3과 num4에는 num1과 num2의 처음 값인 2가 저장되고 이후 증감 연산이 수행되어 num1과 num2에는 각각 3과 1이 저장되어 있을 것이다.
증감 연산자를 변수 앞에 사용하면 다음과 같다.
#include <stdio.h>
int main()
{
int num1 = 2;
int num2 = 2;
int num3;
int num4;
num3 = ++num1;
num4 = --num2;
printf("%d %d\n", num3, num4);
return 0;
}
컴퓨터 과학이 사용되는 범위가 애니메이션, 의학, 패션, 기계공학까지 점점 늘어나고 있고 프로그래밍 활용 분야도 마케팅등 점점 넓어지고 있다. 프로그래밍으로 문제를 해결할 수 있는 부분이 있다면 어느 분야에서든 활용할 수 있다.
1.1 컴퓨터와 프로그램
프로그램은 사람이 원하는 작업을 처리해주는 도구이다. 그러나 기존에 있는 도구로는 원하는 작업을 처리할 수 없을 때 프로그래밍을 통해 직접 문제를 해결하는 프로그램을 만들 수 있다.
1.2 문제 해결을 위한 과학적 사고
복잡한 문제 하나를 해결하기 위해서는 복잡한 문제를 작은 문제들로 작은 문제들을 하나씩 접근하여 해결한다. 다음과 같이 사진을 특수문자로 변환하는 것을 아스키 아트라 한다.
사진을 아스키 아트로 변환하는 것을 작은 문제로 나누어 보면 이미지의 각 픽셀에 대한 정보를 얻어오고, 픽셀의 색상 정보를 얻어오고, 그에 맞는 특수 문자로 출력하는 작은 문제로 나눌 수 있다. 이것처럼 현실 세계의 문제를 분석하여 해결책을 찾는 과학적 사고법을 컴퓨테이셔널 씽킹 이라 하고, 이렇게 설계한 해결책을 컴퓨터 명령어로 작성하는 것을 컴퓨터 프로그래밍이라 한다.
작은 문제로 분해하고, 문제의 패턴을 발견하고, 어떤 데이터를 이용할지 결정하고, 문제를 일반화하고 모델링할 수 있는지를 찾는 과정이다.
아스키 아트로 변환하는 과정을 패턴, 데이터, 일반화와 모델링 과정으로 분류하면 다음과 같다.
1972년 켄 톰슨과 데니스 리치가 유닉스 운영체제를 만들기 위해 고안한 프로그래밍 언어이다.
켄 톰슨이 BCPL언어를 고쳐 B언어를 개발하였고, 데니스 리치가 B언어를 개선하여 C언어를 개발했다. C언어는 이후의 프로그래밍 언어에 직간접적으로 많은 영향을 주었다.
윈도우, 리눅스, osX등 운영체제의 핵심 요소인 커널도 C언어로 만들어져 있다.
C는 데이터베이스, 가전제품의 컴퓨터, IoT를 만드는데도 사용된다.
메모리와 하드웨어를 직접 제어할 수 있는 언어 이기도 하다.
1.5 코딩도장을 학습하는 방법
파이썬 코딩도장을 통해 학습하는 방법을 알고 있기에 생략한다.
Unit 2. Visual Studio 설치하기
컴파일러로 visual studio code를 사용할 것이고, 설치되어 있기에 생략한다.
Unit 3. Hello, world!로 시작하기
3.1 새 프로젝트 만들기
3.2 프로젝트에 C언어 소스파일 추가하기
다음과 같이 작업공간으로 사용할 폴더를 만들어 hello.c 소스파일을 만들었다.
3.3 Hello, world! 출력하기
소스 파일에 다음과 같이 입력한다.
#include <stdio.h>
int main()
{
printf("Hello world!\n");
return 0;
}
Ctrl + F5 키를 누르면 소스파일을 컴파일 하여 실행 파일을 만들 수 있다. 또한 vscode에서 code runner 확장 프로그램을 이용하면 우측 상단의 재생 버튼을 눌러 바로 컴파일 하여 실행할 수 있다.
다음과 같이 터미널에서도 실행 파일을 실행할 수 있다.
위의 소스코드를 보면 printf가 Hello, world!를 출력한다. " "(큰따옴표)로 감싼 부분을 문자열이라 하고, printf()는 화면에 문자열을 출력하는 함수이다. main, printf등 뒤에 괄호가 붙은 단어를 함수라 한다.
printf()괄호 뒤에 ;(세미클론)을 붙이면 printf함수가 실행된다. 함수가 실행되는 것을 함수를 호출(call)한다고 하기도 한다.
Hello, world! 뒤에 붙은 \n은 출력되지 않는다. \n은 제어문자로 화면에 직접 표시되지 않고 문자열을 다음 줄에서 출력되도록 하는 엔터키와 같은 역할을 한다. 파이썬의 print함수는 실행하고 줄바꿈을 하는 것이 기본 설정이라 줄바꿈이 필요 없을 때 따로 지정했어야 했는데 C의 printf함수는 실행하고 줄바꿈을 하지 않아서 줄바꿈을 원하면 \n 제어문자를 넣어야 한다.
소스 코드의 첫줄에 있는 #include는 헤더파일을 포함하는 문법이다. printf 함수를 사용하려면 stdio.h 헤더 파일이 필요하다.
파이썬에서는 소스 파일에 바로 작성하면 됬지만, C의 경우 사용하려는 함수마다 그 함수를 포함하고 있는 헤더파일을 모두 지정해 줘야 한다.
main() 함수는 C로 프로그램을 만들었을 때 프로그램에서 가장 먼저 실행되는 함수이다. 소스에서 main함수가 없다면 컴파일 되지 않기 때문에 main함수는 반드시 있어야 한다.
main 함수의 마지막에 return 0; 이 있다. 함수는 반환값을 함수 바깥으로 전달할 수 있다. main함수도 함수이기 때문에 반환 값을 반환할 수 있다.
C에서 중괄호는 코드의 범위를 나타낸다. 다음 코드의 경우 중괄호로 묶인 부분은 main 함수에 소속된 코드이다.
int main()
{
printf("Hello, world!\n");
return 0;
}
중괄호는 다음과 같이 if문이나 for문 등의 키워드가 영향을 미치는 영역을 묶을 때도 사용한다.
if (a > 10)
{
printf("a");
}
for (int i = 0; i < 10; i++)
{
printf("Hello, world!\n");
}
또한 다음과 같이 구조체(공용체, 열거형)를 정의할 때도 사용한다.
struct Hello {
int a;
int b;
};
중괄호 뒤에는 문법에 따라 세미클론을 붙일 때도 있고, 안붙일 때도 있다.
4.4 들여쓰기
C에서는 키워드에 따라 들여쓰기를 사용한다. 보통 {(여는 중괄호)가 시작될 때 들여쓰기를 한다.
다음과 같이 코드가 한 줄이라 중괄호를 생략할 때도 들여쓰기는 하는 것이 코드를 쉽게 알아보기에 좋다.
들여쓰기 방법은 공백 두칸, 4칸, 탭 등 여러 방법이 있다.
들여쓰기 방법은 여러개지만, 다른 사람이 만든 소스코드를 수정할때는 기존의 들여쓰기 규칙을 따르는 것이 좋다.
Unit 5. 변수 만들기
변수는 자료형 뒤에 변수 이름을 선언하는 방식으로 만들 수 있다. 정수를 저장하는 변수를 만드려면 다음과 같이 선언한다.
int num1;
int 는 정수를 뜻하는 integer의 축약형이며 변수 이름은 다음의 규칙을 따르고, 원하는 대로 지으면 된다.
영문 문자와 숫자를 사용할 수 있습니다.
대소문자를 구분합니다.
문자부터 시작해야 하며 숫자부터 시작하면 안 됩니다.
_ (밑줄 문자)로 시작할 수 있습니다.
C 언어의 키워드(int,short,long,float,void,if,for,while,switch등)는 사용할 수 없습니다.
변수를 선언하고 맨 뒤에는 반드시 세미클론(;)을 붙여야 한다.
C언어는 파이썬과 달리 변수에 저장할 값에 따라 맞는 자료형을 지정해줘야 한다. C에서 사용할 수 있는 자료형의 종류는 다음과 같다.
char,short,int,long: 정수(저장할 수 있는 크기가 다릅니다)
float,double: 실수
void: 형태가 없는 자료형(포인터를 사용할 때, 함수의 반환값을 표현할 때 등 다양하게 사용됩니다)
5.1 변수를 만들고 값 저장하기
#include <stdio.h>
int main()
{
int num1; // 정수형 변수 선언
int num2;
int num3;
num1 = 10; // 변수에 값 할당
num2 = 20;
num3 = 30;
printf("%d %d %d\n", num1, num2, num3); // 변수에 저장한 값을 %d 로 출력
return 0;
}
변수에 값을 할당(저장)할 때는 =(등호)를 사용한다. 변수에 값을 할당하고 출력할 때는 %d 서식지정자를 사용한다. %d는 10진수를 출력할 때 사용하는 서식지정자 이다. 서식지정자 %d에 각 변수가 대응되어 3개의 변수에 저장된 3개의 값이 출력되었다.
5.2 변수 여러 개를 한 번에 선언하기
#include <stdio.h>
int main()
{
int num1, num2, num3; // 변수를 콤마로 구분하여 여러개를 선언
num1 = 10; // 변수에 값 할당
num2 = 20;
num3 = 30;
printf("%d %d %d\n", num1, num2, num3); // 변수에 저장한 값을 %d 로 출력
return 0;
}
위 코드와 같이 int를 한 번만 사용한 뒤 변수를 콤마로 구분하여 여러개의 변수를 선언할 수 있다. 자료형이 같을 때만 가능하며, 자료형이 다르다면 따로 선언해야 한다.
5.3 변수를 선언하면서 초기화하기
#include <stdio.h>
int main()
{
int num1 = 10; // 변수를 선언하면서 값 할당(초기화)
int num2 = 20, num3 =30; // 변수 여러 개를 선언하면서 값 할당(초기화)
printf("%d %d %d\n", num1, num2, num3); // 변수에 저장한 값을 %d 로 출력
return 0;
}
위 코드처럼 변수를 선언하면서 =로 값을 할당하여 초기화 할 수 있다. 또한 변수 여러개를 선언하면서 값을 초기화 할 수 도 있다. 각 코드의 마지막에는 반드시 세미클론을 붙여야 한다.
5.4 퀴즈
정답은 c이다.
정답은 c, e, f, h 이다.
정답은 d이다.
정답은 b이다.
5.5 연습문제 : 변수 여러 개를 선언하면서 값 초기화 하기
변수 이름 num1, num2, num3 각각에 값 10, 20, 30을 할당해야 하므로 정답은 다음 코드와 같다.
int num1 = 10, num2 = 20, num3 = 30;
5.6 심사문제 : 변수를 선언하고 값 할당하기
num1 변수는 선언되어 있기 때문에 정답은 다음 코드와 같다.
num1 = 10;
int num2 = 20, num3 = 30;
Unit 6. 디버거 사용하기
버그는 프로그램이 의도치 않은 동작을 일으키는 것을 말한다. 디버거는 버그를 제거하는 도구 라는 뜻으로 프로그램의 내부 상황을 쉽게 파악할 수 있어 버그(문제점)를 찾는데 도움을 준다.
6.1 중단점 사용하기
중단점은 브레이크 포인트(Break point)라고도 부르는데, 소스코드의 특정 지점에서 프로그램의 실행을 멈추는데 사용한다. 소스코드는 다음 코드를 사용한다.
#include <stdio.h>
int main()
{
int num1 = 10; // 변수를 선언하면서 값 할당(초기화)
int num2 = 20, num3 =30; // 변수 여러 개를 선언하면서 값 할당(초기화)
printf("%d %d %d\n", num1, num2, num3); // 변수에 저장한 값을 %d 로 출력
printf("%d\n", num2);
return 0;
}
{ (여는 중괄호)가 있는 줄의 줄 번호 왼쪽을 클릭하면 빨간 원이 생긴다.
빨간색 원은 중단점을 의미하며 이곳에서 실행을 멈추라는 의미이다.
중단점을 삽입하고, F5 키를 눌러서 디버깅을 시작한다.
그러면 노란색 화살표에 빨간 원이 들어가며 그 줄에서 프로그램이 중단된다.
4번째 줄까지 실행 했고 5번째 줄을 실행할 차례라는 의미이다. 코드를 한줄 한줄 실행할 때는 F10 키를 누르면 된다. F10을 누르면 노란색이 한 줄씩 내려가면서 코드가 한줄 한줄 실행된다.
지금 상태는 num1, num2, num3 변수가 선언되며 값이 할당되고, 아직 출력은 하지 않은 상태이다. 지금 상태에서 DEBUG CONSOLE을 확인해보면 아무것도 출력되지 않는다.
여기서 F10을 한번 더 누르면 8번째 줄이 실행되여 3개의 변수가 출력된다.
한번 더 F10을 누르면 다음줄도 실행된다.
디버깅을 끝내려면 Shift + F5를 누르면 된다.
Visual Studio Code의 디버거 단축키는 다음과 같다.
중단점 삽입/삭제: F9
디버깅 시작: F5
디버깅 중지: Shift+F5
프로시저 단위 실행: F10
한 단계씩 코드 실행: F11
함수 안으로 들어갈 때 사용하는 단축키이다.
Unit 7. 정수 자료형 사용하기
정수 자료형은 크게 char과 int가 있고, 앞에 부호 키워드(signed, unsigned)와 크기 키워드(short, long)을 붙여서 특성을 정의할 수 있다.
signed는 부호가 있는 정수를 표현하며 보통 생략한다. unsigned는 부호가 없는 정수를 표현하여 값은 0부터 시작한다.
C에서는 정수 자료형이 char ,int에 여러 키워드를 조합하여 여러가지가 있지만, 파이썬의 정수 자료형은 int 하나이다.
다음은 정수 자료형 키워드를 조합했을 때 자료형의 크기와 저장할 수 있는 값의 범위이다.
정수 자료형의 크기와 범위를 그림으로 표현하면 다음과 같다. signed와 unsigned는 범위가 다를 뿐 크기는 같다.
7.1 정수형 변수 선언하기
#include <stdio.h>
int main()
{
char num1 = -10; // 1바이트 부호 있는 정수
short num2 = 30000; // 2바이트 부호 있는 정수
int num3 = -1234567890; // 4바이트 부호 있는 정수
long num4 = 1234567890; // 4바이트 부호 있는 정수
long long num5 = -1234567890123456789; // 8바이트 부호 있는 정수
printf("%d %d %d %ld %lld\n", num1, num2, num3, num4, num5);
return 0;
}
위 코드는 부호있는 정수를 만들어 값을 할당한 것이다. 부호있는 정수는 보통 signed를 생략한다. short 와 long은 int 를 생략한다. char은 character의 약어로 문자를 뜻하지만 기본적으로는 정수형이다.
printf함수에서 char, short, int는 %d 서식지정자를 사용하지만 long는 %ld, long long는 %lld 서식지정자를 사용한다.
부호 없는 정수 자료형은 다음과 같다.
#include <stdio.h>
int main()
{
unsigned char num1 = 200; // 1바이트 부호 없는 정수
unsigned short num2 = 60000; // 2바이트 부호 없는 정수
unsigned int num3 = 4123456789; // 4바이트 부호 없는 정수
unsigned long num4 = 4123456789; // 4바이트 부호 없는 정수
unsigned long long num5 = 12345678901234567890; // 8바이트 부호 없는 정수
printf("%u %u %u %lu %llu\n", num1, num2, num3, num4, num5);
return 0;
}
부호 없는 정수 자료형은 앞에 unsigned 키워드를 붙여주면 된다.
printf함수에서 서식지정자로 %d를 사용해도 되지만, unsigned키워드가 붙은 정수는 %u, %lu, %llu를 이용하여 출력해야 한다.
num1 은 char 자료형 범위의 최대값인 127 보다 큰 128을 저장했고, num2는 unsigned char 자료형 범위의 최대값인 255보다 큰 256을 저장했다. 실제 출력에서는 -128과 0이 나왔다. 저장할 수 있는 최대값의 범위를 넘어서서 오버플로우가 발생하여 다시 최솟값부터 시작하게 되므로 -128과 0이 나오는 것이다. 다른 자료형들도 저장할 수 있는 최대값의 범위를 넘어서면 오버플로우가 발생해 최솟값부터 다시 시작한다.
반대로 저장할 수 있는 최솟값보다 작은 값을 저장하면 언더플로우가 발생하여 최댓값에서 다시 시작하게 된다.
파이썬에서는 int 자료형이 숫자의 범위를 제한하고 있지 않아서 오버플로우가 발생하지 않을 것이다.
7.3 자료형 크기 구하기
자료형의 크기를 바이트 단위로 구할 때는 sizeof 연산자를 사용한다. sizeof 연산자는 다음과 같은 형식으로 사용한다.
sizeof 표현식
sizeof(자료형)
sizeof(표현식)
#include <stdio.h>
int main()
{
int num1 = 0;
int size;
size = sizeof num1;
printf("num1의 크기 : %d\n", size);
return 0;
}
변수 num1의 자료형 크기를 구한 것이다. 표현식은 변수, 상수, 배열 등 프로그래머가 만들어낸 요소를 뜻한다. 정수 int형 변수 num1의 자료형을 구했기 때문에 4가 출력됬다.
다음과 같이 sizeof(자료형)의 방식과 sizeof(표현식)의 방식으로도 사용할 수 있다.
int size1;
int size2;
int num1 = 0;
size1 = sizeof(int);
size2 = sizeof(num1);
각 정수 자료형의 크기를 구해보면 다음과 같다.
#include <stdio.h>
int main()
{
printf("char : %d, short : %d, int : %d, long : %d, long long : %d\n",
sizeof(char),
sizeof(short),
sizeof(int),
sizeof(long),
sizeof(long long)
);
return 0;
}
소스코드에서 정수의 최솟값을 표현하려면 limits.h 헤더 파일을 사용해야 한다. limits.h는 자료형의 최솟값과 최댓값이 정의된 헤더파일이다.
#include <stdio.h>
#include <limits.h>
int main()
{
char num1 = CHAR_MIN; // char 최솟값
short num2 = SHRT_MIN; // short 최솟값
int num3 = INT_MIN; // int 최솟값
long num4 = LONG_MIN; // long 최솟값
long long num5 = LLONG_MIN; // long long 최솟값
printf("%d %d %d %ld %lld\n", num1, num2, num3, num4, num5);
return 0;
}
limits.h 파일에는 다음과 같이 정수 자료형들의 최솟값과 최댓값이 정의되어 있다.
다음과 같이 limits.h에 정의된 최댓값을 넘어서도 오버플로우가 발생하여 최솟값부터 다시 시작하며, 최솟값보다 작아지면 언더플로우가 발생하여 최댓값이 출력되게 된다.
#include <stdio.h>
#include <limits.h>
int main()
{
// 부호 있는 정수 오버플로우
char num1 = CHAR_MAX + 1;
short num2 = SHRT_MAX + 1;
int num3 = INT_MAX + 1;
long long num4 = LLONG_MAX + 1;
printf("%d %d %d %lld\n", num1, num2, num3, num4);
// 부호 없는 정수 오버플로우
unsigned char num5 = UCHAR_MAX + 1;
unsigned short num6 = USHRT_MAX + 1;
unsigned int num7 = UINT_MAX + 1;
unsigned long long num8 = ULLONG_MAX +1;
printf("%u %u %u %llu\n", num5, num6, num7, num8);
// 부호 있는 정수 언더플로우
char num9 = CHAR_MIN - 1;
short num10 = SHRT_MIN - 1;
int num11 = INT_MIN - 1;
long long num12 = LLONG_MIN - 1;
printf("%d %d %d %lld\n", num9, num10, num11, num12);
// 부호 없는 정수 언더플로우
unsigned char num13 = 0 - 1;
unsigned short num14 = 0 - 1;
unsigned int num15 = 0 - 1;
unsigned long long num16 = 0 - 1;
printf("%u %u %u %llu\n", num13, num14, num15, num16);
return 0;
}
값을 계산하다가 오버플로우 또는 언더플로우 현상이 발생할 경우 의도치 않은 결과가 나올 수 있기 때문에 프로그래밍 할 때는 값의 범위를 항상 주의해야 한다.
7.5 크기가 표시된 정수 자료형 사용하기
C99 표준부터 stdint.h 헤더 파일이 추가되면서 크기가 표시된 정수 자료형을 사용할 수 있게 되었다.
#include <stdio.h>
#include <stdint.h>
int main()
{
int8_t num1 = -128; // 8비트(1바이트) 크기의 부호 있는 정수
int16_t num2 = 32767; // 16비트(2바이트) 크기의 부호 있는 정수
int32_t num3 = 2147483647; // 32비트(4바이트) 크기의 부호 있는 정수
int64_t num4 = 9223372036854775807; // 64비트(8바이트) 크기의 부호 있는 정수
printf("%d %d %d %lld\n", num1, num2, num3, num4);
uint8_t num5 = 255; // 8비트(1바이트) 크기의 부호 없는 정수
uint16_t num6 = 65535; // 16비트(2바이트) 크기의 부호 없는 정수
uint32_t num7 = 4294967295; // 32비트(4바이트) 크기의 부호 없는 정수
uint64_t num8 = 18446744073709551615; // 64비트(8바이트) 크기의 부호 없는 정수
printf("%u %u %u %llu\n", num5, num6, num7, num8);
return 0;
}
출력할 때는 int64_t와 uint64_t만 %lld와 %llu로 출력하고 나머지는 %d와 %u로 출력한다.
stdint의 최소 최댓값은 헤더 파일 안에 정의 되어 있어 limits.h헤더파일을 따로 사용하지 않아도 된다.
부호 있는 정수(signed) 최솟값:INT8_MIN,INT16_MIN,INT32_MIN,INT64_MIN
부호 있는 정수 최댓값:INT8_MAX,INT16_MAX,INT32_MAX,INT64_MAX
부호 없는 정수(unsigned) 최솟값: 0
부호 없는 정수 최댓값:UINT8_MAX,UINT16_MAX,UINT32_MAX,UINT64_MAX
7.6 퀴즈
정답은 c이다. int는 4바이트이다.
정답은 d이다. unsigned 자료형의 최솟값은 0이다.
정답은 b이다.
정답은 e이다. 자료형으로 크기를 구하려면 괄호에 넣어야 한다.
정답은 c이다.
정답은 e이다.
7.7 연습문제 : 정수형 변수 선언과 오버플로우
정답은 다음 코드와 같다.
unsigned char
unsigned short
long long
7.8 연습문제 : 자료형 크기 구하기
sizeof(int)는 4이기 때문에 sizeof(num1)과 sizeof(num2)의 합은 10이 되어야 하므로 정답은 다음과 같다.
short
long long
7.9 연습문제 : 최댓값 표현하기
정답은 다음과 같다.
#include <limits.h>
SHRT_MAX
LLONG_MAX
7.10 연습문제 : 크기가 표시된 자료형 사용하기
정답은 다음과 같다.
#include <stdint.h>
uint64_t
7.11 심사문제 : 정수형 변수 선언과 오버플로우
정답은 다음코드와 같다.
unsigned short num1;
unsigned int num2;
char num3;
7.12 심사문제 : 자료형 크기 구하기
sizeof(long long)의 크기는 8바이트 이므로 num1과 num2의 크기가 합쳐서 3이 되야 하므로 정답은 다음 코드와 같다.