로보테크AI

융합_로보테크 AI 자율주행 로봇 개발자 과정-26/02/11

steezer 2026. 2. 11. 18:30

표현식: 계산할 때 사용하는 식

ㄴ연산자, 피연산자로 구성

1 + 2

피연산자 (1 ,2)

연산자 (+)

 

상수 표현식: 상수로만 이뤄진 단순한 표현식

 

단항 연산자 표현식: 연산자와 피연산자 일대일 매칭되는 표현식

ㄴ증감 연산자: ++(+1), --(-1)

ㄴㄴ전위 연산: ++a: a값을 1만큼 증가시킨 후에 대입(선연산 후처리)

ㄴㄴ후위 연산: a++: a값을 대입시킨 후 1만큼 증가(후연산 선처리)

#include <iostream>

using namespace std;

int main() {
  int a = 0;    // a 최초 값 0
  int b = 0;    // b 최초 값 0
  int a_prefix;
  int b_postfix;

  a_prefix = ++a;     // a값을 1만큼 증가한 후에 a_prefix에 a값을 넣음
  b_postfix = b++;    // b값을 b_postfix에 넣은 후에 b값을 1만큼 증가함

  cout << "a = " << a << ", " << "a_prefix = " << a_prefix << endl;
  cout << "b = " << b << ", " << "b_postfix = " << b_postfix << endl;

  return 0;
}

 

 

논리 NOT

0은 false, 0 외에는 모두 true로 취급

#include <iostream>

using namespace std;

int main() {
  unsigned int value = 0x00000000;   // 0을 16진수(hex)로 표현한 값

  value = ~value;
  cout << hex << value << endl;

  return 0;
}

1의 보수?

비트열이 반전된 숫자

0001의 1의 보수는

1110이다

 

2의 보수?

어떤 양수가 있을 때, 2의 보수를 취해야 정확한 음수 표현이 된다

비트열이 반전된 숫자 +1

 

    0001 0101 (21)

 + 1110 1011 (-21)

------------------------

    0000 0000

 

※1110 1011이 -21인지 235인지 구분은 자료형 signed, unsigned 기준으로 구분

    2의 보수에선 맨 앞 비트가 1이면 음수, 0이면 양수

    ㄴ 자료형 해석 방식에 따라 달라짐(비트는 같음)

 

비트 연산자

~: 단항 연산자, 각 비트의 값 반전

 

&: 이항 연산자, 대응 비트에 대해 AND 연산

ㄴ    0001 0101 (21)

ㄴ + 0000 0001 (1)

ㄴ    0000 0001

 

|: 이항 연산자, 대응 비트에 대해 OR 연산

ㄴ    0001 0101 (21)

ㄴ + 0000 0001 (1)

ㄴ    0000 0101

 

^: 이항 연산자, 대응 비트에 대해 XOR 연산

ㄴ    0001 0101 (21)

ㄴ + 0000 0001 (1)

ㄴ    0001 0100

 

#include <iostream>
#include <bitset>  

using namespace std;

int main() {
  int a = 13;
  int b = 27;
  int c = a & b; //  비트 연산자 AND 사용

  cout << "a = " << bitset<8>(a) << " : " << a << endl;
  cout << "b = " << bitset<8>(b) << " : " << b << endl;
  cout << "c = " << bitset<8>(c) << " : " << c << endl;

  return 0;
}

 

N진법

평소에 숫자 셀 때 사용하는 표현법 10진수

 

2진수

ㄴ숫자 표현 기호가 2개

ㄴ=>0 1 10 11 100 101 110 111

1111 1111 -> 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = 255

 

시프트 연산

오른쪽 시프트(>>), 왼쪽 시프트(<<)

연산: 변수 >> 이동 비트 수, 변수 <<이동 비트 수

 

삼항 연산자 표현식

조건식 ? 참일_때_표현식 ; 기젓일_때_표현식

 

 

포인터와 메모리 구조

포인터: 메모리 주소를 저장하는 변수

포인터 변수의 크기는 데이터 형식과 관련 없음

모든 포인터 변수의 크기는 같음

포인터 변수를 선언할 때 데이터 형식을 지정하는 이유는 데이터의 형식을 명시하기 위해

 

