728x90

"Call by value"와 "call by reference"는 함수 호출 방식을 나타내는 용어

이 두 가지 방식의 주요 차이점: 함수에 인수를 전달하는 방법 & 함수가 인수를 처리하는 방법

 

1. Call by Value (값에 의한 호출):

  • 함수에 매개 변수로 ‘값’을 전달합니다.
  • 함수 내에서 매개 변수는 별도의 변수로 취급되며, 인수의 값이 복사되어 매개 변수에 저장됨
  • 따라서 함수 내에서 매개 변수의 값을 변경하더라도 호출자(caller/원본)의 변수는 영향을 받지 않음
  • C++에서 함수의 기본 호출 방식은 "call by value"
void modifyValue(int x) {
    x = x + 10;
}

int main() {
    int num = 5;
    modifyValue(num);
    std::cout << "num: " << num << std::endl; // 출력 결과: num: 5
    return 0;
}

 

2. Call by Reference (참조에 의한 호출):

 

"주소/참조 값을 전달받아서, 함수 외부에 선언된 변수직접 접근하는 형태의 함수 호출"

- 함수 입력으로 주소/참조 값이 전달되었다는 사실이 중요한 게 아니라

- 입력된 주소/참조 값을 통해 직접 외부 변수를 참조하여 모종의 행위(읽기/조작 등)을 했을 때 call-by-reference라 칭할 수 있음

  • 함수에 매개 변수로 ‘변수의 참조/주소’를 전달
  • 함수 내에서 매개 변수는 원래 변수와 동일한 메모리 위치를 참조하므로,
    함수에서 매개 변수를 수정하면 호출자(원본)의 변수가 직접 영향을 받음
  • C++에서는 매개 변수를 참조로 전달하려면 참조자(reference) 또는 포인터(pointer)를 사용해야 함

(1) 포인터 *를 활용 (주소)

void modifyValueByPointer(int* x) {
    *x = *x + 10;
}

int main() {
    int num = 5;
    modifyValueByPointer(&num);
    std::cout << "num: " << num << std::endl; // 출력 결과: num: 15
    return 0;
}

- 늘 느끼는 건데.. (?) 주소값은 메모리가 할당된 위치이고, 이 주소값 자체는 변하지 않고 고정됨.

반면, 해당 주소에 저장된 value 자체는 변형&조작의 대상이 됨.

 

- 단, 이하와 같은 방식으로, 포인터를 통해 value를 직접 조작하는 것을 막을 수 있다.

const int* ptr1 = &num; //ptr1를 이용해서 num의 value를 바꿀 수 없음 //*ptr1를 상수화시켰다는 의미

*주의할 점은, ptr = 2; ptr이 const 변수이기 때문에 컴파일 에러가 발생 하지만

 num = 2; num이 non-const 변수이기 때문에 정상이다. 즉, 포인터 변수가 가리키는 num 자체가 상수화가 되는 것이 아니다.

https://easycoding91.tistory.com/entry/C-%EA%B0%95%EC%A2%8C-const-%EC%9C%84%EC%B9%98%EC%9D%98-%EC%9D%98%EB%AF%B8%EC%99%80-%EC%82%AC%EC%9A%A9-%EB%B0%A9%EB%B2%95

 

 

- (완전 별개의 이야기지만) 포인터 주소값 자체를 상수화시킬 수도 있다.

int* const ptr2 = &num; // num의 주소값인 ptr2 자체가 상수화됨

 

 

 

(2) 참조 &를 활용 (별명)

void modifyValueByReference(int& x) {
    x = x + 10;
}

int main() {
    int num = 5;
    modifyValueByReference(num);
    std::cout << "num: " << num << std::endl; // 출력 결과: num: 15
    return 0;
}

 

- int& x에서 &는 C++에서 "참조자"를 나타내는 연산자

- 자료형 바로 뒤에 &가 붙음!!! (변수명 앞에 &가 붙는 게 아니라 ~~~.)

- 참조자는 변수에 대한 별명(alias)이며, 원본 변수를 직접 참조하게 함.

이것은 값(value) 자체가 아니라 변수에 대한 참조(reference)를 함수에 전달함을 의미함

- int& x에서 x는 참조자의 이름이고, int는 참조할 변수의 데이터 타입을 나타냄 (int& x: "정수형 변수에 대한 참조를 받는다")

 

 

참조자를 사용할 때의 특징

  • 원본 변수의 별명: 참조자는 원본 변수의 별명으로 동작함, 따라서 참조자를 통해 값을 읽거나 수정하면 원본 변수에도 동일한 작업이 반영됨.
  • 포인터와 다른 점: 참조자 != 포인터.
    포인터는 메모리 주소를 저장하고 간접 참조를 통해 값을 읽고 쓰고 조작할 수 있지만,
    참조자는 변수에 직접적으로 연결되어 있으므로 포인터보다 사용하기 간편
  • 전달이 아닌 참조 전달: 함수에 참조자를 매개 변수로 전달하면 함수 내에서 원본 변수를 직접 조작할 있으며, 호출자의 변수도 변경됨( 성질은 포인터에서도 동일~)
728x90
728x90

실행중인 프로그램은 운영체제로부터 메모리 공간을 할당받는데, 프로그램이 실행 중인 동안 사용되는 메모리는 일반적으로 세 가지 주요 영역으로 나뉨

: 데이터, 스택, 힙. 각 영역에는 다음과 같은 형태의 변수나 데이터가 할당됨.

 

간단히 말해 데이터 영역은 전역 변수와 정적 변수를 저장하고, 스택 영역은 함수 호출 관련 데이터를 저장하며,  영역은 동적으로 할당된 변수와 데이터를 저장함. 이러한 영역은 각각의 역할을 수행하여 프로그램의 메모리 사용을 관리하고, 변수  데이터의 수명을 결정함.

 

1. 데이터(Data) 영역:

- 전역 변수(Global Variables): 프로그램 전체에서 접근 가능한 변수로, 초기화된 데이터를 저장. 이러한 변수는 프로그램이 시작될 메모리에 할당되고 프로그램이 종료될 때까지 유지됨.

- 정적 변수(Static Variables): 함수 내부에서 선언되지만, 프로그램이 실행될 메모리에 할당되며 함수가 호출될 변수 값이 초기화됨. 함수가 종료되더라도 메모리에 남아있는 특성을 가짐.

 

#include <iostream>

// 전역 변수 (global variable)
// globalVar는 프로그램 전체에서 접근 가능하며 초기화된 데이터를 저장
int globalVar = 10;

int main() {
    // 정적 변수 (static variable)
    // staticVar는 main 함수 내에서 선언되었지만 프로그램이 시작될 때 메모리에 할당되며, 프로그램이 종료될 때까지 유지됨
    static int staticVar = 5;
    
    std::cout << "전역 변수: " << globalVar << std::endl;
    std::cout << "정적 변수: " << staticVar << std::endl;

    return 0;
}

 

2. 스택(Stack) 영역

- 지역 변수(Local Variables): 함수 내부에서 선언되며 함수가 호출될 메모리에 할당됨. 함수가 종료되면 해당 변수도 소멸됨. 스택은 함수 호출과 관련된 데이터 함수의 호출 복귀 정보를 저장하는 사용됨.

#include <iostream>

