리메이크 중/C,C++ 이론 중심

C(&C++) 이론 10. 심화 연산자

라이피 (Lypi) 2023. 1. 4. 17:57
반응형

목록용 썸네일 이미지


내용 참고

C++ 기초 플러스 4판 (Stephen Prata저, 윤성일역 / 성안당)

두두코딩 (https://0xd00d00.github.io/)


연산자(Operator)


Ⅲ. 심화 연산자(operator) 설명

  아래에서 설명하는 연산자들은 C 및 C++ 혹은 프로그래밍이 처음이라면 '이런 것도 연산인가?'라는 생각을 할 수도 있다. 왜냐하면 아래에서 설명하는 연산자들은 대부분 프로그래밍 상황이 아니라면 쓰일 일이 없기 때문이다. 또한 여기서는 서로 다른 연산자인데도 같은 기호가 사용되는 경우가 있는데 이에 대해서는 '연산자 오버로딩(=연산자 재정의)'에서 자세하게 설명한다.

 

  또한 아직 이야기하지 않은 함수나 클래스, 포인터 등과 관련된 내용이 많으므로 이해가 되지 않으면 레퍼런스로 체크만 해두고 나중에 그 내용을 공부할 때 다시 확인하는 것이 좋을 것이다.

 


ⅰ. 괄호와 쉼표 연산자

■ 쉼표 연산자는 피연산자로 수식을 취하는 연산자이다.

■ 괄호를 사용하는 이유는 수식의 괄호 속 연산을 다른 연산보다 우선해서 하게 만들기 위해서이다.

■ 수식을 묶는 괄호 자체는 연산자가 아니지만 연산자 우선순위에 큰 영향을 끼리므로 연산자와 함께 소개했다.

■ 쉼표 연산자를 사용하는 이유는 두 수식을 연결하기 위해서이다.

■ 두 수식을 연결해야하는 경우의 가장 대표적인 예는 for문에서 2개 이상의 제어문자를 사용하고자 할 때가 있다.

■ 쉼표 연산자는 모든 연산자들 중 가장 우선순위가 낮기 때문에 먼저 연산시키기 위해서는 괄호가 반드시 필요하다.

 

괄호와 쉼표 연산자 표


ⅱ. 데이터 타입 및 형변환과 관련된 연산자

■ 아래 표의 첫번째에 소개된 연산자는 C스타일의 형변환 연산자라고 한다.

■ C스타일의 형변환 연산자는 값을 지정한 데이터형으로 해석한 값을 반환하며, 이때 문제가 발생할 수도 있다.

■ 4가지 명시적 cast연산자는 C++에서 추가된 것으로 작성자의 의도 파악을 돕고 안정성을 향상시키는 것이 목적이다.

■ 굉장히 깊이있는 내용에 해당하므로 이 포스트에서는 자세한 설명을 생략했다.

■ typeid연산자도 C++에서 추가된 연산자로 주로 실행 중에 데이터형을 확인할 필요가 있을 때 사용한다.

 

형변환 및 데이터 타입 확인 연산자 표


ⅲ. sizeof 연산자

■ sizeof 연산자는 하나의 값을 피연산자로 갖는 단항 연산자이다.

■ 주로 변수나 배열 등의 크기를 구하기 위해서 사용하나 상수나 문자열 값등의 크기도 구할 수 있다.

■ 배열 이름을 피연산자로 하면 배열의 전체 크기를 반환한다.

■ 하지만 배열 포인터를 피연산자로 하면 포인터 자체의 크기를 반환한다.

■ 또한 배열 포인터 내에 배열의 이름을 넣고 내용 참조 연산자를 사용하면 배열 요소 하나의 크기가 반환된다.

■ 이는 사실 sizeof 연산자가 배열의 이름에 대해서만 특별한 반응을 보이는 것으로 자세한 것은 배열을 다룰 때 설명하겠다.

 

sizeof 연산자


ⅳ. 함수 호출 연산자

■ 함수 호출 연산자는 함수의 이름과 인수를 피연산자로 받는 연산자이다.

■ 정의된 함수가 어떤 형태인가에 따라 인수의 개수는 달라질 수 있으므로 단항 연산자일수도 있고, 다항 연산자일수도 있다.

■ 이러한 성질 때문에 함수 호출 연산자는 인수의 개수에 따라 구분해서 재정의할 수 있다.

■ 사실 함수 호출 연산자는 연산자 재정의를 공부하기 전까지는 연산자임을 몰라도 상관없다고 생각한다. 

■ 또한 함수명이 사실은 포인터의 일종이라는 사실도 아직까지는 중요하지 않을 것이다.

 

함수 호출 연산자


ⅴ. 배열 첨자 연산자 (=인덱스 연산자)

■ 배열 첨자 연산자는 배열의 이름과 인덱스를 피연산자로 받는 이항 연산자이다.

■ 배열명이 가리키는 위치에서부터 인덱스*배열의 자료형만큼 떨어져있는 위치에 저장된 데이터를 반환한다.

■ 리스트와 같은 선형 구조를 갖는 클래스는 주로 인덱스 연산자를 재정의해서 사용하는 경우가 많다.

 

배열 첨자 연산자


ⅵ. 구조체와 포인터 관련된 연산자들

■ '&'은 주소 연산자 혹은 참조 연산자라고 부르며 변수 등의 좌측값(LValue)에 사용하면 메모리 주소를 반환한다. 

■ '*'은 포인터 변수를 선언할 때도 사용하지만 역참조 연산자로서 포인터 변수에 사용하면 메모리에 담긴 값을 반환한다.

■ '.'와 '->'은 구조체나 공용체 내부의 멤버 변수의 값을 반환하는 연산자이다.

■ '.*'과 '->*'은 구조체나 공용체 내의 멤버를 가리키는 포인터 변수가 가리키는 값을 반환하는 연산자이다.

■ '.'과 '.*'은 구조체 변수 자체에 사용하며, '->'과 '->*'은 구조체를 가리키는 포인터 변수에 사용한다.

 

포인터 및 사용자 정의 자료형 관련 연산자 표


ⅵ. 구조체와 포인터 관련된 연산자들

■ C언어에서 메모리를 동적 할당할 때는 malloc()함수를 해제할때는 free()함수를 사용했었다. 

■ 하지만 malloc()함수는 C++의 클래스에 대해서 사용하면 문제가 발생하기 때문에 C++에서는 새로운 연산자가 추가되었다.

■ C++에서도 malloc()함수와 free()함수를 사용할 수 있지만, 웬만하면 new와 delete 연산자를 활용할 것을 추천한다.

동적 할당과 관련된 연산자 표


ⅵ. 범위 확인 연산자

■ C++에 추가된 name space(이름 공간) 내에서 선언된 변수는 원래 그 범위 안에서만 사용할 수 있다.

■ 하지만 범위 확인 연산자를 이용하면 범위 밖에서도 그 범위 안의 변수를 사용할 수 있다.

 

범위 확인 연산자 표


반응형

 

ⅴ. 모던에서 추가된 연산자

 

■ 아래 내용은 이곳(두두코딩) 의 내용을 참고했습니다.

★  delete 연산자 (C++11에서 추가됨)

 

■  동적 할당과 관련된 연산자인 delete를 재정의한 것이다.

■  delete의 의미대로 함수 선언 뒤에 써서 그 함수를 지우는 역할을 한다.

■  이를 통해서 암시적 형변환 등으로 인해 생길 수 있는 에러를 컴파일 단계에서 찾을 수 있게된다.

■  예를 들어 아래와 같은 코드에서 생길 수 있는 문제를 delete 연산을 통해 해결할 수 있다.

#include <iostream>
using namespace std;

 int foo(int n) {
   return n;
 }
 // int하나를 받는 foo라는 함수를 정의했다.
 
 
 //일단 아래의 코드가 없다고 생각해보자.
 // int foo(double n) = delete;
 
 
 int main() {
 
   int r = foo(3.14) + 3;
   
   cout << r << endl;
 }
 
 // 위의 코드는 문제 없이 작동할 것이다. 
 // 하지만 foo 함수를 설계한 사람은 이게 작동되지 않기를 바랬을 수도 있다
 // 그렇다면 이 코드가 작동되는 건 문제가 있다.
 // 왜냐하면 암시적 형변환은 추후 에러를 일으킬 가능성이 높기 때문이다.

■  위의 코드에서 주석친 코드를 지우면

■  foo함수에 잘못된 인자가 들어갔을 때 컴파일 에러가 나게 할 수 있다.

 

■ 아래 내용은 이곳(두두코딩) 의 내용을 참고했습니다.

★  defualt 연산자 (C++11에서 추가됨)

 

■  클래스에 컴파일러가 자동으로 추가해주는 것과 완전히 같은 기본 생성자를 추가할 때 사용한다.

■  C++11 이전에서는 기본 생성자가 필요하면 아래와 같이 코드를 쳐서 기본 생성자를 만들어 줬었다.

■  아래 코드에서 4번째 줄의 코드는 C++11 이전에 기본 생성자가 필요할 때 쓰던 코드이다.

struct Point
{
  int x, y;

  Point() {}
  // 이건 기본 생성자와 같은 빈 생성자로 보이지만 실제로는 그렇지 않다.

  Point (int a, int b) : x(a), y(b) {}
};

int main() {
  Point a; //기본 생성자를 필요로 함
}

■  진짜 컴파일러가 제공하는 것과 같은 내용 없는 기본 생성자를 만들고 싶다면 default 연산자를 써야한다.

struct Point
{
  int x, y;

  Point() = default;
  // 이러면 컴파일러가 자동으로 생성해주는 빈 기본 생성자와 같다.

  Point (int a, int b) : x(a), y(b) {}
};

int main() {
  Point a; //기본 생성자를 필요로 함
}

■  이 default 연산자가 필요해진 이유는 C++11에서 추가된 Trivial 이라는 개념 때문이다.

■  C++11에서 Trivial은 "아무것도 하지 않음" 이라는 의미로 기본 생성자가 하는 일이 없음을 나타낸다.

■  컴파일러는 빈 블럭이라도 생성자가 있으면 Trivial 하지 않다고 판단한다.

■  Trivial함은 is_trivially_constructible <T> 클래스를 이용하여 알 수 있다.

■  Trivial한 클래스는 복사시 얕은 복사가 일어나기 때문에 문제가 생기는 경우가 있기에 이를 방지하려는 것이다.

 

 

 

반응형