다중 포인터

포인터를 가리키는 포인터

 

배열과 포인터

배열 선언

ㄴ 자료형 배열_이름[크기] = {값1, 값2, 값3, 값4};

#include <iostream>

using namespace std;

int main() {
  int lotto[45] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
                    16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
                    31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45 };

  cout << "Today's Lotto : "
    << lotto[0] << ", " << lotto[7] << ", " << lotto[15] << ", "
    << lotto[27] << ", " << lotto[36] << ", " << lotto[44] << endl;

  return 0;
}

배열의 이름은 포인터처럼 쓸 수 있다.

 

동적 메모리 할당

실행 중에 힙 영역에 메모리를 만들고, 사용 후 반드시 직접 해제해야 하는 메모리 관리 방식

 

원래 하던 방식

int number = 10; //지역 내에서 유효하게 살아있는 변수

 

새로운 방식

int *ptr_number = new int; //동적 메모리에 별도로 존재하는 변수

 

동적 메모리 할당(new)을 하면

자유롭게 썼다 지웠다 할 수 있는 메모리 공간 사용

이때 해당 메모리 공간의 이름을 힙영역(heap)이라 한다

 

#include <iostream>
#include <string>

using namespace std;

int main() {
  int customer_num = 0;

  cout << "오늘 방문 손님: ";
  cin >> customer_num;   // 손님 수 입력

  string* bread = new string[customer_num];  // 손님 수만큼 string 배열 생성

  // 입력받은 손님 수만큼 반복
  for (int i = 0; i < customer_num; i++) {
    bread[i] = "빵_" + to_string(i);       // ‘빵_숫자’ 형태로 배열에 저장 
  }

  cout << endl << "--생산된 빵--" << endl;
  for (int i = 0; i < customer_num; i++) {
    cout << *(bread + i) << endl;   // 생산된 빵 출력(포인터 연산 참고)
  }

  delete[] bread;   // string 배열 삭제

  return 0;
}

 

포인터를 다룰 때 주의할 점

포인터를 역참조하기 전에 포인터가 유효한 메모리를 가리키는지 확인

할당된 메모리의 범위를 벗어나는 포인터 연산은 피해야 한다.

할당 해제된 메모리를 역참조하지 않아야 한다.

 

 

함수와 구조체

 

함수: 특정 작업을 수행하는 코드 집합

int반환 형식 func함수 이름 ()매개변수 {}함수 몸체

 

call-by-value: 값을 전달하는 호출 방식

#include <iostream>

using namespace std;

void change_negative(int _val) {
  if (_val > 0) {
    _val = -_val;
  }
}

int main() {
  int a = 3, b = -3;

  cout << "a : " << a << endl;
  cout << "b : " << b << endl;

  change_negative(a);
  change_negative(b);

  cout << "change_negative(a) : " << a << endl;
  cout << "change_negative(b) : " << b << endl;

  return 0;
}

 

call-by-reference: 주소를 전달하는 호출 방식

#include <iostream>

using namespace std;

// 포인터 변수를 매개변수로 사용
void change_negative(int* _val) {
  if (*_val > 0)
  {
    *_val = -(*_val);
  }
}

int main() {
  int a = 3, b = -3;

  cout << "a : " << a << endl;
  cout << "b : " << b << endl;

  change_negative(&a);	// a 변수의 주솟값을 매개변수로 전달
  change_negative(&b);	// b 변수의 주솟값을 매개변수로 전달

  cout << "change_negative(a) : " << a << endl;
  cout << "change_negative(b) : " << b << endl;

  return 0;
}

 

구분 Call-by-Value Call-by-Reference
전달 값 복사 주소/참조 전달
원본 변경 불가능 가능
안전성 높음 주의 필요
성능 복사 비용 있음 효율적

 

구조체는 하나 이상의 변수를 묶어 새로운 자료형으로 정의 가능

구조체는 서로 관련된 여러 변수를 한 곳에 모으는 방법

구조체 내의 각 변수는 구조체의 멤버라 함

구조체를 생성하려면 struct 키워드를 사용하고 각 멤버를 중괄호 안에 선언