int add(int a, int b) {
    int result = a + b; // 지역 변수 (local variable)
    return result;
    // add의 지역변수 ; 함수가 호출될 때 메모리에 할당되고 함수가 종료될 때 소멸
}

int main() {
    int x = 5; // 지역 변수 (local variable)
    int y = 3; // 지역 변수 (local variable)
    // main의 지역변수 ; 함수가 호출될 때 메모리에 할당되고 함수가 종료될 때 소멸

    int sum = add(x, y); // add 함수 호출

    std::cout << "합계: " << sum << std::endl;

    return 0;
}

 

3. 힙(Heap) 영역:

- 동적 할당된 변수(Dynamically Allocated Variables): 프로그램 실행 중에 필요한 메모리를 동적으로 할당하고 해제하는 사용됨. 이러한 변수는 개발자가 메모리 할당 해제를 관리해야 . 예를 들어, new (C++) 또는 malloc (C) 함수로 메모리를 할당하고 delete (C++) 또는 free (C) 함수로 메모리를 해제함.

 

#include <iostream>

int main() {
    int* dynamicVar = new int; // 동적 할당된 변수 (dynamically allocated variable)
    *dynamicVar = 7;

    std::cout << "동적 할당된 변수: " << *dynamicVar << std::endl;

    delete dynamicVar; // 메모리 해제

    return 0;
    
    // 프로그램 실행 중에 메모리를 할당하고 new 연산자를 사용하여 초기화함
    // 이 변수는 개발자가 명시적으로 메모리를 해제해야 하며, delete 연산자를 사용하여 메모리를 해제함
}
728x90

728x90

1. 함수와 배열

c++에서, 배열 이름을 그 배열의 첫번째 원소의 주소로 인식함
// arr == &arr[0]

#include <iostream>
using namespace std;

const int SIZE = 8;
int sumArr(int arr[], int n);
int sumArr2(int*, int*);
void sizeArr(int* arr);

/*c++에서, 배열 이름을 그 배열의 첫번째 원소의 주소로 인식함*/
// arr == &arr[0]
int main()
{
    int arr[SIZE] = {1,2,3,4,5,6,7,8};

    int total = sumArr(arr, SIZE);
    cout << "This is total1: " << total << endl;
    // This is total1: 36
    int total2 = sumArr2(arr, arr+SIZE);
    cout << "This is total2: " << total2 << endl;
    // This is total2: 36


    cout << "size of arr[SIZE]: " << sizeof arr << endl;
    // size of 'arr[SIZE]': 32 //argument
    sizeArr(arr);
    // size of 'arr': 8 //parameter
}

/*배열을 전달받을 때에는 배열 전체가 들어오는 것이 아닌, 첫번째 원소의 주소값이 들어옴*/
int sumArr(int arr[], int n) { //int* arr라고 써도 무방
    int total = 0;
    for (int i=0; i<n; i++)
        total += arr[i]; //배열의 원소에 접근하는 대괄호는 배열이든 포인터든 사용가능
    return total;
}

int sumArr2(int* begin, int* end) {
    int total = 0;
    for (int* pt = begin; pt != end; pt++)
        total += *pt;
    return total;
}

void sizeArr(int* arr) {
    //int arr[] 일 때와 int* arr 일 때 결과가 동일함
    cout << "size of array as parameter: " << sizeof arr << endl;
    // size of array in sumArr(): 8
    // 32가 아닌 8이 나옴
}

 

2. 함수와 구조체

구조체와 함수
- 함수 통해 구조체 자체를 직접 리턴할 수 있고
- 함수의 인풋으로 구조체 형식을 사용할 수도 있음
 
구조체 규모가 너무 커질 때, 함수는 구조체의 원본이 아닌 복사본으로 작업하기에
- 이때 구조체를 포인터로 사용할 시 훨씬 효율적이게 됨
- 이 경우 원소에 접근하려면 화살표를 사용함

#include <iostream>
using namespace std;
const int MINUTES = 60;
int totalmin(int hour, int minute);

struct Time{
    int hour;
    int minute;
};

Time sum(Time*, Time*); //구조체와 구조체를 더하여서 구조체를 Return하는 함수
void showTime(Time);

int main()
{
    Time market;
    market.hour = 2;
    market.minute = 20;
    Time library = {1, 11};

    cout << "Total minutes to market: " << totalmin(market.hour, market.minute) << endl;
    cout << "Total minutes to library: " << totalmin(library.hour, library.minute) << endl;

    Time total = sum(&market, &library);
    showTime(total);
}

/*
Total minutes to market: 140
Total minutes to library: 71
총 3시간 31분 걸렸습니다.
 */

함수 쪼개서 보기 - totalmin

int totalmin(int hour, int minute)
{
    return hour * MINUTES + minute;
};

함수 쪼개서 보기 - sum

- 구조체를 return하는 함수

- 입력으로 구조체를 받음

- 이때 구조체의 주소값을 입력으로 받음

**당연하지만, 입력으로 Pointer를 지정해놓으면, 실제 함수 입력으로도 주소값을 넣어야 함(앞에 & 붙여서~)

Time sum(Time* a, Time* b) {
    //구조체의 값에서 멤버(원소)로 접근할 때: . (dot)
    //구조체의 주소에서 멤버(원소)로 접근할 때: -> (arrow)

    Time total;

    total.minute = (a->minute + b->minute) % MINUTES;
    total.hour = (a->hour + b->hour) + ((a->minute + b->minute) / MINUTES);

    return total;
}

void showTime(Time t){
    cout << "총 " << t.hour << "시간 " << t.minute << "분 걸렸습니다." << endl;
}

 

3. 재귀함수

재귀호출: 함수가 자기 자신을 호출할 수 있음
재귀함수: 재귀호출을 하는 함수 - 종결을 위해 보통 If문을 사용

  void recurs(argumentList)
   //code #1
   if (재귀호출조건)
       recurs(argumentList)
   //code #2

   이때 재귀호출이 연쇄적으로 일어나는 동안에는 code#2가 실행되지 않고 유보됨
   연쇄가 끊이면, 가장 마지막에 호출된 code#2부터 쭈욱 실행되어 마지막에 최초 code#2를 실행

 

#include <iostream>
using namespace std;

void countDown(int);

int main()
{
countDown(5);
}

void countDown(int n)
{
    cout << "Counting..." << n << endl;
    if (n > 0)
        countDown(n-1);
    cout << n << " 올라간다~" <<endl;
}

/*
Counting...5
Counting...4
Counting...3
Counting...2
Counting...1
Counting...0
0 올라간다~
1 올라간다~
2 올라간다~
3 올라간다~
4 올라간다~
5 올라간다~*/

 

4. 함수를 지시하는 포인터

함수를 지시하는 포인터
-> 어떠한 함수에 함수의 주소를 매개변수로 넘겨주는 경우, 유용하게 사용할 수 있다.

1. 함수의 주소를 얻는다 (그냥 '함수명'만 입력해주면 됨)
2. 함수를 지시하는 포인터를 선언한다. 이때 함수 포인터 선언 형태가 특이함 
    

    1) 반환형 (*포인터명)(매개변수) = 함수명; OR
    
    2)반환형 (*포인터명)(매개변수);
       포인터명 = 함수명
    
