c++ 언어 A 부터 Z까지 - 두번째

 



인사 


안녕하세요. 오늘은 C++ 언어 학습 일지 두 번째 시간으로, 구조체(struct), 배열, 그리고 포인터 개념을 중심으로 공부한 내용을 정리해 보려 합니다.

이번 학습에서는 이전보다 조금 더 깊은 고민과 시도를 하며 학습에 임했고, 특히 포인터 개념과 값의 전달 방식 등 헷갈릴 수 있는 부분에 집중했습니다. 여전히 어렵게 느껴지는 부분도 있지만, 점점 이해되는 부분이 늘어나고 있어 다행이라고 생각합니다.


구조체(struct) 


저는 이전에 Go 언어를 경험한 적이 있어서 구조체 개념이 낯설지는 않았습니다. Python의 dict와 유사한 면도 있어 개념 자체는 금방 익숙해졌습니다. 다만 C++에서 구조체를 함수에 전달하는 방식은 Go와 다소 차이가 있어 헷갈렸습니다.


package main

import "fmt"

type Data struct {
  Num int
}

func UppNumber(data *Data) {

  for i := 0; i < 100; i++ {
    data.Num += i
  }

}

func main() {

  var (
    data Data
  )

  UppNumber(&data)

  fmt.Println(data.Num)
}

   

Go에서는 포인터(*)를 명시적으로 선언하고 주소를 넘겨주면 함수 내부에서 해당 값을 직접 수정할 수 있습니다.


#include <stdio.h>

struct Data {
  int num;
};

void UppNumber(struct Data& data) {
 
  int i;
  for (i = 0; i < 100; i++) {
    data.num += i;
  }
 
}

int main(void) {
 
  struct Data data;
 
  UppNumber(data);
 
  int i;
  printf("%d", data.num);
 
  return 0;
}


C++에서는 함수 매개변수에 참조형(&)을 사용해 원본 값을 직접 수정할 수 있도록 합니다. 이 부분은 C++의 중요한 특징 중 하나로, 다른 언어와의 차이를 이해하는 데 좋은 경험이었습니다.


배열 


C++로 알고리즘 문제를 해결하는 것이 주된 학습 목표인 저는, 배열을 다루는 부분에서 많은 어려움을 느꼈습니다. 특히 Python과 비교했을 때 불편하다고 느낀 점들이 몇 가지 있었습니다.


1. 배열 크기 선언의 불편함

Python에서는 append()를 통해 크기를 동적으로 조절할 수 있지만, C++에서는 배열의 크기를 직접 지정해야 하며 동적으로 생성하기 위해 new를 사용해야 합니다.

예를 들어 Python에서는 다음과 같이 자유롭게 배열을 생성합니다:




def InputFunc(arr):

  # n의 값
  m = int(input())

  for _ in range(n):
    a, b = map(int, input().split())
    arr.append([ a, b ])

def MakeMatrix(arr, matrix):

  for arr_data in arr:

    a, b = arr_data
    matrix[a].append(b)


def main():

  n = int(input())

  arr = []
  InputFunc(arr)

  matrix = [ [] for _ in range(n + 1) ]
  MakeMatrix(arr, matrix)

  for i in range(1, n + 1):

    print(f"{i}번째 원소들 ", end = " ")

    for matrix_data in matrix[i]:
      print(f"{matrix_data}", end = " ")

    print()


반면, C++에서는 아래와 같은 절차가 필요합니다:

#include <stdio.h>

void InputFunc(int m, int** arr) {

  int i;
  for (i = 0; i < m; i++) {
   
    arr[i] = new int [2];
   
    scanf("%d %d", &arr[i][0], &arr[i][1]);
  }
}

void DeleteArr(int m, int** arr) {
 
  int i;
  for (i = 0; i < m; i++) {
   
    delete[] arr[i];
   
  }
  delete[] arr;
 
}

void MakeMatrix(int m, int n, int** matrix, int** arr, int* cnt) {
 
  int i;
 
  for (i = 0; i < n + 1; i ++) {
    matrix[i] = new int [n];
    cnt[i] = 0;
  }
 
 
  for (i = 0; i < m; i++) {
   
    int a = arr[i][0], b = arr[i][1];
    matrix[a][cnt[a]] = b;
    cnt[a] += 1;
  }
}

void DeleteMatrixVoid(int n, int* cnt, int** matrix) {
 
  int i;
 
  for (i = 0; i < n + 1; i++) {
   
    delete[] matrix[i];
  }
 
  delete[] matrix;
  delete[] cnt;
 
}

