본글은 최근에 Pwnable 문제중 C언어, Python뿐만 아니라
C++로 이루이진 문제를 접하는 경우가 많아져서 Python, Java와 다른 C++만의 문법이나
차이점등을 정리하기 위해 작성한 글입니다.
틀린부분이 있다면 댓글로 알려주시기 바랍니다.
(예시 코드등은 TCP 스쿨의 자료를 참고했습니다.)
https://www.tcpschool.com/cpp/intro
C++ 11/14 추가 내용
기존 C++ 98/03과는 다르게 11/14에 변경된 사항이 많은데 다음과 같다.
1. 초기화 리스트 및 초기화 방법의 통합
2. 새로운 타입의 추가 : long long형 정수
3. 새로운 스마트 포인터 추가 :
4. 널 포인터 상수 추가 : nullptr
5. 열거체의 범위 지정
6. 자동 타입 변환 : auto
7. 타입 변환 연산자 추가 : explicit
8. 범위 기반 for 문 추가
9. 람다 함수와 람다 표현식 추가
# 라이브러리 변경 사항
1. 확장할 수 있는 난수 생성기의 추가 : random 헤더 파일
2. 튜플 템플릿의 추가: tuple 헤더 파일
3. 정규 표현식의 추가 : regex 헤더 파일
4. 다중 프로그래밍을 위한 스레드의 지원 : thread_local 키워드, automic 헤더 파일
5. 임베디드 프로그래밍을 위한 저수준 프로그래밍 지원
이중에서 람다와 범위기반 for문, 열거체 범위, 리스트 초기화, tuple, regex같은 익숙한부분들은
이미 python에서 사용하는 방식과 동일하다.
(ex. python for문의 list를 반복 범위로 넣거나 배열 전체 초기화를 위해 사용하는 {0,}같은 방식)
이중 새롭게 알게되 정리할 부분은 nullptr부분이다.
기본 C에서는 포인터를 Null 하기 위해서는 아래와 같은 형식으로 코드를 작성했지만
int* ptr1 = 0;
int* ptr2 = nullptr;
C++에서 포인터를 초기화하는 방식은 11/14 버전이후부터는
nullptr이라는 값을 쓰는 것으로 널을 표현할 수 있게 됬다.
기존에는 0이라는 숫자를 넣어 포인터를 초기화하는 것이 기본이였다.
하지만 포인터의 널 체크를 시도해보면 0이라는 숫자가 들어 있어
확인이 모호할때가 많다.
하지만 이제 nullptr을 사용하게 되면 null인 포인터만을 빠르게 체크할 수 있는 장점을 가지게 된다.
이외에도 코드의 가독성을 높일 수 있는 장점이 있는데
코드를 보면
int main()
{
int *a =0; //포인터
if (a==0) // a가 int 값 0인지 포인터 타입인지 헷갈릴 가능성 있음
{
cout << "a==0" << endl;
}
if (a == nullptr) // a가 포인터 타입임을 직관적으로 알 수 있음
{
cout << "a==nullptr" << endl;
}
return 0;
}
기존에는 포인터 값을 초기화 하기 위해 0을 사용했고 이를
검증하기 위해 조건문에 넣게 되면 조건문을 봤을때 int값 0을 의미하는 것인지
포인터인지 헷갈릴 가능성이 있다.
그러나 nullptr을 사용하게 되면 a의 값을 직관적으로 포인터타입이구나 알 수 있기에
nullptr을 사용하게 되면 코드의 가독성을 높일 수 있다고 하는 것이다.
C++ namespace
C++은 기본적으로 printf 와 같은 출력을 다음과 같이 작성한다.
#include <iostream>
#define TEXT "Welcome to C++ Programming!!"
int main()
{
std::cout << TEXT;
return 0;
}
C언어에서 사용하는 printf는 C++에서는 콘솔출력을 cout가 담당한다.
그런데 옆에 붙은 std:: 라는 문자가 추가되어 있는데 이는
표준라이브러리에 있는 함수들을 호출하기 위해 추가하는 것으로
헤더파일인 iostream을 사용하기 위해서는 std::를 붙여야만 사용이 가능하다.
그러면 헤더파일에 들어있는 함수를 호출할때마다 매번 std::를 붙여야하는
번거러움이 추가되는데 이를 해소하기 위해
네임스페이스(namespace)라는 것을 사용할 수 있다.
namespace를 미리 정의해두면 std 네임스페이스에 속한 cout 이다.
라는 식으로 인식해 코드를 조금더 간결하게 작성할 수 있게 된다.
예시코드는 아래와 같다.
#include <iostream>
#define TEXT "Welcome to C++ Programming!!"
using namespace std;
int main()
{
cout << TEXT;
return 0;
}
코드를 직역해보면 std에 속한 cout객체에 TEXT에 들어있는 "welcome to C++ Programming!!"를 넘겨서
출력해라 라고 볼 수 있다.
또한 namespace의 유용한 점은 한가지가 더 있다.
namespace는 모든 식별자가 고유하도록 보장하는 코드 영역을 정의하는데
이게 무슨소리냐면 "소속"을 나눈다고 이해할 수 있다.
다른 언어에서는 함수의 이름을 동일하게 사용할 수 없는 경우가 많은데
namespace를 사용하게 되면 아래와 같이 코드를 구성할 수 있다.
using namespace std;
namespace test1
{
void print()
{
cout << "test1입니다." << endl;
}
}
namespace test2
{
void print()
{
cout << "test2입니다." << endl;
}
}
int main()
{
test1::print();
test2::print();
return 0;
}
분명히 동일한 print()라는 이름의 함수를 구성했지만
namespace로 test1, test2를 구분하는 것으로 "소속"을 다르게 만들어
같은 이름의 함수를 사용할 수 있게된다.
이러한 점은 여러명의 개발자가 개별로 코딩을 한뒤 소스코드를 합치는 과정에서
매우 유용하게 사용할 수 있다고 한다.
C++ 서식 문자 지정
C에서는 다음과 같이 서식문자형태를 표기만 해주면 자동으로 변환을 해 출력을 해준다.
printf("%#x \n",a);
하지만 C++에서는 일일히 어떤 형태인지 다 표현해야 하는데
int a = 10;
cout << "숫자 10을 10진수로 표현하면 " << a << "이며, " << endl;
cout << oct;
cout << "숫자 10을 8진수로 표현하면 " << a << "이며, " << endl;
cout << hex;
cout << "숫자 10을 16진수로 표현하면 " << a << " 입니다.";
숫자 10을 10진수로 표현하면 10이며,
숫자 10을 8진수로 표현하면 12이며,
숫자 10을 16진수로 표현하면 a 입니다.
주의점도 존재한다.
만약 출력표현을 hex나 oct등 다른 표현으로 변경한 이후
다시 int등으로 출력하고 싶은 경우인데
c언어에서는 변수에 해당하는 a가 선언된 형식을 따르지만
C++에서는 cout의 출력서식을 바꿔주는 코드를 작성하지 않으면
이전에 적용된 서식을 그대로 적용받아 출력하기에
원치 않는 출력을 하게 되는 경우가 발생한다.
그러므로 출력 형식을 바꿔야 한다면 이를 잊지말고 작성해야 한다.
취약점 분석관점에서 생각해보면 이는 다른 취약점으로도 연계될 가능성이 있기에
타겟이 될수도 있으므로 매우 중요하다고 생각한다.
Reference
1. https://www.tcpschool.com/cpp/cpp_datatype_constant
2. https://steemit.com/kr-dev/@codingman/c-3-c--1558743338400
'Reference > Language_Study' 카테고리의 다른 글
[C++] 3. C++ new()와 delete() (0) | 2024.03.22 |
---|---|
[C++] 2. C++ typeid, endl 과 \n 차이 (0) | 2024.03.19 |
명품 JAVA 에센셜 6장 실습문제 (0) | 2022.06.14 |
상속 문제(Unit) (0) | 2022.06.07 |
명품 JAVA 에센셜 5장 연습문제 (0) | 2022.06.07 |