3. 함수를 지시하는 포인터를 사용하여 그 함수를 호출한다.

 

#include <iostream>
using namespace std;
int func(int);

//함수 포인터에서 괄호를 빠뜨리지 말 것 !

int main() {
    int (*ptr_func)(int) = func;
    cout << ptr_func(10) << endl; //1000
    cout << (*ptr_func)(10) << endl; //1000

    int (*ptr_func2)(int);
    ptr_func2 = func;
    cout << ptr_func2 << endl; //1
    cout << (*ptr_func2)(10) << endl; //1000
}

int func(int a)  {
    return a*100;
}

//함수의 주소는 함수 당 하나밖에 없음! 입력이 어떻든, 함수마다 고유한 값~

 

728x90
728x90

1. 함수 기본

/*
 * 1. 함수 정의 제공
 * 2. main 앞에, 함수 원형 명시 ; (1)함수 데이터형 (2)함수 이름 (3)(매개변수);
 * 3. 함수 호출
 * */

/* 함수의 두 타입
 * 1. 리턴값이 있는 타입: 함수가 리턴하는 값의 데이터형으로 선언 & return 뒤 변수 있음
 * 2. 리턴값이 없는 타입: void로 선언 & return 뒤 변수 없음 */
#include <iostream>
using namespace std;

const float PIE = 3.14;
/* main 함수 이전에 함수 원형 명시 */
//물론 원형 명시와 함께 함수를 정의하는 것도 가능
void cheers(int n);
float circle(int x);

int main(){

    int a;
    cout << "하나의 수를 입력하십시오." << endl;
    cin >> a;
    cheers(a);

    int b;
    cout << "원의 반지름을 입력하십시오." << endl;
    cin >> b;
    float c = circle(b); //입력과 리턴 모두 자료형과 함께 미리 선언해주어야 함
    cout << "원의 넓이는 " << c << "입니다." << endl;

}
/* main 함수가 끝날 때 함수 정의 */
void cheers(int n) {
    for (int i = 0; i < n; i++) {
        cout << "cheers!" << endl;
    }
}

float circle(int x) {
    return x * PIE;
}

→ 당연히, main 함수 안에, 내가 따로 선언해준 함수를 명시해서 사용해주어야 한다!

 

2. 매개변수와 전달인자

#include <iostream>
using namespace std;
void helloCPP(int, int);

int main() {
    int n; //파라미터(매개변수)
    int m; //파라미터(매개변수)
    cout << "n: " << endl;
    cin >> n; //파라미터(매개변수)
    cout << "m: " << endl;
    cin >> m; //파라미터(매개변수)
    helloCPP(n, m); //전달인자(argument)
}

void helloCPP(int n, int m) {
    for (int i=0; i < n; i++)
        cout << "Hello n" << endl;

    for (int i=0; i < m; i++)
        cout << "Hello m" << endl;
}

함수의 인자를, 우리의 입력 내용에 따라 정해줌

매개변수와 전달인자 간 자료형이 일치해야 함

 

 

 

728x90
728x90

1. 개념 요약

- 데이터에 대한 라벨을 사전에 구축하지 않아도, self supervised learning을 통해 Representation Learning을 수행

 

2. 기본 아이디어

여러 입력 쌍에 대해 유사도를 라벨 판별 모델을 학습함. (데이터 포인트 간의 유사성과 차이를 학습)

즉, 학습된 표현 공간 상에서 비슷한 데이터는 가까이, 다른 데이터는 멀리 위치하도록 학습하는 방법.

- 이때, GT label y가 0(similar) 아니면 1(dissimilar)임. (only two types)

- GT가 similar인 경우와 dissimilar인 경우 각각에 적용되는 loss function의 종류가 서로 다름 (L_s, L_d)

D_w: 우리가 계산한 x_1와 x_2 간 distance

- 학습을 통해, 모델은  데이터 포인트를  구별할  있는 특성을 파악해내고, 데이터 표현을 향상시키는  도움을 줌 

 

3. 이점:

- self-supervised인 만큼 비용이 적음 (단, 아예 비지도 학습인 생성 모델을 활용하는 경우도 구축 비용이 적음.

라벨링 데이터를 활용하는 판별 모델보다는 구축 비용이 저렴함.)

- 보다 general한 feature representation & unseen class에도 대응 가능

- 다른 task로 fine tuning할 때 모델 구조 수정 없이 이뤄질 수 있어서 간편함.

 

4. 기본 원리

https://velog.io/@yjkim0520/Contrastive-Learning

1) Data Augmentation

- positive pair & negative pair 생성 ((늘 그렇듯^^) negative sampling이 주요 관건)

2) Generating Representation (=Feature Extraction)

- 주로 pre-trained backbone 활용 (ex. ResNet)

3) Projection Head (=Obtain Metric Embedding)

- 2에서 얻은 extracted feature embedding을 metric space로 project시켜줌

- 이를 위해 간단한 MLP가 사용됨

- 이 Head 자체를 통해 metric learning이 진행되는 건 아니고, 앞단의 embedding output의 차원을 축소하는 역할을 함.

- 해당 head의 output이 이후 metric learning을 위한 contrastive metric loss 계산의 대상이 됨

4) Contrastive Loss 계산

- Loss function에 여러 종류가 있는데, 그 중 InfoNCE, NT-Xent 등이 있음

- 아까 언급했듯, GT가 similar인 경우와 dissimilar인 경우 적용되는 loss function의 종류가 서로 다름

 

 

4. 원리 조금 더 구체적으로

Input sample X_i 각각에 대해 이하 과정을 진행

1) prior knowledge를 바탕으로, X_i와 비슷한 것들을 모두 S_{xi}에 넣음
2) S_{xi}에 해당하는 모든 X에 대해서는 Y_{ij}의 라벨을 0으로 지정, 해당하지 않을 경우 라벨을 1로 지정 

→ 마치 labeled training set처럼, (X_i, X_j, Y_{ij})과 같은 pair로 만듦
→ 실제 train을 진행할 때, Y_{ij}=0인 경우에는 W가 D_w = D(ij)를 줄이도록 학습하고, vice versa 

 

5. Application

5-1. SimCLR

1차 임베딩 결과인 Representation output을 활용하여&nbsp;&rarr; Downstream task 수행

loss function으로 NT-Xent를 사용함 (분자: L_s, 분모: L_d)

이 NT-Xent를 통해 contrastive learning이 이루어지는 것.

NT-Xent

 

 

5-2. NCE

SIMCLR은 데이터 포인트 간 유사성을 비교하고 구조화된 임베딩을 학습하는 반면, 
NCE는 비교적 단순한 분류 모델(binary classifier)을 활용하여 데이터 포인트를 분류하며 학습

특히, 확률분포 개념을 이용함으로써 task가 더 간단해짐.

 

- M개의 training examples가 있다고 할 때

→ True pairs: centor word + another word appeared in context (즉 함께 get along하는 두 단어)

→ Fake pairs: 랜덤하게 아무 두 단어로 구성된 pair

 

- "True pairs가 어떠한 확률분포로부터 샘플링되었나?"

: 그것을 p_m이라고 가정하고, 우리의 목표는 p_m을 예측하는 것.

- 반면, Fake pairs에 대한 확률분포 p_n을 구해둠. (이건 상수같은 느낌, 학습 대상이 아님)

 

