데브코스 웹풀스택 과정/TIL

1106 프로그래밍 언어 기초문법 - C언어 포인터

thinktank911 2025. 11. 5. 18:18

연산자

<평가문제>

#include <stdio.h>

int main() {
    int input1;
    int input2;
    int input3;
    scanf("%d", &input1);
    scanf("%d", &input2);
    scanf("%d", &input3);
    printf("곱: %d\n", input1*input2*input3);
    printf("합: %d\n", input1+input2+input3);
    printf("평균: %d\n", (input1+input2+input3)/3);

    return 0;
}

분기문

  • if~else문

<평가문제>

#include <stdio.h>

int main() {
    int input1;
    scanf("%d", &input1);

    if(input1%2 == 0){
        printf("입력한 수는 짝수입니다.");
    }else{
        printf("입력한 수는 홀수입니다.");
    }

    return 0;
}

<평가문제2>

#include <stdio.h>

int main() {
    int input1;
    scanf("%d", &input1);

    if(input1 >= 60){
        printf("합격입니다.");

        if(input1 >= 90){
            printf("장학대상입니다.");
        }

    }else if(input1 < 60){
        printf("불합격입니다.");
    }else{
        printf("보류");
    }

    return 0;
}

<평가문제3>

#include <stdio.h>

int main() {
    int input1;
    scanf("%d", &input1);

    if(input1 >= 19){
        printf("성인입니다.");        
    }else if(input1 < 19 && input1 >= 13){
        printf("청소년입니다.");
    }else{
        printf("어린이입니다.");
    }

    return 0;
}

<평가문제4>

#include <stdio.h>

int main() {
    char input1;
    scanf("%c", &input1);

    if(input1 == 'y'){
        printf("네, 드라이브를 포맷하겠습니다.");        
    }else if(input1 == 'n'){
        printf("아니오, 드라이브를 포맷하겠습니다.");
    }else{
        printf("잘못된 입력값입니다.");
    }

    return 0;
}

<평가문제5>

#include <stdio.h>

int main() {
    int score;
    scanf("%d", &score);

    if(score >= 91 && score <= 100){
        printf("학점은 A 입니다.");        
    }else if(score >= 81 && score <= 90){
        printf("학점은 B 입니다.");
    }else{
        printf("잘못된 입력값입니다.");
    }

    return 0;
}

반복문

  • continue : Go
    ➡️ while문 내에서 특정 조건으로 continue문 만나면 이하의 수행 무시하고 다시 반복의 시작점으로 간다.
  • break : Stop
    ➡️ while문 안에서 break 만나면 반복문 종료

함수

  • 나누어서 처리하기 위한 목적
    • 코드의 가독성 향상
    • 코드의 유지 보수 및 확장 용이
  • 대부분 프로그래밍 언어는 함수의 집합체
  • 함수의 종류
    • 표준함수 : 함수 라이브러리화 시켜서 편리하게 사용
    • 사용자정의함수 : 사용자가 원하는 기능 직접 만듦
#include <stdio.h>

int Add(int i, int j){
    return i + j;
}

int main() {
    int a = 10;
    int b = 20;
    int sum = Add(a, b);

    printf("%d\n", sum);

    return 0;
}

※ void 타입

  • 결과값을 리턴하지 않는 함수

변수의 범위

지역변수

  • 스택 메모리에 존재하는 시간 - 언제 생성되고 언제 소멸되는가?
  • 함수 호출 순서와 소멸 순서는 반대

전역변수

  • 함수 외부에 선언된 변수
  • 디버깅하기 힘들다. 유지보수 어려움 ➡️ 가급적 지양
  • 소멸시점 : 프로그램이 종료될 때 메모리상에서 소멸
  • 메모리 영역 ➡️ 스택, 힙, 데이터 영역 중에서 데이터 영역에 있다.
#include <stdio.h>
int global;

void Add(int i, int j){
    global = i + j;
}

int main() {
    int a = 10;
    int b = 20;
    Add(a, b);

    printf("%d\n", global);

    return 0;
}

static 변수

  • 지역변수 + 전역변수
  • 지역변수처럼 중괄호 영역에서 선언되지만, 중괄호 벗어나도 메모리 상에 고정되어 소멸하지 않는다.
  • 프로그램 종료될 때 소멸됨
  • 전역변수와 마찬가지로 데이터 영역에서 관리
#include <stdio.h>

void func(){
        static int value = 0; // 매번 함수 호출 때마다 소멸되지 않고 고정
    value++;
    printf("%d\n", value);
}

int main() {
    int i = 0;

    while(i < 5){
        func();
        i++;
    }

    return 0;
}

 

메모리의 생명 주기

<평가문제>

#include <stdio.h>

int func(int input){
    static int sum = 0; // 매번 함수 호출 때마다 소멸되지 않고 고정
    sum += input;
    return sum;
}

int main() {
    while(1){
        int input;
        scanf("%d", &input);
        if(input == -1){
            printf("더 분발하세요");
            break;
        }
        int sum = func(input);
        printf("최종 누적 페이지 : %d\n", sum);

        if(sum>=200){
            printf("목표 달성!\n");
            break;
        }

    }

    return 0;
}

 


