데브코스 웹풀스택 과정/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임을 볼 수 있다. 주소값을 넘겨 받기 위해서는 포인터 형태로 받아야 하기 때문
- 메모리 구조

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