- 모델이 학습해야 할 task

: True pairs와 Fake pairs가 섞여있는 와중에, 각 샘플들이 p_m에서 왔는지 p_n에서 왔는지 binary classification

p_m에서 true pairs가 나올 확률을 maximize

p_m에서 fake pairs가 나올 확률을 minimize

 

그림에서 p_m: p_model / p_n: p_noise

 

 

reference: https://velog.io/@yjkim0520/Contrastive-Learning

https://cedar.buffalo.edu/~srihari/CSE676/18.6%20Noise%20Contrastive%20Estimation.pdf

728x90
728x90

1. if, else if, if 조건문

#include <iostream>
using namespace std;

int main(){
    //분기 구문: if & switch
    /*if(조건)
     *  코드*/
    if (true) {
        cout << "참";
        cout << "입니다." << endl; }
    if (false)
        cout << "거짓" << endl;

    // if - else if - else 구문은 하나의 덩어리로 인식함
    if (false)
        cout << "거짓" << endl;
    else {
    cout << "참이라구요~!" << endl;}

    // if 하나에 else 여러 개 가능
    // 이때 else: 앞단 것의 차집합이 연쇄로 계산됨
    // 양끝 말고는 else if를 사용하는 게 더 나을 듯.
    
    cout << "종료됨" << endl;
    /*
    참입니다.
    참이라구요~!종료됨*/
    
    }

2. 논리연산자

#include <iostream>
using namespace std;

int main(){

    /*논리 표현식: 논리합, 논리곱, 논리부정 연산자*/
    //논리합(or): 좌항 || 우항
    //논리곱(and): 좌항 && 우항
    //논리부정 연산자: !(표현식)
        //e.g. !(True) => False
    cout << "당신의 나이는?" << endl;
    int age;
    cin >> age;
    if (age<0 || age>100)
        cout << "거짓말 마세요!" << endl;
    else if (age>=20 && age<30)
        cout << "20대시군요!" << endl;
    else
        cout << "당신의 나이를 모르겠어요."<< endl;
}

3. switch, break, continue

#include <iostream>
using namespace std;

int main(){
    /*switch 구문*/
    //선택할 수 있는 경우의 수가 3개 이상으로 넘어가면, if-else보다 switch 구문이 훨씬 효율적.!
//    integer-expression에 해당하는 값의 case로 이동 -> 해당 값에 있는 코드가 실행됨
//    e.g.
//     switch (integer-expression) {
//        case label1:
//            code1 / break;
//        case label2:
//            code2 / break;
//     ... }
    int a;
    cin >> a;
    switch(a){
        case 1:
            cout << "1\n";
            break; //switch 구문을 바로 종료시킴 //*break* 는 모든 반복문에서 사용 가능
        case 2:
            cout << "2\n";
            continue; //이하 코드를 무시하고, 다음 loop를 돈다 //*continue* 는 반복문에서만 사용가능하다
        case 3:
            cout << "3\n";
            break;
        default:
            cout << "case 이외의 수를 입력하셨습니다.\n";
    }
    cout << "switch 구문이 끝났습니다.";
    //지정한 case 이외의 경우는,
    //default 지정 시 해당 구문이 실행되거나, 그렇지 않을 경우 별다른 실행 없이 종료됨.

}

4. 예제

#공백 제외한 글자 수 세기

#include <iostream>
using namespace std;
const int SIZE = 30;

int main(){
    char line[SIZE]; //문자 배열, string의 끝에는 개행문자 \0가 포함돼있음
    cin.get(line, SIZE); //get을 통해 입력 받기

    int word_cnt = 0;
    for(int i = 0; line[i]!='\0'; i++){
        if(line[i]==' ')
            continue;
        word_cnt++;
    };

    cout << "공백 제외 총 글자 수는 " << word_cnt <<"입니다~";
    //영어로 하니까 괜찮은데 한글은 개수 처리가 애매하다, 다른 설정이 필요한 듯.

}

reference:https://inf.run/4ttD

728x90
728x90

0. 증가/감소 연산자

/*잠깐
     * - 증가연산자
     * - 감소연산자
     * => 반복문의 카운터에 구현하면 유리함*/

    int a = 10;
    int b = 10;
    cout << "a는 " << a << " b는 " << b << endl;
    cout << "a++는 " << a++ << endl; //변수의 값을 판단한 후 증가시킴 -> 10 출력
    cout << "++b는 " << ++b << endl; //증가시킨 후 변수의 값 판단 -> 11 출력
    cout << "다시 a는 " << a << endl; //11

    for(int j = 5; j>0; j--){ //부등식은 관계표현식의 일종
        cout << j << endl;
    }

1. 반복문 for

#include <iostream>
using namespace std;

int main(){
    /*반복문 for*/
    for(int i = 0; i < 5; i++) {
        cout << i << "번째" << endl;
    }
    /*반복문의 네 가지 규칙
     * 1. 반복문에 사용할 카운터(반복 횟수에 대한 변수)의 값을 초기화 (여기서 i=0)
     * 2. 반복문을 진행할 것인지 '조건 검사' (여기서 i < 5)
     * 3. 반복문 몸체를 수행
     * 4. 카운터의 값을 변화시킴(여기서 i++) */}

 

2. 반복문 while, do while

#include <iostream>
using namespace std;

int main(){
    /*반복문 while
     * while(조건) {카운터변화}
     * 조건에 true나 false와 같은 bool형도 들어갈 수 있음
     * while로 짠 것을 for문으로도 짤 수 있고, vice versa, 둘 사이 복잡도 차이도 없음
     * 단, while문은 bool데이터만으로, 관계표현식 없이도, 실행할 수 있음
     * 또한, for문은 카운터 초기화, 카운터 변화 모두 for문 행에서 한꺼번에 정의할 수 있는데
     * while문은 카운터 초기화를 while문 바깥에서 미리 선언해야 하며, 이는 while문을 벗어나서도 해당 변수를 사용할 수 있음을 의미.
     *
     */

    int k = 0;
    while (k<3) {
        cout<<k<<"번째"<<endl;
        k++; //while문 내부에 반드시 '카운터변화' 행을 포함해야 함
    }

    string str = "panda"; //string은 명시적으로 개행문자'\0'를 포함
    int l = 0;
    while (str[l] != '\0'){
        cout << str[l] << endl;
        l++;
    }

    //일반 while문: 조건 검사 후 -> 반복 실행
    //do while문: 반복을 실행 후 -> 조건 검사 //따라서 어떤 조건이든 최소한 1회는 실행 됨
    int m = 0;
    do{
        cout << "while문 입니다.\n";
        m++;
    } while (m < 3);
}

3. 예제

3-1. 배열과 반복문

#include <iostream>
using namespace std;

int main(){
   /*배열 기반 반복문*/

    int a[10] = {1, 3, 5, 7, 9};

    //1: index로 배열 원소 접근
    for (int i = 0; i<5; i++){
        cout << a[i];
    } //13579
    cout << "\n";

    //2: 배열 자체로 원소 접근 //for (int 원소: 배열 이름) {}
    //간결하지만, 실제 원소 개수보다 선언한 원소 개수가 많을 경우, 0으로 채워져 선언한 만큼 출력됨
    for (int k : a) {
        cout << k;
    } //1357900000


}