int main(void) {
 
  // n의 값
  int n;
  scanf("%d", &n);
 
  int m;
  scanf("%d", &m);
  int** arr = new int* [m];
  InputFunc(m, arr);
 
 
  // 매트릭스 만들기
  int** matrix = new int* [n+1];
  int* cnt = new int[n+1];
 
  MakeMatrix(m, n, matrix, arr, cnt);
 
 
  // 매트릭스가 잘 갔나 확인하기
  int i;
  int j;
  for (i = 1; i < n+1; i++) {
    int matrix_num = cnt[i];
    printf("%d의 원소들: ", i);
    for (j = 0; j < matrix_num; j++) {
      printf("%d ", matrix[i][j]);
    }
    printf("\n");
   
  }
   
  // 배열 나가기
  DeleteArr(m, arr);
  DeleteMatrixVoid(n, cnt ,matrix);
 
  return 0;
}



(이 코드에 대해 설명을 하자면 n(1이상의 자연수)값을 정하고 m(1이상의 자연수)배열의 정수 a,b (1 <= a, b <= n)를 대입하는데  a -> b로 가는 로직을 작성하는 함수이다. )

배열을 다룰 때는 꼭 메모리를 할당하고 나중에 해제해주는 과정이 필요하기 때문에, 처음 접했을 때 꽤 까다롭게 느껴졌습니다.


2. 배열 메모리 해제


Python이나 Go에서는 배열 메모리를 따로 해제하지 않아도 되지만, C++에서는 명시적으로 메모리를 반환하지 않으면 메모리 누수가 발생할 수 있습니다.


3. 초기값 설정 


C++에서는 배열의 초기값을 지정하지 않으면 '쓰레기 값'이 들어가게 됩니다. 이 부분은 특히 의도하지 않은 동작을 만들 수 있어서 주의가 필요합니다.

#include <stdio.h>

int InputArr(int n, int* arr) {
 
  int i;
  for (i = 0; i < n; i++) {
   
    int a, b;
    scanf("%d %d", &a, &b);
    arr[a] = b;
  }
 
}

int answer(int n, int* arr) {
 
  int i;
  for (i = 0; i < n; i++) {
   
    if (arr[i]) {
      printf("%d %d\n", i, arr[i]);
    }
   
  }
 
}

int main(void) {
 
  // 몇개 입력할려고 하나요?
  int n;
  scanf("%d", &n);
 
  // 배열
  int* arr = new int [n];
  InputArr(n, arr);
 
  // 정답
  answer(n, arr);
 
  delete[] arr;
  return 0;
}





const InputArr = (n, arr) => {

  for (let i = 0; i < n; i++) {

    const a =  Number(prompt())
    const b = Number(prompt())
    arr[a] = b;
  }

}

const Answer = (n, arr) => {

  for (let i = 0 ; i < n; i++) {
    if (Array.from(arr)[i]) {
      console.log(`${i} ${Array.from(arr)[i]}`)
    }
  }

}

const main = () => {
 
  // 초기 설정
  const n = Number(prompt)
  const arr = Array(n).fill(undefined)

  // 데이터 집어넣기
  InputArr(n, arr);

  Answer(n, arr);
}
main()


JavaScript에서는 undefined, Python에서는 None 혹은 값이 없는 것으로 인식되지만, C++에서는 비어있는 값 자체가 없기 때문에 반드시 초기화를 해주는 습관을 들여야겠다고 느꼈습니다.


마무리

이번 학습에서는 구조체와 배열, 그리고 포인터에 대한 기초적인 이해를 다졌습니다. Go나 Python, JavaScript와 같은 언어와 비교하면서 C++의 특징을 익혀가는 과정이 무척 유익했습니다. 특히 메모리 관리나 초기화 습관 등은 C++만의 철저함이 필요한 부분이라는 점을 실감했습니다.

원래는 큐와 스택에 대한 이야기도 함께 정리하려 했지만, 내용이 길어지는 관계로 다음 학습 일지에서 다루려고 합니다. 다음에는 백준 문제를 활용해 큐와 스택의 활용법과 C++과 다른 언어들에서의 차이점을 비교해 보려 합니다.

그럼 오늘도 열심히 학습하며 개발 실력을 차곡차곡 쌓아가 보겠습니다.






개발자 김동완

개발, 비전공, 백엔드, 풀스택, 프로그래밍

댓글 쓰기

다음 이전