C언어 시작하기(기초2)-다양한 연산자들, scanf 함수, 상수와 기본 자료형, 제어문
안녕하세요. YKK 입니다.
어제에 이어 C언어 워크숍에서 다룬 내용을 복습해 보겠습니다.
마지막으로 다뤘던 증가 감소 연산자에 이어 다양한 연산자들과 scanf 함수, 기본적인 자료형, 제어문까지 다뤄보겠습니다.
*다양한 연산자들
-관계 연산자(비교 연산자): 두 피연산자의 관계를 따지는 연산자이며, 논리적으로 참일 때는 1을 반환/거짓일 때는 0을 반환합니다. 종류로는 '>, <, ==, !=, <=, >='이 있는데 파이썬을 공부하신 분이라면 어려움 없이 넘기실 수 있을 거라 생각합니다.
-논리 연산자: and, or, not을 표현하는 연산자이며, 관계연산자와 동일하게 참일 경우 1을, 거짓일 경우 0을 반환합니다.
a && b//a와 b 둘 다 참이면 참을 반환(and)
a || b//둘 중 하나라도 참이면 참을 반환(or)
!a // 참이면 거짓을, 거짓이면 참을 반환(not)
논리 연산자 활용의 예시를 보겠습니다.
int main(void){
int a = 3 > 4;
int b = ((3 <= 4) && (3 == 4));
int c = ((3 != 4) || (3> 4));
printf("a = %d, b = %d, c = %d", a, b, c)
}
a는 '3이 4보다 크다'는 거짓이기 때문에 0의 값을 갖습니다.
b는 '4가 3보다 크거나 같다'는 참이기에 1, '3은 4와 같다'는 거짓이기 때문에 0이지만, 논리연산자 '&&'로 인해 두 값이 모두 참이어야 참이 되기 때문에, b는 거짓, 0의 값을 갖습니다.
c는 '3은 4와 다르다'는 참으로 1, '3이 4보다 크다'는 거짓이기 때문에 0이지만, 논리연산자 '||'로 인해 둘 중에 하나만 참이면 참이 됩니다. 그래서 c는 1의 값을 갖습니다.
▼ 표 25‑1 C 언어 연산자 우선순위
1 |
x++ x-- ( ) [ ] . -> (자료형){값} |
증가 연산자(뒤, 후위) 감소 연산자(뒤, 후위) 함수 호출 배열 첨자 구조체/공용체 멤버 접근 포인터로 구조체/공용체 멤버 접근 복합 리터럴 |
→ |
2 |
++x --x +x -x ! ~ (자료형) *x &x sizeof |
증가 연산자(앞, 전위) 감소 연산자(앞, 전위) 단항 덧셈(양의 부호) 단항 뺄셈(음의 부호) 논리 NOT 비트 NOT 자료형 캐스팅(자료형 변환) 포인터 x 역참조 x의 주소 자료형의 크기 |
← |
3 |
* / % |
곱셈 나눗셈 나머지 |
→ |
4 |
+ - |
덧셈 뺄셈 |
→ |
5 |
<< >> |
비트를 왼쪽으로 시프트 비트를 오른쪽으로 시프트 |
→ |
6 |
< <= > >= |
작음 작거나 같음 큼 크거나 같음 |
→ |
7 |
== != |
같음 다름 |
→ |
8 | & | 비트 AND | → |
9 | ^ | 비트 XOR | → |
10 | | | 비트 OR | → |
11 | && | 논리 AND | → |
12 | || | 논리 OR | → |
13 | ? : | 삼항 연산자 | ← |
14 |
= += -= *= /= %= <<= >>= &= ^= |= |
할당 덧셈 후 할당 뺄셈 후 할당 곱셈 후 할당 나눗셈 후 할당 나머지 연산 후 할당 비트를 왼쪽으로 시프트한 후 할당 비트를 오른쪽으로 시프트한 후 할당 비트 AND 연산 후 할당 비트 XOR 연산 후 할당 비트 OR 연산 후 할당 |
← |
15 | , | 쉼표(콤마) 연산자 | → |
위 연산자 우선순위 표를 참고하여 공부하시면 됩니다.
출처: https://dojang.io/mod/page/view.php?id=188
C 언어 코딩 도장: 25.0 연산자 우선순위 알아보기
수학에서 35 + 1 * 2의 결과는 무엇일까요? 이 식에서는 덧셈 다음에 곱셈으로 나와있지만 곱셈이 덧셈보다 우선순위가 높으므로 72가 아니라 37이 됩니다. 즉 1 * 2를 먼저 계산해서 2가 나오고, 앞
dojang.io
*scanf() 함수의 이해
-모양: int scanf("입력 받을 문자의 서식", 변수의 주소)
scanf 함수 또한 헤더파일 <stdio.h>에 속하며, 형식화된 데이터를 읽는 함수입니다. 첫 번째 매개변수의 형식에 맞게 데이터를 입력 받아서 두 번째 세번째... N번째 매개변수가 가리키는 위치에 데이터를 저장하는 방식입니다.
첫 번째 매개변수에는 어떤 종류(정수, 문자, 실수, 문자열 등)를 입력받을 것인지, 어떤 식으로 사용자로부터 입력을 받아 저장할 것인지를 정하는 부분입니다.
예를 들어 scanf(%d, &num)은 변수 num에 숫자 1개를 입력받는 것을 의미합니다.
만약 1개가 2개의 변수에 1개씩 숫자를 입력 받고 싶다면
scanf("%d, %d", &num1, &num2) 또는 scanf("%d %d", &num1, &num2) 이런 식으로 입력 받을 수 있습니다.
전자의 경우 1, 2 이런 식으로 입력을 해야 하고 후자의 경우는 1 2 이런 식으로 콤마를 생략하고 입력 가능합니다.
첫 번째 인자로 들어오는 입력받을 문자의 서식에서 특별한 기능을 하는 문자들이 있습니다. 이 문자들은 어떤 형태로 입력을 받을 것인지 구분하고, 메모리에 어느 정도 용량을 확보할지, 어떤 타입의 변수를 넣을지, scanf 함수 어디까지 그 값을 인식할 것인지 등등 고려해야 할 것이 많기 때문에 미리 타입을 정하는 것이라고 보면 되겠습니다.
%d : int 타입 정수형
%c : char 타입 문자형
%s : char* 타입 문자열
%lf : double 타입 실수
%f : float 타입 실수
(출처: https://blockdmask.tistory.com/471 [개발자 지망생:티스토리])
두 번째~N 번째 매개변수는 수가 정해져 있지 않습니다. 첫 번째 매개변수에서 이미 몇 개의 데이터를 받을 지 정하기 때문에 첫 번째 매개변수에 따라 뒤의 매개변수의 가 전해지기 때문입니다.
또한 scanf 함수에서 두 번째 인자부터는 &를 붙여 해당 변수의 주소를 가리키게 되는데요. int a;라는 변수가 선언되었다면, &a는 변수 a의 주소를 가리킵니다. scanf 함수에서 전달되는 인자에 사용자가 입력한 값을 넣어야 하는데, 그 인자가 'call by value'라면 복사만 될 뿐 인자로 넣어준 변수에는 값이 들어오지 않기 때문에 &를 붙여 'call by reference'를 이용하기 위함이라고 보시면 됩니다.
예시를 통해 알아보겠습니다.
#include <stdio.h>
int main(void)
{
int a;
printf("a를 입력하세요: ");
scanf("%d", &a);//사용자에게 입력받은 값을 변수 a에 저장
int b;
printf("b를 입력하세요: ");
scanf("%d", &b);//사용자에게 입력받은 값을 변수 b에 저장
printf("a는 %d이 입력되었고, b는 %d가 입력되었습니다.", a, b);//a와 b에 저장된 값을 출력
}
*C 언어가 제공하는 기본 자료형
C언어의 기본 자료형은 다음과 같습니다.
특별히 자료형을 지정하지 않은 경우, 상수에 적용되는 자료형은 char, int, double이며 이를 기본 자료형이라 칭합니다.
(출처: https://ittrue.tistory.com/51 [IT is True:티스토리])
#include <stdio.h>
int main() {
short a = -32768;
int b = -5;
long c = -1000000000;
long long d = 10000000000;
unsigned int e = 4294967295;
char f = a;
float g = 3.14;
double h = 3.14;
long double i = 1.2345678;
return 0;
}
//출처: https://ittrue.tistory.com/51 [IT is True:티스토리]
C언어가 다양한 자료형을 제공하는 이유는 크게 두 가지가 있는데요.
첫 번째, 데이터들의 표현 방식이 다르기 때문입니다. 정수형 데이터를 표현하는 방식과 실수형 데이터를 표현하는 방식에 차이가 있기 때문에 이를 명확히 구별하여 사용하기 위해서라고 보시면 되겠습니다.
두 번째, 메모리 공간을 효율적으로 사용하기 위해서 입니다. 데이터의 표현 범위를 고려해서 효과적인 자료형 선택을 통해 메모리 공간을 절약하고, 작은 메모리 공간에 공간의 크기보다 큰 데이터를 저장하여 데이터의 손실이 발생하는 경우를 방지할 수 있습니다.
-문자형 자료형("char")
우리가 사용하는 각 문자에는 고유의 정수 값이 부여됩니다. 이는 미국 표준 협회(ANSI)에 의해 정의된 'ASCII 코드'를 따르는데요. 컴퓨터를 이용하여 문자를 표현하기 위한 표준이고, 문자와 숫자의 연결 관계를 정의하는 것입니다.
예시를 통해 알아보겠습니다.
#include <stdio.h>
int main(void)
{
int val1 = 65;
char val2 = val1;
printf("%c - %c", val2, ++val1);
}
코드를 실행한 결과는 'A - B' 입니다. 분명히 val1 변수에는 정수 65를 저장했는데, 왜 이런 실행 결과가 나올까요?
변수 val2는 val1의 값을 문자형 자료형으로 저장했습니다. 이때 ASCII 코드에 따라 숫자 65는 문자 A를 가리키는데요. 그래서 val2의 값은 정수 65가 아닌 65가 가리키는 문자형 'A'가 된 것입니다. 같은 맥락에서 ++val1은 정수 66이고 이는 문자형 B를 가리킵니다. 그래서 결과가 'A-B'로 출력된 것입니다. 이처럼 ASCII 코드를 이용해서 숫자를 문자로, 문자를 숫자로 표현할 수 있습니다. 아래는 반대의 경우, 문자 'C'를 ASCII 값으로 치환한 코드 입니다.
#include <stdio.h>
int main(void) {
int val1 = ‘C’;
printf(“문자형 C의 ASCII 값은 %d”, val1);//C는 67의 값을 갖음
}
지금까지 학습한 내용을 바탕으로 간단한 출석률 계산 프로그램 작성을 해보겠습니다.
#include <stdio.h>
int main(void) {
int total_students, absent_students;
float attendance_percentage;
// 전체 학생 수 입력 받기
printf("전체 학생 수를 입력하세요: ");
scanf("%d", &total_students);
// 결석한 학생 수 입력 받기
printf("결석한 학생 수를 입력하세요: ");
scanf("%d", &absent_students);
// 출석률 계산하기
attendance_percentage = ((float) (total_students - absent_students) / total_students) * 100;
// 출석률 출력하기
printf("출석률은 %.1f%% 입니다.\n", attendance_percentage);
return 0; // 프로그램 종료
}
cmd창 실행결과:
전체 학생수를 입력하세요: 100
결석한 학생 수를 입력하세요: 10
출석률은 90.0%입니다.
*제어문(1)
-반복문
-특정 영역을 특정 조건이 충족되는 동안 반복 실행하기 위한 문장
-while문, do~while문, for문
-다양한 형태의 반복문(1): for 문
for 문은 반복 횟수를 알고 있거나 배열을 이루는 원소에 접근하고 싶을 때 사용하기 용이합니다.
다음은 for 문의 기본 구조 입니다.
for (초기식; 조건식; 변화식)
{
실행문;
}
초기식에서는 변수를 선언합니다. 변수를 선언할 땐 변수의 자료형도 함께 선언해야 합니다.
이때 변수에는 보통 0 또는 1을 선언합니다. 반복 횟수를 계산하기 편리하기 때문입니다.
int i = 0;
조건식에서는 부등호를 이용하여 실행문이 반복될 조건을 정의합니다.
i < 100;
변화식에선 증가 또는 감소 방향을 표현하는데, 이때 증감 연산자를 이용합니다(++, --).
보통 조건식이 실행된 다음 값에 증감을 반영하는 후위 증감 연산자를 사용합니다. 이러한 작업은 조건식의 결과가 참일 때까지 반복되다 결과가 거짓이 될 때 for 문에서 벗어납니다.
i++; ++i; or i--; --i;
-다양한 형태의 반복문(2): while 문
while 문은 특정 조건을 만족하는지 아닌지(조건 만족 여부)가 중요하게 고려될때 사용하면 좋습니다. while문에서는 지정 조건이 거짓이 될 때 while문을 탈출하기 때문인데요.
while문의 기본 구조는 다음과 같습니다.
변수 선언;
while (조건식)
{
실행문;
}
예제를 통해 정리해보겠습니다.
우리는 주사위 숫자가 6이 나올 때까지 주사위를 반복해서 굴리고자 합니다. 6이 나오면 주사위 던지기를 종료합니다!라는 멘트와 함께 프로그램을 종료하고자 할 때, 코드는 다음과 같습니다.
#include <stdio.h>
#include <time.h>
int main(void)
{
srand(time(NULL));
int dice = rand() % 6; // dice의 값은 0부터 5사이의 숫자
while (dice != 0)
{
printf("주사위의 수가 %d 입니다.\n", dice);
dice = rand() % 6;
}
printf("주사위의 수가 6입니다! 주사위 던지기를 종료합니다.\n");
return 0;
}
*제어문(2): 조건에 따른 분기
-조건문: 특정 조건에 따라 각기 다른 명령을 수행하도록 프로그램의 순차적인 흐름을 제어하는 실행문
파이썬과 달리 C언어는 if와 switch를 이용하여 조건문을 만듭니다. if 문은 조건이 범위와 값일 때 모두 사용할 수 있습니다. switch문은 조건이 값일 때만 사용할 수 있습니다. 조건이 범위일 때는 if문을 사용하고, 조건이 값일 때는 switch문을 사용하면 코드를 간단하게 작성할 수 있습니다.
조건이 값 또는 범위일 때:
- 조건이 1개이고, 조건이 거짓일 때는 명령을 실행하지 않음: if문
- 조건이 1개이고, 조건이 거짓일 때 명령을 실행함: if-else 문
- 조건이 2개 이상일 때: if-else if-else 문
조건이 값일 때:
- switch case 문
*if 문
-if만을 사용한 구문은 불필요한 연산을 하게 됩니다. 그래서 사용하게 된 것이 if ~ else 구문 입니다. 조건이 만족될 경우 if문을 실행하고, 만족되지 않으면 else문을 실행함으로써 불필요한 연산을 최소화 합니다.
if () // 조건식이 참일 때
실행문;
if (조건식) // 조건식이 참일 때
실행문;
else // 조건식이 거짓일 때
실행문;
if (조건식1) // 조건식1이 참일 때
실행문;
else if (조건식2) // 조건식2가 참일 때
실행문;
else if (조건식3) // 조건식 3이 참일 때
실행문;
⋮
else // 모든 조건식이 거짓일 때
실행문;
-if문 예제
기상청에서는 체감온도에 따라 다른 경고를 알려주는 프로그램을 만들려고 합니다. 우리나라에서는 2021년부터 체감온도에 따라 폭염주의보 또는 폭염경보를 발령하고 있습니다. 기상청에서는 일최고체감온도가 33도 이상인 상태가 2일 이상 지속될 것으로 예상될 때 폭염주의보를, 일최고체감온도가 35도 이상인 상태가 2일 이상 지속될 것으로 예상될 때 폭염경보를 내립니다.
위 상황은 온도 구간이 3개로 나뉘기 때문에 if else if else문을 사용하기에 적합합니다. 그럼 내일과 모레의 일최고체감온도를 입력하고, 입력받은 값에 따라 다른 경고를 출력하는 프로그램을 만들어보겠습니다.
#include <stdio.h>
int main(void)
{
float windchill1, windchill2;
printf("내일과 모레 예상 일최고체감온도: ");
scanf_s("%f %f", &windchill1, &windchill2);
// 2일 연속 일최고체감온도가 35도 이상으로 예상
if (windchill1 && windchill2 <= 35)
printf("폭염경보를 발령합니다.\n");
// 2일 연속 일최고체감온도가 33도 이상으로 예상
else if (33 <= windchill1 && windchill2 < 35)
printf("폭염주의보를 발령합니다.\n");
else
printf("이상무\n"); // 문제 없음
return 0;
}
*switch 문
switch 문에서는 각 case의 코드를 마칠 때마다 break를 넣어주어야 합니다. 만약 break를 사용하지 않으면 조건이 참인 경우 다음에 나오는 모든 명령을 수행합니다. 분기의 경우 수가 많아지거나, 비교 연산이 없는 경우가 switch문을 사용하기 적합한 상황입니다.
다음은 switch문의 기본 구조 입니다.
switch (변수)
{
case 값1:
실행문;
break;
case 값2:
실행문;
break;
⋮
case 값N:
실행문;
break;
default:
실행문;
break;
}
switch 문 예제를 살펴보겠습니다.
- 컴퓨터가 랜덤으로 가위바위보를 합니다. 값이 0일 때는 '가위'를, 2일 때는 '바위'를 3일 때는 '보'를 출력하고 싶습니다. 기본 문구는 "비밀"이라고 출력하고 싶습니다. C 언어로 랜덤 가위바위보를 표현하면 다음과 같습니다.
#include <stdio.h>
#include <time.h>
int main(void)
{
srand(time(NULL));
int rsp = rand() % 3; // 0 ~ 2 사이 값 정수 반환
switch (rsp)
{
case 0: printf("가위\n"); break;
case 1: printf("바위\n"); break;
case 2: printf("보\n"); break;
default: printf("비밀\n"); break;
}
return 0;
}
오늘 학습한 내용을 바탕으로 예제 2개를 풀어보고 마무리하겠습니다.
실습1. 역삼각형 출력 프로그램
높이에 해당하는 한 개의 정수를 입력 받아 역삼각형을 출력 하는 프로그램을 작성하시오.
-입력되는 값의 종류 :
역삼각형의 높이 (예 : 3, 5, 10,…)
출력되는 값의 형태 : 아래와 같은 역삼각형 모양
* * * (높이가 3인 경우)
* *
*
실습2. 구구단 프로그램
2 이상 9 이하의 자연수 중 하나를 입력 받아 그 숫자의 구구셈을 출력하는 프로그램(반복문과 분기문을 반드시 사용할 것) -입력되는 값의 종류 :
2 이상 9 이하의 자연수 (2, 3, 4, …, 9)
이외의 자연수가 입력되면 오류 메시지를 출력하고 2~9 사이의 숫 자가 입력될 때까지 다른 숫자를 입력 받는다.
출력되는 값의 형태 :
3*1=3 (3이 입력된 경우)
3*2=6
:
3*9=27
실습2
#include <stdio.h>
int main(void)
{
int num;
while (1)
{
printf("2 이상 9 이하의 자연수 중 하나를 입력하세요: ");
scanf("%d", &num);
if (num < 2 || num > 9)
{
printf("%d는 범위를 벗어난 수입니다. 다른 수를 입력하세요.\n", num);
continue;
}
else
{
break;
}
}
// 구구단 출력
for (int i = 1; i <= 9; i++)
{
printf("%d * %d = %d\n", num, i, num * i);
}
return 0;
}
실습1
#include <stdio.h>
int main(void) {
int height, i, j;
// 역삼각형의 높이 입력 받기
printf("역삼각형의 높이를 입력하세요: ");
scanf("%d", &height);
// 높이에 따른 역삼각형 출력
for (i = height; i >= 1; i--) {
for (j = 1; j <= i; j++) {
printf("*");
}
printf("\n"); // 한 줄 출력 후 줄바꿈
}
return 0; // 프로그램 종료
}
오늘은 다양한 연산자들과 scanf 함수, 기본적인 자료형, 제어문까지 다루어 봤습니다.
아무래도 어느 정도 다른 프로그래밍 언어의 학습이 되어있다는 전제 하에 진행되는 워크숍이다 보니 난이도가 조금 있습니다. 그렇지만 끝까지 따라오신다면 기본적인 C언어의 지식을 얻어가실거라 생각합니다.
오늘도 수고하셨습니다. 새로운 내용과 함께 돌아오겠습니다.