배열

  • 같은 속성을 가진 요소들의 나열

배열의 선언 구조

  • 배열 타입
  • 배열 이름
  • 배열 길이 ➡️ int array[5]

배열의 초기화

  • 배열의 길이 생략
  • 초기값의 개수를 보고 컴파일러는 메모리 할당

배열의 복사

arr1 = arr2 (x)
  • 배열은 상수이기 때문에 대입 연산자를 통해 값을 넘겨받을 수 없다.
  • 배열은 요소끼리 복사해야 한다.

<평가문제>

  • 역순 복사
#include <stdio.h>

int main() {  
int i;  
int arr1\[5\] = {1, 2, 3, 4, 5};  
int arr2\[5\];

for(i =0; i < 5; i++){
    arr2[i] = arr1[4-i];
}

for(i =0; i < 5; i++){
    printf("%d\n", arr2[i]);
}

return 0;


}

문자열 변수

  • 문자열은 모두 상수
  • 문자열에 이름 붙여주면 변수로 사용 가능
  • 문자열 변수는 마지막에 null 들어감

null 문자에 관하여

  • 문자열 끝에는 null 문자가 반드시 추가된다.
  • null 문자가 왜 필요한가?
  • char str[100] = "Beautiful";
    • 문자수는 9개, 나머지 91개에는 쓰레기 값이다.
    • 사람은 문자와 쓰레기값을 구분하지만, 컴퓨터는 구분이 불가능
    • 컴퓨터가 문자열의 끝을 인식하기 위해 null 표시
    • "Beautiful" 공간은 9개 아니고 null 포함 10개임

포인터

우리가 알고 있는 변수 선언 시 메모리 구조

  • 메모리는 주소를 통해 메모리에 접근하여 값을 읽고 쓸 수 있다.

포인터의 개념

  • 포인터는 포인터 변수의 줄임말로 메모리의 주소값을 저장하고 있는 변수
  • 보통 주소값을 저장한다는 표현을 반대로 해당 메모리를 가리킨다라고도 표현함
  • 어느 특정 메모리 주소를 가리키거나 향하고 있다는 뜻
    // 캡처

포인터 사용 방법

  • 포인터 변수 선언 시 일반 변수명 앞에 * 기호만 붙여주면 된다. 주소값만 저장하겠다는 의미
  • & 기호를 통해 변수의 주소값을 얻어갈 수 있다.
  • 포인터 변수 pB가 변수 b의 주소값을 가리킨다고 표현
#include <stdio.h>

int main() {  
int b = 100;  
int \*pB = &b; // 선언 시 \*는 포인터변수임을 표시하기 위한 것

printf("b = %d\n", b); // 변수의 값
printf("&b = %p\n", &b);// 변수의 주소값
printf("pB = %p\n", pB); // *pB변수에 넣은 b의 주소값
printf("*pB = %d\n", *pB); // pB 포인터 변수가 가리키는 값

return 0;

}


포인터와 배열

배열 이름의 의미

  • 해당 배열의 첫번째 요소의 주소값 갖는다.
    즉, 첫번째 요소의 주소값을 가리키는 포인터라는 의미
  • arr은 이 배열을 가리키고 있는 포인터임을 알 수 있다.
  • arr은 &arr[0]의 값과 일치한다.
  • 배열의 이름 == 포인터
#include <stdio.h>

int main() {
    int arr[]= {1, 2, 3, 4, 5};

    for(int i = 0; i < 5; i++){
    printf("%d\n", arr[i]);
    printf("%p\n", &arr[i]);
    }

    printf("배열의 이름 : %p\n", arr);

    return 0;

}
  • 배열이 포인터지만 일반 포인터와 차이가 있다.
  • 배열의 이름은 주소값을 갖는 포인터지만 상수이므로 주소값을 변경할 수 없다.

함수와 포인터

call by value : 값에 의한 복사

int main(void){
int a = 10;
Temp(a);
}

void Temp(int b){
 ...
}

➡️ 실인수 a => 복사 => 형식인수 b

 

call by reference : 참조에 의한 복사

  • 배열형의 인자는 포인터형으로 받는다.
    • 1-5개 정도까지는 전달인자로 사용할 수 있다.
    • 그 이상이면 배열을 전달인자로 사용한다.
    • 어떻게 배열을 전달하고 받을 것인가? 바로 포인터(메모리 주소값)이다.
#include <stdio.h>

void func(int *pArr){
    for(int i = 0; i < 5; i++){
        printf("함수 안의 배열 : %d\n", *(pArr+i));
    }
}

int main() {
    int arr[] = {1,2,3,4,5};
    func(arr);
    for(int i =0; i < 5;i++){
        printf("%d\n", arr[i]);
    };
    return 0;
}
  • 배열의 요소값이 포인터로 잘 넘어갔고, 포인터변수값으로 배열 값을 출력했다.
  • 형식 인수의 형태가 int *pArr임을 볼 수 있다. 주소값을 넘겨 받기 위해서는 포인터 형태로 받아야 하기 때문
  • 메모리 구조

➡️ 주소를 넘겨주므로 메모리 낭비를 줄여 성능 면에서 좋다.