3-2. 중첩 Loop

#include <iostream>
using namespace std;

int main(){
   /*중첩루프
    * 2차원 배열에서 많이 활용됨*/

   int temp[4][5] = {
           {1,2,3,4,5},
           {11,22,33,44,55}

   }; // 배열[행개수][열개수]
   // 2차원 배열 생성 시 {{}}의 이중 중첩 활용 & 행 간 콤마로 연결

   for (int row = 0; row < 4; row++) {
       for (int col = 0; col < 5; col++) {
           cout << temp[row][col] << " ";
       } cout << endl;
   }
/*
1 2 3 4 5 
11 22 33 44 55 
0 0 0 0 0 
0 0 0 0 0 */

reference:https://inf.run/4ttD

728x90
728x90

배열(array)

- 주소값을 가지고 포인터 연산을 통해 배열의 모든 연소에 각각 접근할 수 있음

- 배열 선언 시, 메모리에 같은 크기의 공간들을 연속적으로 할당해놓고,

-->첫번째 요소의 주소값을 가지고 정수 연산

-->이어진 다음 요소의 주소를 계산할 수 있음

-->주소에 *를 붙여서 각 요소에 직접 전근 가능

 

예시1

int ary[5] = {1, 2, 3, 4, 5}

cout << art ; //4390184 첫 번째 원소의 주소 반환

cout << art+1 ; //4390188 두 번째 원소의 주소 반환(+4)

파이썬에서 그 배열이 아니다~~~!~!

c++에서 배열은 그 배열 첫번째 원소의 주소값을 반환한다!

 

이때, 'cout << 배열'을 하면, 해당 배열의 첫번째 원소 주소값이 저장되어있긴 하지만,

cout을 만났을 때 '얘는 배열이므로, 저장되어 있는 첫번째 원소의 주소값부터 null의 주소값까지의 value(chars)를 출력해주자

 

로 해석한다.

 

예시2

int* p = ary+1 // 포인터 p에 4390188 입력했다는 것.

이때, p - ary는 4가 아니라 1임. p와 ary 둘 다 '주소'이기 때문에 주소 연산을 수행함.

포인터+정수 → 포인터+(정수 x sizeof포인터)

 

 

pointer & new를 통해 동적 자료 만들기.

 

1.

#include <iostream>
#define SIZE 20
int main(){
    using namespace std;

    double* p3 = new double[3]; //double형 데이터 3개를 저장할 수 있는 공간을 대입
    p3[0] = 0.2; //p3를 배열 이름처럼 취급
    p3[1] = 0.5;
    p3[2] = 0.8;
    //new 연산자를 통해 동적 배열을 생성
    //pointer를 사용하여, 해당 배열의 원소에 개별적으로 접근 가능

    p3 = p3 + 1; //배열에 값을 더하는 행위//double의 크기만큼 더해짐

    cout << p3 <<endl; //0x6000022e1188
    cout << "Now p3[0] is " << p3[0] << " and "; //Now p3[0] is 0.5 //기존 p3[1]의 값
    cout << "p3[1] is " << p3[1] << ".\n"; // p3[1] is 0.8.

    p3 = p3 -1;
    delete[] p3;
    /**/

    char animal[SIZE];
    //char로 문자열을 지정할 때는, 문자열 크기를 미리 선언해야 함
    //컴파일 시간에 배열의 크기를 결정하는 것
    char* ps;

    cout << "동물 이름을 입력하십시오\n";
    cin >> animal;

    ps = new char[strlen(animal)+1]; //사용자가 무슨 animal을 입력하든 그보다 한 칸 더 넉넉히.
    //실행 시간에 배열의 크기를 결정하는 것이, 메모리 차원에서 훨씬 유리함

    strcpy(ps, animal); //animal의 값을 ps에 복사

    cout << "입력하신 동물 이름을 복사하였습니다" << endl;

    cout << "입력하신 동물 이름은 " << animal << "이고, 주소는 " << (int*)animal << "인데," << endl;
            //입력하신 동물 이름은 panda이고, 주소는 0x16f9235e4인데,
    cout << "그것을 복사한 것의 이름은 " << ps << "이고, 주소는 " << &ps << "일까, " << (int*)ps <<"일까?";
            //그것을 복사한 것의 이름은 panda이고, 주소는 0x16f9235d0일까, 0x600000b48030일까?
            //복사된 값이 원래 주소와 다름
}

2.

#include <iostream>
#define SIZE 20

/**/
//동적(dynamic): 컴파일 시간이 아닌, 실행 시간에 size를 받음
//동적 구조체 생성
//temp* ps = new temp;

struct MyStruct{
    char name[20];
    int age;
};

int main() {
    using namespace std;
    MyStruct* temp = new MyStruct; //new 연산자를 통해, 구조체를 동적으로 생성
    cout << "당신의 이름: ";
    cin >> temp->name; //화살표 멤버 연산자

    cout << "당신의 나이: ";
    cin >> (*temp).age; // 화살표 대신, (*구조체 변수). 도 가능

    cout << "안녕하세요! " << (*temp).name << "씨!\n";
    cout << "당신은 " << temp->age << "살 이군요!";

    cout << temp <<endl; //0x600000289180
    cout << (int*)temp << endl; //0x600000a5d180
    cout << &temp << endl; //0x16b0d75f8
//    cout << temp[0] << endl;
    cout << temp->name << endl; //fish
    // char 배열이므로 저장된 건 첫번째 요소의 주소값이지만 cout을 통해 해당 주소값부터 null이 있는 주소값까지의 char들을 출력
    cout << *(temp->name) << endl; //name에 영단어를 입력하면 첫번째 알파벳 f가 나옴(한글은 ?로 뜸)
    cout << &(temp->name) << endl; //0x600000289180
    
    /*번외*/
    cout << (int*)(temp->name) << endl; //0x600000289180
    // !=(int)(*변수) : 변수가 참조하는 대상을 int로 typecast한 결과

}

→ 여기서도 temp 구조체 포인터 안에 저장된 name은 배열이므로, 해당 배열 첫번째 원소의 주소값이 저장되어 있다.

따라서,

- temp->name: 해당 주소값~null의 주소값 범위에 해당하는 chars를 문자열로 출력해주고 : fish

- *(temp->name): 해당 배열 첫번째 원소의 주소값이 가리키는 char를 출력해주고 : f

- &(temp->name): 해당 배열 첫번째 원소의 주소값의 주소값을 출력해주고 : 0x6000~

 

- (int*)(temp->name): 얘는 아직 잘 모르겠다,, ㅎㅎ (int*)pointer는 pointer의 값 자체를 주소값으로 선언해준다는 건 읽었는데, 위 결과와 매칭을 잘 못 시키겠음...

"pointer를 이용하여 출력된 값은 정수 값이고 (int *)pointer를 이용해서 나오는 값은 번지 값이다"
출처:https://sunder4.tistory.com/150

728x90
728x90

c++은 객체지향 프로그래밍

- 컴파일 시간이 아닌 '실행 시간'에 어떠한 '결정'을 내릴 수 있다.

 

e.g.) 배열을 생성할 때,

재래적 절자척 프로그래밍: 배열의 크기를 미리 결정해야 함

