C언어 (2)
1. 입력함수
1-1. scanf
printf 가 화면에 출력하는 함수라면, scanf 는 사용자로부터 값을 입력받는 함수다.
int age;
printf("나이를 입력하세요: ");
scanf("%d", &age);
printf("입력한 나이: %d\n", age);
scanf 의 첫 번째 인자는 서식 문자(%d, %f, %c 등)이고, 두 번째 인자부터는 입력값을 저장할 변수의 주소 를 넘긨다. 여러 값을 한 번에 입력받을 수도 있다.
int x, y;
scanf("%d %d", &x, &y);
이때 사용자는 값 사이에 공백이나 엔터를 넣어서 구분하면 된다.
주의할 점이 하나 있다. 문자(%c)를 입력받을 때는 이전 입력에서 남은 개행문자(\n)가 버퍼에 남아있어서 의도하지 않은 값이 들어갈 수 있다. 이 경우 scanf 의 서식 문자 앞에 공백을 넣어주면 해결된다.
char ch;
scanf(" %c", &ch); // %c 앞에 공백 추가
1-2. & 를 사용하는 이유
scanf 에서 변수 앞에 & 를 붙이는 이유는 메모리 주소를 넘겨야 하기 때문 이다.
scanf 는 사용자가 입력한 값을 특정 메모리 공간에 써넣는 함수다. 그러려면 "어디에 쓸지"를 알아야 하고, 그 정보가 바로 변수의 주소다. & 연산자는 변수의 메모리 주소를 반환한다.
int x;
scanf("%d", &x); // x의 주소를 넘겨서 그 위치에 값을 저장
만약 & 를 빼면 scanf 는 x 에 들어있는 값 을 주소로 해석하려 한다. 초기화되지 않은 변수라면 쓰레기값이 주소로 쓰이면서 프로그램이 비정상 종료될 수 있다.
단, 배열 이름은 그 자체가 배열 첫 번째 원소의 주소이므로 & 를 붙이지 않는다.
char name[20];
scanf("%s", name); // 배열 이름 자체가 주소이므로 & 불필요
2. 연산자
2-1. 산술 연산자
사칙연산과 나머지를 구하는 연산자다.
+ 는 덧셈, - 는 뺄셈, * 는 곱셈, / 는 나눗셈, % 는 나머지를 구한다.
int a = 10, b = 3;
printf("%d\n", a + b); // 13
printf("%d\n", a - b); // 7
printf("%d\n", a * b); // 30
printf("%d\n", a / b); // 3 (정수끼리 나누면 소수점 버림)
printf("%d\n", a % b); // 1
여기서 주의할 점은 정수끼리의 나눗셈 이다. 10 / 3 의 결과는 3.333... 이 아니라 3 이다. 소수점 이하가 버려지기 때문이다. 실수 결과를 얻으려면 피연산자 중 하나를 실수형으로 만들어야 한다.
printf("%f\n", 10.0 / 3); // 3.333333
printf("%f\n", (double)10 / 3); // 캐스팅으로도 가능
2-2. 대입 연산자
변수에 값을 저장하는 연산자다. = 가 기본이고, 산술 연산과 결합한 복합 대입 연산자도 있다.
int x = 10;
x += 5; // x = x + 5 → 15
x -= 3; // x = x - 3 → 12
x *= 2; // x = x * 2 → 24
x /= 4; // x = x / 4 → 6
x %= 4; // x = x % 4 → 2
x += 5 는 x = x + 5 와 같다. 코드가 짧아지고 의도도 더 명확해진다.
2-3. 증감 연산자
변수의 값을 1 증가시키거나 1 감소시키는 연산자다. ++ 가 증가, -- 가 감소다.
int a = 5;
a++; // a = 6
a--; // a = 5
중요한 건 전위(prefix)와 후위(postfix)의 차이 다.
int a = 5;
int b = a++; // b = 5, a = 6 (먼저 대입하고 나중에 증가)
int c = ++a; // c = 7, a = 7 (먼저 증가하고 나중에 대입)
a++ 는 현재 값을 먼저 사용한 뒤 증가시키고, ++a 는 먼저 증가시킨 뒤 그 값을 사용한다. 단독으로 a++; 처럼 쓸 때는 차이가 없지만, 다른 연산과 함께 쓸 때는 결과가 달라지므로 주의해야 한다.
2-4. 관계 연산자
두 값을 비교해서 참(1) 또는 거짓(0)을 반환한다. 조건문과 반복문에서 핵심적으로 사용된다.
== 는 같은지, != 는 다른지, > 는 큰지, < 는 작은지, >= 는 크거나 같은지, <= 는 작거나 같은지 비교한다.
int a = 5, b = 3;
printf("%d\n", a == b); // 0 (거짓)
printf("%d\n", a != b); // 1 (참)
printf("%d\n", a > b); // 1 (참)
C언어에는 boolean 타입이 기본적으로 없다. 대신 0 이면 거짓, 0이 아닌 모든 값 이면 참으로 취급한다. C99부터 <stdbool.h> 를 포함하면 true, false 를 사용할 수 있긴 하다.
흔한 실수로 ==(비교)와 =(대입)를 혼동하는 경우가 있다. if (a = 5) 는 a 에 5를 대입하고 항상 참이 되므로, if (a == 5) 로 써야 한다.
2-5. 논리 연산자
여러 조건을 조합할 때 사용한다.
&& 는 논리 AND(둘 다 참일 때 참), || 는 논리 OR(하나라도 참이면 참), ! 는 논리 NOT(참과 거짓을 반전)이다.
int age = 25;
int score = 80;
if (age >= 20 && score >= 70) {
printf("합격\n");
}
if (age < 18 || score < 50) {
printf("불합격\n");
}
if (!0) {
printf("0의 NOT은 참\n");
}
단축 평가(Short-circuit evaluation) 라는 특성이 있다. && 에서 왼쪽이 거짓이면 오른쪽은 평가하지 않는다. 어차피 결과가 거짓이기 때문이다. 마찬가지로 || 에서 왼쪽이 참이면 오른쪽은 평가하지 않는다. JavaScript의 &&, || 와 동일한 동작이다.
3. 분기문
프로그램의 실행 흐름을 조건에 따라 나누는 것이 분기문이다.
3-1. if 문
조건이 참일 때만 특정 코드를 실행한다.
int score = 85;
if (score >= 90) {
printf("A학점\n");
}
조건식이 0이 아닌 값을 반환하면 참으로 판단하고 블록 안의 코드를 실행한다. 실행할 문장이 한 줄이면 중괄호를 생략할 수 있지만, 가독성과 버그 방지를 위해 항상 붙이는 것이 좋다.
3-2. else 문과 else if
if 의 조건이 거짓일 때 실행할 코드를 지정한다. else if 로 여러 조건을 순차적으로 검사할 수도 있다.
int score = 75;
if (score >= 90) {
printf("A학점\n");
} else if (score >= 80) {
printf("B학점\n");
} else if (score >= 70) {
printf("C학점\n");
} else {
printf("F학점\n");
}
else if 는 위에서부터 순서대로 검사하고, 처음으로 참인 조건을 만나면 해당 블록만 실행한 뒤 나머지는 건너뛴다. 어떤 조건도 만족하지 않으면 else 블록이 실행된다.
3-3. switch 문
하나의 변수 값에 따라 여러 갈래로 분기할 때 사용한다. if-else if 체인이 길어질 때 더 깔끔하게 쓸 수 있다.
int menu = 2;
switch (menu) {
case 1:
printf("짜장면\n");
break;
case 2:
printf("짬뽕\n");
break;
case 3:
printf("볶음밥\n");
break;
default:
printf("메뉴에 없습니다\n");
break;
}
switch 는 괄호 안의 값과 일치하는 case 로 점프한다. break 를 빼먹으면 일치하는 case 이후의 모든 코드가 연달아 실행되는 fall-through 가 발생하므로 주의해야 한다.
// break 없으면 이렇게 됨
switch (1) {
case 1:
printf("1\n"); // 실행됨
case 2:
printf("2\n"); // 이것도 실행됨 (fall-through)
case 3:
printf("3\n"); // 이것도 실행됨
}
default 는 어떤 case 에도 해당하지 않을 때 실행되며, if-else 에서의 else 와 같은 역할이다.
switch 는 정수형과 문자형(char)에만 사용할 수 있다. 실수형이나 문자열은 사용할 수 없으므로 그런 경우에는 if-else if 를 써야 한다.
4. 반복문
같은 코드를 여러 번 실행해야 할 때 사용한다.
4-1. while 문
조건이 참인 동안 계속 반복한다.
int i = 0;
while (i < 5) {
printf("%d\n", i);
i++;
}
// 출력: 0, 1, 2, 3, 4
동작 순서는 조건 검사 → 실행 → 조건 검사 → 실행 → ... 이다. 조건이 처음부터 거짓이면 한 번도 실행되지 않는다.
무한 루프를 만들 때는 while (1) 을 사용한다. 서버 프로그램이나 게임 루프처럼 프로그램이 계속 돌아야 하는 경우에 쓰인다.
while (1) {
// 무한 반복
// break로 탈출
}
4-2. do~while 문
while 과 거의 같지만, 최소 한 번은 무조건 실행된다 는 차이가 있다. 조건 검사가 뒤에 오기 때문이다.
int num;
do {
printf("양수를 입력하세요: ");
scanf("%d", &num);
} while (num <= 0);
동작 순서는 실행 → 조건 검사 → 실행 → 조건 검사 → ... 이다. 위 예시처럼 일단 입력을 받고 나서 유효한지 검사해야 하는 경우에 유용하다. 조건이 처음부터 거짓이라도 블록 안의 코드가 한 번은 실행된다는 점이 while 과의 핵심 차이다.
do~while 문 끝에는 반드시 세미콜론(;)을 붙여야 한다.
4-3. for 문
반복 횟수가 명확할 때 가장 많이 사용하는 반복문이다. 초기식, 조건식, 증감식을 한 줄에 모아서 쓴다.
for (int i = 0; i < 5; i++) {
printf("%d\n", i);
}
// 출력: 0, 1, 2, 3, 4
실행 순서는 초기식 → 조건 검사 → 실행 → 증감식 → 조건 검사 → 실행 → 증감식 → ... 이다. 초기식은 맨 처음 한 번만 실행된다.
중첩 for 문으로 2차원 반복도 가능하다.
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
printf("(%d, %d) ", i, j);
}
printf("\n");
}
for문의 세 가지 구성요소(초기식, 조건식, 증감식)는 전부 생략할 수 있다. for (;;) 는 while (1) 과 같은 무한 루프다.
4-4. break
반복문을 즉시 탈출한다. 가장 가까운 반복문 하나만 빠져나온다.
for (int i = 0; i < 10; i++) {
if (i == 5) {
break;
}
printf("%d\n", i);
}
// 출력: 0, 1, 2, 3, 4 (5에서 탈출)
중첩 반복문에서 바깥쪽 루프까지 한 번에 빠져나오고 싶다면, break 만으로는 안 된다. 플래그 변수를 사용하거나, 해당 로직을 함수로 분리해서 return 으로 탈출하는 방법이 있다.
int found = 0;
for (int i = 0; i < 10 && !found; i++) {
for (int j = 0; j < 10; j++) {
if (arr[i][j] == target) {
found = 1;
break; // 안쪽 루프 탈출
}
}
// found가 1이면 바깥 루프 조건도 거짓 → 탈출
}
4-5. continue
현재 반복을 건너뛰고 다음 반복으로 넘어간다. break 와 달리 반복문 자체를 탈출하지는 않는다.
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) {
continue; // 짝수면 건너뜀
}
printf("%d\n", i);
}
// 출력: 1, 3, 5, 7, 9
for 문에서 continue 를 만나면 증감식으로 점프한다. while 문에서는 조건식으로 점프한다. while 에서 continue 를 쓸 때 증감식을 continue 아래에 둔 경우, 증감이 실행되지 않아 무한 루프에 빠질 수 있으므로 주의해야 한다.
// 위험한 패턴
int i = 0;
while (i < 10) {
if (i == 5) {
continue; // i++에 도달 못 함 → 무한 루프
}
printf("%d\n", i);
i++;
}
// 안전한 패턴
int i = 0;
while (i < 10) {
i++;
if (i == 5) {
continue; // 문제없음
}
printf("%d\n", i);
}
마무리
입력함수로 데이터를 받고, 연산자로 데이터를 처리하고, 분기문으로 흐름을 나누고, 반복문으로 반복 작업을 수행한다. 이 네 가지가 C언어 프로그래밍의 기본 뼈대다. 특히 & 의 의미, 전위/후위 증감의 차이, break 와 continue 의 동작 방식은 헷갈리기 쉬우니 직접 코드를 돌려보면서 익히는 것이 좋다.