오늘은 배열의 평균값을 풀었다. 꽤 어렵다...
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
double solution(int numbers[], size_t numbers_len)
{
int sum = 0;
for (int i = 0; i < numbers_len; i++)
{
sum += numbers[i];
}
return (double)sum / numbers_len;
}
int numbers[]
정수 배열 (리스트처럼 쓰는 거)
size_t numbers_len
배열의 길이 (몇 개 있는지)
(double)sum / numbers_len
소수점 계산하려고 형변환
size_t
양수만 저장할 수 있는 정수형 타입
배열 길이, 메모리 크기, 반복문 인덱스 등에 안전하게 쓰이는 자료형
| int | size_t |
|---|---|
| 음수 포함됨 | 항상 0 이상 값만 가능 |
| 32비트 제한 있음 | 시스템에 따라 더 크거나 안전함 |
for (int i = 0; i < numbers_len; i++)
i = 0부터 시작해서
i가 numbers_len보다 작을 동안
i를 하나씩 증가시키면서 반복
→ 배열의 0번째부터 끝까지 하나씩 검사함
numbers = [10, 20, 30]
numbers_len = 3
sum += numbers[i];sum에 더함 (총합 구함)포인터 아직도 헷갈림.. 유튜브에 검색해서 영상 찾아봄...ㅎ
📎개발자로의 기로에 서게 했던 ... C언어 포인터 문제 (from 정보처리기사 정처기 문제)
int x = 10;
int* p = &x;
| 표현 | 의미 | 값의 예 |
|---|---|---|
x | 변수 자체 | 10 |
&x | x의 주소값 | 0x100 |
p | 주소를 저장한 포인터 | 0x100 |
*p | p가 가리키는 값 (x) | 10 |
x: [ 10 ] ← 실제 데이터
↑
0x100 ← 주소값
p: [ 0x100 ] ← 주소 저장
*p == 10 ← p가 가리키는 곳의 값
int a[3] = {10, 20, 30};
int* p = a;
// a == &a[0]
// p = a;는 곧 p = &a[0];
| 표현 | 의미 | 예시 |
|---|---|---|
p | a[0] 주소 | 0x200 |
*p | a[0] 값 | 10 |
*(p + 1) | a[1] 값 | 20 |
*(p + 2) | a[2] 값 | 30 |
a[0]: [ 10 ] ← 주소: 0x200
a[1]: [ 20 ] ← 주소: 0x204
a[2]: [ 30 ] ← 주소: 0x208
p = a;
*p → 10
*(p + 1) → 20
*(p + 2) → 30
int a[3] )int* p )int* p = new int[3]; // 동적 배열
p[0] = 10;
p[1] = 20;
p[2] = 30;
*는 무조건 "주소를 역참조"할 때만 써야 한다
주소 없이 * 쓰면 세그먼트 오류(잘못된 메모리 접근)
int x = 10;
int* p = &x; // p는 x의 주소를 저장
*p = 20; // x의 값이 20으로 바뀜
p는 유효한 주소를 가지고 있음 (&x)
그래서 *p는 x의 값에 접근 가능
int* p; // 선언만 하고 주소를 안 넣음
*p = 10; // ❌ 큰일남!!!
p는 지금 아무 주소도 안 가리키고 있어
그런데 *p를 쓰면 "어디인지도 모르는 메모리 공간을 열어보는 것" → ❌
변수에 다른 이름(별명)을 붙여주는 것
선언할 때 & 기호를 사용하지만 주소가 아니라 레퍼런스 타입을 의미
int x = 10;
int& ref = x; // ref는 x의 또 다른 이름
ref = 20; // x도 20이 됨
ref는 x의 레퍼런스
ref에 값을 바꾸면 x도 같이 바뀜
실제로는 둘이 같은 메모리 공간을 공유함
x: [ 10 ] ← ref도 이 공간을 그대로 가리킴
ref = 20;
결과:
x: [ 20 ]
| 항목 | 포인터 (*) | 레퍼런스 (&) |
|---|---|---|
| 선언 방식 | int* p = &x; | int& ref = x; |
| 주소 저장 가능 | 가능 (p = &x) | 불가능 (다른 대상의 별명이 될 수 없음) |
| 초기화 없이 선언 | 가능 (int* p;) | 불가능 (int& ref; ❌ 오류) |
| 재할당 가능 여부 | 가능 (다른 주소도 넣을 수 있음) | 불가능 (처음 연결된 변수만 참조) |
| 값 접근 방식 | *p | 그냥 ref |
| NULL 설정 가능 | 가능 (p = nullptr;) | 불가능 (null 레퍼런스 없음) |
클래스를 사용하는 이유 : 재사용성
✅ 멤버 함수 (함수 = 동작)
역할
특징
✅ 멤버 변수 (변수 = 데이터)
→ 필요한 동작만 공개하고 세부 데이터는 숨김
class AMyActor : public AActor
{
public:
// 외부에서 접근 가능한 기능
void Jump();
void TakeDamage(int Amount);
private:
// 외부에 노출하지 않는 세부 정보
int CurrentHP;
float MoveSpeed;
};
구현 위치에 따라 2개의 방식이 있다
✅ 클래스 내부에서 직접 구현하는 방법
➡ 클래스 안에서 바로 함수 본문까지 작성
✅ 클래스 내부에서는 선언만 하고 외부에서 구현하는 방법
➡ 클래스 안에서는 함수 이름만 적고,
➡ 밖에서 클래스이름::함수이름으로 정의
파일로 나누기
클래스 외부에서 멤버 함수 정의할 때 구조
반환형 클래스이름::함수이름(매개변수)
{
// 함수 내용
}
// 예시
double Student::getAvg() // Student 클래스의 멤버 함수이다
{
return (kor + eng + math) / 3.0;
}
| 개념 | 설명 | 비유 |
|---|---|---|
| 클래스(Class) | 객체를 만들기 위한 설계도, 틀 | 붕어빵 기계 |
| 객체(Object) | 클래스를 이용해 메모리에 만들어진 실제 데이터 | 붕어빵 |
| 인스턴스화 | 클래스를 기반으로 객체를 메모리에 생성하는 행위 | 기계로 붕어빵 찍기 |
Student s; // 클래스 Student를 인스턴스화 → s는 객체
// 인스턴스화 : 변수로 선언됨 → 메모리에 잡힘 → 객체
class 붕어빵틀 {
// 생김새, 맛, 내용물 정의
};
붕어빵 s; // s는 실제로 찍힌 붕어빵
→ s는 붕어빵 하나 / 클래스를 기반으로 만들어진 객체(인스턴스)
→ 그 붕어빵에 내용물 바꾸거나 한입 먹는 행위 = 멤버 접근
멤버 함수 / 멤버 변수 접근 방법 (. 연산자 사용)
: 객체.멤버이름 형태로 접근
s.getAvg(); // 함수 접근
s.kor = 100; // 변수 접근 (public일 때만)
🔐 접근 지정자
접근 지정자 사용해 멤버의 접근 권한을 제어할 수 있음
| 지정자 | 설명 |
|---|---|
public | 외부에서도 접근 가능 |
private | 외부 접근 불가, 클래스 내부에서는 OK |
protected | 상속받은 클래스에서 접근 가능 |
| 접근자 | 내 클래스 | 내 자식 클래스 | 외부 클래스 |
|---|---|---|---|
private | ✅ | ❌ | ❌ |
protected | ✅ | ✅ | ❌ |
public | ✅ | ✅ | ✅ |
💡 규칙
private 이 기본값class Student {
int kor; // ← 자동으로 private
void hello(); // ← 이것도 private
};
멤버 변수 private : 외부에서 직접 수정 못하게 함 → 정보 보호(캡슐화)
멤버 함수 public : 외부에서 객체 동작 호출 가능하게
class Student {
public:
double getAvg(); // 외부에서 호출할 수 있도록
private:
int kor, eng, math; // 외부에서 직접 접근 불가
};
🔒 private로 멤버 변수를 숨기는 이유
사용자가 직접 변수에 접근하는 건 위험함
→ 잘못된 값이 들어가거나 객체의 상태가 망가질 수 있음
→ 외부에서 직접 변수 접근을 막고,
필요할 때만 함수로 값에 접근하거나 수정하게 함
✅ getter와 setter의 역할
직접 제어하지 않고 내가 구현한 멤버 함수에 의해서만 변수를 제어할 수 있도록 하는 함수
| 구분 | 설명 | 예시 함수 |
|---|---|---|
| setter | 값을 변경할 때 사용하는 함수 | setMathScore(int math) |
| getter | 값을 읽어올 때 사용하는 함수 | getMathScore() |
void setMathScore(int math)
{
this->math = math;
}
void setEngScore(int eng)
{
this->eng = eng;
}
void setKorScore(int kor)
{
this->kor = kor;
}
getter, setter 를 통해서만 제어setter를 호출해서 값을 수정함✅ this
class Student {
private:
int math; // 이건 클래스의 "멤버 변수"
public:
void setMathScore(int math) { // 이건 "함수 매개변수"
this->math = math; // 왼쪽 this->math는 객체의 변수, 오른쪽 math는 매개변수
}
};
| 이름 | 위치/정의 | 설명 | 예시 |
|---|---|---|---|
math | 함수 매개변수 | 함수가 외부에서 전달받는 값 (임시 저장용) | 그냥 math라고 씀 |
this->math | 클래스 내부 멤버 변수 | 객체가 실제로 가지고 있는 변수, 값을 저장하는 곳 | this->math로 접근 |
✅ 매개변수
✅ 매개변수와 인자 차이
| 구분 | 설명 | 예시 코드에서 역할 |
|---|---|---|
| 📦 매개변수 | 함수를 선언/정의할 때 쓰는 변수 | int add(int a, int b)에서 a, b |
| 🎁 인자 | 함수를 호출할 때 넣는 실제 값 | add(3, 5)에서 3, 5 |
int add(int a, int b) { // → a, b는 "매개변수"
return a + b;
}
int main() {
int result = add(3, 5); // → 3, 5는 "인자(인수)"
}
객체를 생성할 때 딱 한 번 자동으로 호출되는 특별한 멤버 함수
객체 생성 이후에는 생성자 호출 불가
| 항목 | 설명 |
|---|---|
| 이름 | 클래스 이름과 동일해야 함 |
| 반환형 | 없음 (void도 쓰지 않음) |
| 자동 호출 | 객체가 생성될 때 자동으로 딱 한 번 호출됨 |
| 목적 | 객체가 처음 만들어질 때 멤버 변수를 초기화하거나 객체가 동작할 준비 작업을 하기 위해 사용 |
클래스
↓ (인스턴스화)
객체 생성 (실제 메모리에 올라감)
↓
생성자(Constructor) 자동 호출
↓
멤버 변수 초기화 등 준비 작업 실행
✅ 매개변수 없는 기본 생성자
#include <iostream>
using namespace std;
// 클래스 정의
class Person {
public:
string name; // 멤버 변수
int age; // 멤버 변수
// 기본 생성자 : 인자가 없는 생성자
// 클래스 이름과 같은 함수 → Person()
Person() {
name = "Unknown";
age = 0;
// 반환형이 없음 → 생성자
// 객체가 생성될 때 자동으로 실행됨
}
// 멤버 함수 display()
void display() {
cout << "Name: " << name << ", Age: " << age << endl;
}
};
int main() {
Person p; // 객체 p 생성 → 생성자 자동 호출
p.display(); // p의 정보 출력
return 0;
}
// 실행 흐름
[클래스 Person 정의됨]
↓
[main 함수 시작]
↓
[Person p; → 객체 생성]
↓
[생성자 Person() 자동 호출]
↓
[p.name = "Unknown", p.age = 0 설정됨]
↓
[p.display() 실행 → 정보 출력]
Person p;
p.name = "Unknown", p.age = 0 으로 설정됨p.display();
Name: Unknown, Age: 0✅ 매개변수 있는 생성자
#include <iostream>
using namespace std;
class Person {
public:
string name; // 멤버 변수
int age; // 멤버 변수
// 매개변수가 있는 생성자
// 매개변수 n, a를 받아서 멤버 변수 name, age에 넣어줌
Person(string n, int a) {
name = n;
age = a;
}
void display() {
cout << "Name: " << name << ", Age: " << age << endl;
}
};
int main() {
Person p("Alice", 25); // 매개변수가 있는 생성자 호출
p.display();
return 0;
}
// 실행 흐름
객체 생성: Person p("Alice", 25);
↓
매개변수 있는 생성자 호출: Person("Alice", 25)
↓
n → "Alice", a → 25
this->name = n ("Alice")
this->age = a (25)
↓
p.display(); → 화면에 출력
Person p("Alice", 25);
/*
1. Person 클래스의 객체 p를 생성
2. 매개변수가 있는 생성자 Person(string, int)가 자동 호출됨
3. name = "Alice", age = 25로 설정됨
*/
✅ 기본 매개변수가 있는 생성자
#include <iostream>
using namespace std;
class Person {
public:
string name;
int age;
// 기본 매개변수가 있는 생성자
// n, a에 기본값이 설정되어 있음
Person(string n = "DefaultName", int a = 18) {
name = n;
age = a;
}
void display() {
cout << "Name: " << name << ", Age: " << age << endl;
}
};
int main() {
Person p1; // 기본값 사용
Person p2("Bob", 30); // 값을 지정
p1.display();
p2.display();
return 0;
}
/*
출력 결과
Name: DefaultName, Age: 18
Name: Bob, Age: 30
*/
| 파일 종류 | 역할 |
|---|---|
헤더파일 .h | 선언(정의가 아님), 인터페이스 제공 |
소스파일 .cpp | 실제 동작 구현 (함수 정의 등) |
📄 헤더 파일을 따로 만드는 이유