객체지향 프로그래밍: 배열의 크기를 실행 시간에 결정할 수 있음-> 값이 변할 수 있는 변수배열의 크기를 결정할 수 있음

 

일주일에 한 번만 200개의 공간이 필요하다면, 재래적 절차적 프로그래밍의 경우 남은 6일 간 180이 낭비됨

객체지향 프로그래밍에서는, 20을 유지하다가, 필요에 따라 간헐적으로 200으로 늘림


포인터

변수 선언 과정에서, 변수의 이름을 통해 변수를 관리하는 것이 아니라, 저장할 데이터를 다루는 '주소'이름을 붙임

즉, 포인터는 포인터의 이름 자체주소를 나타냄.

이를 간접값 연산자, 간접 참조 연산자 * 로 나타냄

 

1-1. 포인터 선언, 변수와 연결

#include <iostream>

using namespace std;

int main(){

    int val = 3;
    cout << &val << endl; //val이라는 변수가 저장된 메모리 주소 : 0x16b0135fc

    /*포인터 선언하기*/
    //포인터는 개념적으로 정수와 다른 개념 (수가 아닌 위치를 나타냄)
    //포인터에는 어떤 정수를 대입하는 것이 아무런 의미가 없음
    //대신, 포인터에 어떠한 변수의 주소를 대입함으로써,
    // 포인터를 이용하여 해당 주소의 값에 직접 변화를 줄 수 있음

    int *a; // c style
    int* b; // c++ style
    int* c,d; //c는 포인터 변수, d는 int형 변수로 선언됨
	
    /*e의 주소 저장을 위해 f가 선언됨*/
    int e = 6;
    int* f; //그 자체로 주소를 나타냄

    f = &e; //포인터 f와 변수 e가 연결됨 -> f가 바뀌면 e도 연동되어 바뀜
    //위 코드는 *f = e;로 대체할 수 없음(error)

    cout << "e의 값 " << e << endl; //e의 값 6
    cout << "*f의 값 " << *f << endl; //*f의 값 6
    cout << "e의 주소 " << &e << endl; //e의 주소 0x16fcf75d8
    cout << "*f의 주소 " << &*f << endl; //*f의 주소 0x16fcf75d8
    cout << f << endl; //0x16f7e35d8

    *f = *f + 1; //주소에 해당하는 값에 직접 변화 주기
    cout << "이제 e의 값은 " << e << endl; //이제 e의 값은 7
    cout << *f << endl; // 0x16d2ef5d8 -> 값이 저장된 주소는 바뀌지 않음

}

- 즉, 포인터는 특정 데이터의 '주소'저장을 위한 용도로 생성됨.

 

1-2. new를 통해 포인터 선언 및 초기화

#include <iostream>

using namespace std;

int main(){

    /*이전시간에 활용한 방법: 주소에 접근할 때 a와 b 둘을 통해 모두 접근 가능*/
    int a;
    int* b = &a;

    //new 연산자
    /*
     * 어떤 데이터형을 원하는지 new 연산자에 알려주면,
     * new 연산자는 '그'에 알맞은 크기의 메모리 블록을 찾아내고
     * 그 블록의 '주소'를 리턴함*/

    int* pointer = new int; //int형을 위한 새로운 메모리가 필요함을 알려줌
    //new는 int임을 확인하여 몇 바이트가 필요한지 계산(e.g. 4btyes)
    //해당 바이트를 저장할 수 있는 메모리 블록을 찾아 해당 주소를 반환
    
 
}

1-3. 배열 형식의 포인터 선언 및 초기화

#include <iostream>

using namespace std;

int main(){
    
    double* p3 = new double[3]; //double형 데이터 3개를 저장할 수 있는 공간을 대입
    p3[0] = 0.2; //p3를 배열 이름처럼 취급
    p3[1] = 0.5;
    p3[2] = 0.8;

    cout << "p3[1] is " << p3[1] << ".\n"; //p3[1] is 0.5.

    p3 = p3 + 1; //배열에 값을 더하는 행위
    //원래 배열 이름은 값을 변경할 수 없음.
    //그러나 포인터는 변수처럼 사용할 수 있어서, 값 변경 가능.
    /* double 데이터 3개를 저장하는 공간이므로,
     p3에 1을 더한다는 것 =뒤에 있던 double형 데이터 공간 1개를 앞당기는 것
     p3[0] -> 0.5를 point
     p3[1] -> 0.8을 point
     */

    cout << "Now p3[0] is " << p3[0] << " and "; //Now p3[0] is 0.5 //기존 p3[1]의 값
    cout << "p3[1] is " << p3[1] << ".\n"; // p3[1] is 0.8.


}

 

 

2. 포인터를 delete

- delete 연산: 사용한 메모리를 다시 메모리 풀의 영역으로 환수 => 환수된 메모리는 프로그램의 다른 부분이 다수 사용할 수 있음
- delete ps; //new를 사용한 다음에는 반드시 delete를 사용해야 함
- delete 없으면 ; 대입은 되었지만 사용되지 않는 메모리 누수 발생 -> 프로그램 먹통

#include <iostream>

using namespace std;

int main(){

    /*delete 연산: 사용한 메모리를 다시 메모리 풀의 영역으로 환수*/
    //환수된 메모리는 프로그램의 다른 부분이 다수 사용할 수 있음

    int* ps = new int;
    //포인터변수 ps를 선언, 메모리 사용
    delete ps; //new를 사용한 다음에는 반드시 delete를 사용해야 함
    //delete 없으면 ; 대입은 되었지만 사용되지 않는 메모리 누수 발생 -> 프로그램 먹통

    /*delete를 사용하는 네 가지 규칙*/
    //1. new로 대입하지 않은 메모리는 delete로 해제할 수 없음
    //2. 같은 메모리 블록을 연달아 두 번 연속 delete로 해제할 수 없음 (한번으로 충분)
    //3. new[]와 같이 대괄호 형식 메모리 대입할 시, delete 역시 delete[]로 해제
    //4. 대괄호를 사용하지 않았다면, delete

    double* p3 = new double[3]; //double형 데이터 3개를 저장할 수 있는 공간을 대입
    p3[0] = 0.2; //p3를 배열 이름처럼 취급
    p3[1] = 0.5;
    p3[2] = 0.8;

    cout << "p3[1] is " << p3[1] << ".\n"; //p3[1] is 0.5.

    p3 = p3 + 1; //배열에 값을 더하는 행위
    //원래 배열 이름은 값을 변경할 수 없음.
    //그러나 포인터는 변수처럼 사용할 수 있어서, 값 변경 가능.
    /* double 데이터 3개를 저장하는 공간이므로,
     p3에 1을 더한다는 것 =뒤에 있던 double형 데이터 공간 1개를 앞당기는 것
     p3[0] -> 0.5를 point
     p3[1] -> 0.8을 point
     */

    cout << "Now p3[0] is " << p3[0] << " and "; //Now p3[0] is 0.5 //기존 p3[1]의 값
    cout << "p3[1] is " << p3[1] << ".\n"; // p3[1] is 0.8.

    p3 = p3 -1;
    delete[] p3;
}

reference: https://inf.run/4ttD

728x90
728x90

1. 배열과 문자열

#include <iostream>

using namespace std;

