Skip to main content

Command Palette

Search for a command to run...

C언어 (2)

Updated
9 min read

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의 주소를 넘겨서 그 위치에 값을 저장

만약 & 를 빼면 scanfx 에 들어있는 을 주소로 해석하려 한다. 초기화되지 않은 변수라면 쓰레기값이 주소로 쓰이면서 프로그램이 비정상 종료될 수 있다.

단, 배열 이름은 그 자체가 배열 첫 번째 원소의 주소이므로 & 를 붙이지 않는다.

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 += 5x = 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언어 프로그래밍의 기본 뼈대다. 특히 & 의 의미, 전위/후위 증감의 차이, breakcontinue 의 동작 방식은 헷갈리기 쉬우니 직접 코드를 돌려보면서 익히는 것이 좋다.

More from this blog

C언어 (13)

1. STL의 개념 1-1. 배경 C++로 프로그래밍을 하다 보면 동적 배열, 연결 리스트, 정렬, 검색 같은 자료구조와 알고리즘을 반복적으로 구현하게 된다. 프로젝트마다 매번 새로 만들면 시간도 낭비되고, 버그가 생길 가능성도 높아진다. 이런 문제를 해결하기 위해 자주 사용되는 자료구조와 알고리즘을 미리 만들어서 표준 라이브러리에 포함 시킨 것이 STL이

Apr 1, 202610 min read3

chamdom's tech

16 posts