선언 후에는 구조체 변수의 이름을 지정'

구조체의 멤버에 접근하려면 점 구문(. .)을 사용

 

#include <iostream>

using namespace std;

struct Person {
  string name;    // 이름
  int age;        // 나이
  float height;   // 키
  float weight;   // 몸무게
};

void check_age(Person* _adult, int _count) {
  for (int i = 0; i < _count; i++) {
    if (_adult[i].age >= 25) {
      cout << "name : " << _adult[i].name << endl;
      cout << "age : " << _adult[i].age << endl;
      cout << "height : " << _adult[i].height << endl;
      cout << "weight : " << _adult[i].weight << endl;
    }
  }
}

int main() {
  Person adult[3] = {
      {"Brain", 24, 180, 70},
      {"Jessica", 22, 165, 55},
      {"James", 30, 170, 65},
  };

  check_age(adult, 3);

  return 0;
}

 

개념 설명
struct 여러 데이터를 하나로 묶는 사용자 정의 타입
구조체 배열 같은 구조체 객체를 여러 개 저장
. 멤버 접근 연산자
포인터 + 배열 구조체 배열은 함수에서 포인터로 전달됨

 

지역 변수: 함수 내부에 선언된 변수, 해당 블록 내에서만 효력있음

전역 변수: 전역 범위에 선언된 변수, 해당 파일 전체에서 효력있음

 

지역 변수에 static 키워드 사용 시 자동 지속에서 정적 지속으로 변수 유효 범위가 바뀜

ㄴ자동 지속: 블록에 들어갈 때 생성되고, 블록을 벗어나면 자동으로 소멸하는 변수의 수명

ㄴ정적 지속: 프로그램 시작 시 생성되어 프로그램 종료 시까지 유지되는 변수의 수명

 

 

정적 변수의 수명주기가 지역 변수와 다른 이유

 

지역 변수와 정적 변수는 메모리 위치가 다름

지역 변수는 메모리 구조에서 스택 영역에 조정

스택 영역의 변수는 함수가 호출될 때 메모리에 할당되어 종료 될 때 메모리에서 해제

static으로 선언된 정적 변수는 데이터 영역에 저장

데이터 영역은 프로그램이 시작할 때 할당되며 종료할 때 해제됨

 

 

메모리 목적에 따른 구분

 

1.코드 영역: 프로그램의 코드를 저장하는 영역

 

2.데이터 영역: 전역 변수와 정적 변수를 저장하는 영역

ㄴ프로그램이 종료될 때 변수들이 소멸되는 특징

 

3.힙 영역: 동적 할당된 데이터를 저장하는 영역

ㄴ필요에 따라 영역 할당 및 해제를 지정할 수 있다는 특징이 있음

 

4.스택 영역: 지역변수(매개변수)를 저장하는 영역

ㄴ지역이 유지되는 동안에 데이터가 유지된다는 특징이 있음

 

 

const: 값을 바꿀 수 없는 변수/변수를 상수화

 

const int* ptr는 값을 못 바꾸는 포인터
int* const ptr는 주소를 못 바꾸는 포인터

 

 

일반 변수:

값 저장하는 변수

 

포인터 변수:

주소를 저장하는 변수

 

래퍼런스:

변수에 붙인 또 다른 이름

#include <iostream>

using namespace std;

// 매개변수를 레퍼런스 변수로 선언
void swap(int& ref_a, int& ref_b) {
  // 교환 전 a, b 값
  cout << "[swap func] before swap, ref_a: " << ref_a << "  ref_b : " << ref_b << endl;

  int temp = ref_a;
  ref_a = ref_b;
  ref_b = temp;

  // 교환 후 a, b 값
  cout << "[swap func] after swap, ref_a: " << ref_a << "  ref_b : " << ref_b << endl;
}

int main() {
  int a = 5;
  int b = 10;

  // swap 함수 호출 전 a, b 값
  cout << "[main] before swap, a: " << a << "  b: " << b << endl << endl;

  swap(a, b);

  // swap 함수 호출 후 a, b 값
  cout << endl << "[main] after swap, a: " << a << "  b: " << b << endl;

  return 0;
}