int main(){
//c++는 복합데이터형을 제공 : 사용자 정의대로 새로운 데이터형을 만들 수 있음
//복합데이터형 : 기본 정수형과 부동소수점형의 조합
//ex) 배열(array) : 같은 데이터형의 집합

/*배열(array)
 * typeName arrayName[arraySize]

 * 배열 초기화를 선언 이후 나중에 할 수 없음, 배열을 다른 배열에 통째로 대입할 수 없음
 * short month[5] = { ... };
 * short year[12] = { ... };
 * year = month; 불가능함
 * 초기화 값의 개수를 배열 원소의 개수보다 모자라게 제공할 수 있음
 * 배열을 부분적으로 초기화하면 나머지 원소는 모두 0으로 설정
 * 명시적으로 첫 번째 원소만 0으로 초기화하면, 나머지 원소들은 모두 0으로 초기화됨
 * 배열 사이즈를 명시하지 않으면 초기화 시의 원소 개수로 배정됨
 * */

short month[12]; //새로운 배열 선언 //month 배열의 원소는 최대 12개, 모두 short형
short month2[12] = {1, 2, 3};

cout << month2[0] << endl; //인덱스 통해 원소 출력

/*
 * 문자열: 문자의 열
 * char 리스트와 쌍따옴표로 문자열(스트링) 표현 가능
 * */

char a[5] = {'H', 'e', 'l', 'l', 'o'};
//이보다 더 효율적으로 문자열 정의하는 방법
char b[] = "Hello"; //쌍따옴표는 명시적으로 null문자를 포함하므로 스트링 표현 가능

cout << b;

}

2-1. 사용자 입력(cin)

#include <iostream>
#include <cstring>

using namespace std;

int main(){
    //<cstring>의 헤더 파일에 포함되어 있는 <strlen> : 문자열의 길이를 반환하는 함수
    //<sizeof> : 변수의 크기를 반환하는 함수
    const int Size = 15;
    char name1[Size];
    char name2[Size] = "C++programming";

    cout << "안녕 나는" <<name2;
    cout << "이야~\n";
    // cin >> name1; //사용자의 입력을 오른쪽 변수에 저장 //input느낌
    //문제는 화이트스페이스를 반영하지 못하고 그 전으로 자름
    /*이를 해결하는 것이 get & getline*/
    cin.getline(name1, Size); //(변수, 크기) //get도 마찬가지

    cout << "음 " << name1 << "씨, 당신의 이름은 ";
    cout << strlen(name1) << "자입니다만\n";
    cout << sizeof(name1) << "바이트 크기에 저장되었습니다.\n";
    cout << "이름이" << name1[0] << "자로 시작하시는군요.\n";
    name2[3] = '\0'; //문자열 출력 시 null 문자까지만 출력함
    cout << "제 이름의 세 문자는" << name2 << "입니다.";

}

2-2. string

#include <iostream>

using namespace std;

int main(){

    //c++에서 문자열을 다루는 방법 중 하나인 'string'
    /*
     * c스타일로 string 객체를 초기화할 수 있음
     * cin을 사용하여 string 객체에 키보드 입력을 저장
     * cout을 사용하여 string 객체를 보여줌
     * 배열 표기를 사용하여 string객체에 저장된 개별 문자에 접근 가능
     * => 문자열과 속성이 비슷한데,
     * => string을 통해서는 배열을 다른 배열에 통째로 대입할 수 있음 
     * => 배열 크기를 미리 지정하지 않아도 됨 */

    char char1[20];
    char char2[20] = "jauar";
    string str1;
    string str2 = "panda";
    // char1 = char2; //값이 들어가지 않음
    str1 = str2; //제대로 대입 됨
    cout << str1;

}

3. 구조체(struct)

#include <iostream>

using namespace std;

int main(){

    //c++만의 특장점: 사용자의 정의 및 입력대로 원하는 데이터를 만들 수 있음
    /*정말 중요한 '구조체'  (복합데이터형 중 하나)*/
    //배열: 같은 데이터형의 집합
    //구조체: '다른 데이터형이 허용되는' 데이터의 집합

    //축구선수
    struct MyStruct {
        string name;
        string position;
        int height;
        int weight;
    };

    MyStruct A; //A라는 이름의 MyStruct형의 데이터를 만든 것 //약간 class느낌..?
    A.name = "Son";
    A.position = "Striker";
    A.height = 183;
    A.weight = 77;

    MyStruct B = {
            "Min", "defender", 160, 49
    };

    cout << B.weight << endl;

    //구조체 생성 시 중괄호 뒤에 생성할 구조체명을 선언하면
    //실제 선언 시 구조체 형식을 전부 명시하지 않아도 됨

    //또한, 구조체에 포함된 모든 변수를 선언하지 않아도 됨, 자동으로 0이 됨

    struct Ming {
        string firstname;
        string lastname;
    } minji;

    minji.firstname = "minji";
    minji.lastname = "Jo";

    cout << minji.firstname << endl;

    //배열을 선언하듯 구조체를 선언할 수 도 있음
    Ming minfamily[2] = {
            {"jungah", "Han"}, //첫번째 인덱스에 해당하는 정보
            {"seokhyeon", "Jo"} //두번째 인덱스에 해당하는 정보
    };

    cout << minfamily[0].lastname;

}

4. 공용체 (union)

#include <iostream>

using namespace std;

int main(){

    //c++에서 지원하는 복합데이터형 중 : 공용체 & 열거체
    /*공용체(union): 서로 다른 데이터형을 한 번에 한 가지만 보관할 수 있음 */

    union MyUnion{
        int intVal;
        long longVal;
        float floatVal;
    };

    MyUnion test;
    test.intVal = 3;
    test.longVal = 33;
    test.floatVal = 3.3;

    cout << test.intVal << "\n" << test.longVal << "\n" << test.floatVal << endl;
    //1079194419
    //1079194419
    //3.3
    //여러 데이터형을 사용할 수 있지만, 동시에 사용은 불가능
    // -> 메모리 절약 가능 (운체, 하드웨어 등에 주로 활용됨)


}

5. 열거체(enum)

#include <iostream>

using namespace std;

int main(){

    //c++에서 지원하는 복합데이터형 중 : 공용체 & 열거체

    /*열거체(enum): 기호 상수(한번 선언된 이후 변경 불가)를 만드는 또다른 방법*/
    enum spectrum {red, orange, yellow, green=5};
    //1. spectrum을 새로운 데이터형 이름으로 만듦
    //2. red, orange, yellow, green의 열거자를 다양한 수를 0~3까지 정수 값을 각각 나타내는 기호 상수로 만듦
    //-> 즉 문자로 정수를 사용하게 됨
    //-> 사용된 열거자를 상수로 관리함
    // 명시적으로 정수 값을 직접 대입할 수 있음 (자동배정되는 값을 사용하지 않으려면.)
    spectrum a = orange; //열거체 생성 시, 열거자만 대입할 수 있음
    //열거자끼리 직접 산술연산 불가능 (orange+yellow//error)

    int b;
    b = green; //열거자를 int 변수에 대입하면 산술연산 사용 가능
    b = green + 3;


    cout << b << endl;

}

 

reference: https://inf.run/4ttD

728x90
728x90

1. 변수 선언과 규칙

#include <iostream>

using namespace std;

int main(){
    //1. 변수

    /*
     1. 변수의 자료형
     2. 변수의 이름
     3. 변수가 어디에 저장되는가?(메모리 영역) - 컴파일러가 직접 결정해줌
     -> 주소값 알려면 변수 앞에 &붙이면 됨
     */
    int a; //int라는 자료형의 a라는 변수명
    a = 7; //int라는 자료형 명시했으므로 가능
    /*
     * 변수명의 규칙
     1. 숫자로 시작할 수 없음
     2. c++에서 사용하고 있는 키워드는 사용할 수 없음
     3. white space(space/tab/enter)를 사용할 수 없음
     */
    { int b;
        {
            //int a; 하단 블록에서 정의되면, 상단에서 사용될 수 없음
            b=3; //상단 블록에서 정의되었으므로 가능
        }
        //a=5;
    }

    int b = 3; //초기화 : 선언하자마자 대입

    cout << a << b; //73

    //변수는 사용되기 이전에 정의되어야 함

    return 0;
}

 

2. 정수형 자료형과 실수형 자료형

#include <iostream>
#include <climits>
using namespace std;

int main(){
    /*
     * 정수형: 소수부가 없는 수 -> 음의 정수, 0, 양의 정수
     * */
    //short, int, long, long long
    //메모리가 한정되어 있는데, 정수 크기와 정수 개수의 스펙트럼이 넓다 보니
    //이를 효율적으로 활용하기 위해 정수형을 네 가지의 types로 나누었음

    int n_int = INT_MAX; //이는 <climits>에서 정의된 int의 max값에 대한 '상수'
    short n_short = SHRT_MAX;
    long n_long = LONG_MAX;
    long long n_longlong = LLONG_MAX;

    cout << "int는 " << sizeof n_int << "바이트이다." <<endl; //4바이트
    cout << "이 바이트의 최대값은 " << n_int << " 이다." <<endl;

    cout << "short는" << sizeof n_short << "바이트이다." <<endl; //2바이트
    cout << "이 바이트의 최대값은" << n_short << " 이다." <<endl;
    //short 자료형은 음의 정수를 표현할 수 있음 : ~32768 ~ 32767

    cout << "long은" << sizeof n_long << "바이트이다." <<endl; //4바이트
    cout << "이 바이트의 최대값은" << n_long << " 이다." <<endl;

    cout << "long long은" << sizeof n_longlong << "바이트이다." <<endl; //8바이트
    cout << "이 바이트의 최대값은" << n_longlong << " 이다." <<endl;

    unsigned short b;
    //음의 정수형을 사용하지 않을 예정이므로, 범위를 0~655535까지 넓힐 수 있음
    b = -1;
    cout << b << endl; //65535


    /*
     * 실수형: 소수부를 저장할 수 있음
     */
    float a = 3.14; //3.14
    int c = 3.14; //3

    cout << a << endl << c << endl;

 

3. char형과 bool형

#include <iostream>

using namespace std;

int main(){
    //char: 작은 문자형-아스키코드 // 아스키코드로 표현할 수 있는 문자 내에서만
    int a = 77;
    char b = a; //M
    char c = 'a'; //a
    cout << b << c << endl;

    //큰따옴표로 표현되는 문자열은 char형로 사용될 수 없음!!!
    //c++에서 문자 출력하고자 할 때, 문자를 정수로 저장하는데,
        //이를 위해 어디까지가 문자인지 알아야 함
    //한 개의 문자가 아닌 긴 문자열을 출력하고자 할 때는, 어디까지가 문자인지 표시하는 null 문자(\0)가 필요함
        //큰 따옴표는 명시적으로 null 문자가 표현된 것 -> string 자료형을 위해 사용

    char d[] = {'a', 'b', 'c'};
    cout << d << endl; //abc�aMM //우연치않게 null을 만날 때 까지 출력을 반복

    char e[] = {'a', 'b', 'c', '\0'};
    cout << e << endl; //abc

    //char f = "a" //'a'와 null 문자를 함께 저장한 건데, char은 한 개의 문자만 포함해야 하므로, 오류 발생

    //한글은 char-아스키코드에서 지원해주지 않음, 유니코드로만 표현됨
        //c-wput, location, string형 사용 등

    /*
     * bool 타입: 숫자를 0 혹은 1로만 저장, 0이외 모든 수는 1로 저장됨
     * */

    bool g = 0; //0
    bool h = 1; //1
    bool i = 10; //1

    cout << g << endl << h << endl << i << endl;


}

3. 상수형과 자료형변환

int main(){

    /*상수: 바뀔 필요가 없거나, 바뀌어서는 안 되는 수 -> 변수형까지 선언해야 함*/
    const float PIE = 3.1415926;
    int r = 3;
    float s3 = r*r*PIE;

    cout << s3; //28.2743

    /*데이터형 변환
    1. 특정 데이터형의 변수에 다른 데이터형 값을 대입했을 때
     2. 수식에 데이터형을 혼합하여 사용했을 때
     3. 함수에 매개변수를 전달할 때
     */

    int a = 3.14;
    cout << a << endl; //3 //자동으로 자료형변환

    //강제적으로 데이터형 변환
    //typeName(a) //바꿀데이터형(변수) //(바꿀데이터형)변수
    //c++ : static_cast<typeName>

    char ch = 'M';
    cout << int(ch) << endl << static_cast<int>(ch) << endl << ch;

    return 0;
    
}

4. 산술연산자와 auto

#include <iostream>

using namespace std;

int main(){

    /*
     연산자: + - * / %
     */

    // '/'에서 두 개의 피연산자가 모두 정수이면 '몫' 계산임
    // 계산에서 둘 중 하나가 실수형이면, 결과도 실수형으로 나옴
    int a = 3 + 2;
    cout << a; //5

    //연산 시 일반대수학의 우선순위를 따름 (곱하기, 나누기 > 더하기, 빼기)
    //곱하기와 나누기 간 순서는... 구글링해보기!

    /*auto*/
    auto n = 100; //int형으로 자동 지정
    //그러나 auto로 인해 의도와 다른 지정이 발생할 수 있으므로 사용 지양

}

 

 

728x90
728x90
#include <iostream> //전처리 지시자

/*
c++에서 함수를 사용 하고자 한다면
반드시 그 함수(ex.cout; 출력)의 원형을 미리 정의 하여야 한다
*/

using namespace std;
//세미콜론 ; 은 종결자의 역할: 한 문장의 끝으로 인식(엔터키 필요 없음)
//using namespace std가 없다면, 뒤 cout과 endl 앞에 'std::'를 추가로 붙여야 함

int main(){
    //c++ 코드에는 반드시 main의 이름을 가지고 있는 함수가 있어야 한다

    cout << "Hello, World!" << endl;
    //cout : 출력
    //꺽쇠 두 개 : 데이터의 흐름, 데이터의 방향을 나타냄
    //endl : 줄바꿈
    //std::cout<< "" <<std::endl;

    return 0;
}
  • 전처리 지시자: include <iostream>
  • 세미콜론을 통한 문장의 종결
  • using namespace std
    • std::
  • main의 이름을 가진 함수의 필요성
  • cout
  • 꺽쇠를 통한 데이터 흐름 표시
  • endl

reference: https://inf.run/JGFT

 

728x90

+ Recent posts