노력과 삽질 퇴적물

혼잣말처럼 넘기는 C++ (파트1) 본문

📂기초 및 세팅 note/언어. C&C++ 계열

혼잣말처럼 넘기는 C++ (파트1)

MTG 2023. 8. 20. 16:10

 
목차
 시작하기
 기초
 > 키워드와 식별자
 > 자료형, 변수
 > 연산자
 > 자료형 캐스팅
 > 배열
 제어문
 함수
 포인터와 참조
 ???
파트3.
 ???


* 해당 포스팅은 JAVA혹은 파이썬처럼 C기반과 직결되지는 않는 언어에 익숙한 상태로 진행되는터라 과감히 생략하고 넘기는 부분이 존재합니다.
* 일반적인 강의형 포스팅이라기보다는 개인적인 노트정리여서 보조 설명이나 자체적인 언어(?)로 해석/재구성한 부분은 밑줄을 포함한 이탤릭체 혹은 기울임꼴로 표기됩니다.

* 참조 자료상 auto, long long int등이 있는터라 최소사양을 C++11(2011년 승인)에 두고 있습니다.





1. 시작하기
 
0) 준비
-> 어지간한 '웹기반 컴파일러'로 문법 연습은 가능하지만,
C++의 경우 헤더파일을 따로 두는 기본 형태상 헤더/소스파일을 위한 멀티탭까지 지원해주는 웹 컴파일러가 좀더 유용하다고 봅니다.
Online C Compiler - online editor  //소스파일 멀티탭가능.
Compiler Explorer  //빌드 결과를 어셈블리로도 볼 수 있음.
 

1) C하고의 차이?
-> 클래스를 통한 OOP(객체 지향 프로그래밍)이 가능하며, 템플릿처럼 실무에서도 유용한 범용적인 구조를 지원.
-> "C++ 컴파일러는 점차 <iostream.h>을 지원하지 않는 추세로 접어들었다." (p.16, 윤성우의 열혈 C++ 프로그래밍, 2010)


2) 기본 구성
선행처리기  예.
// import com.xxx1.xxx2;같은거?
#include <iostream> //'표준 라이브러리'는 확장자 없이 꺽쇠.
#include "Scenario"  //'사용자 정의 헤더'는 확장자 없이 쌍따옴표
#define SELF_RULE_MIN=2023;  //'매크로 선언'이라 불림.
#if, #ifdef, #ifndef
/* 조건부 컴파일'은 JAVA에 없던 개념이라 애먹음. 조건문과 static변수를 이용한 비스무리는 있지만,[#링크] 조건부 구간내 코드가 컴파일시 아예 안 들어가는게 아니라 (진입만 막힐뿐)코드상으로는 존재하니 안드로이드 프로그래밍에서 productFlavors와 빌드변수쪽이 더 유사한거 아닌가? */
main 함수  프로그램이 시작되고 종료되는 함수.
입출력  std::cout << "출력 마무리시, 끝에 라인 종료 필수." << std::endl;
 std::cin >> userInputValue;
네임스페이스
(namespace)
namespace Scenario
{
   void DoRefresh(){  ... ... ...   }
   ... ... ...
}
namespace Character
{
   void DoRefresh(){  ... ... ...   }
   ... ... ...
}
int gENV = 2023;


int main()
{
   Scenario::DoRefresh();
   Character::DoRefresh();
/*C#에도 본 개념이긴 한데, JAVA에서 같은 함수명&다른 라이브러리 패키지들을 같은 구간내에서
com.apache.sameFuncName();
com.java.sql.sameFuncName(); 이런식으로 쓰는거?*/


   std::cout << ::gENV << std::endl;
//전역변수는 최상위 경로여서 앞부분이 빈 모양새?

   using namespace std;
   cout << ::gENV << endl;  //import com.xxxx.*; ?

   using NameSpaceInOtherPath::DoRefresh();
   DoRefresh();  //import com.xxxx.DoRefresh();로 일부만 쓴다?
}
-> "명칭공간(namespace)이란 특정한 이름들이 인식되는 프로그램의 부분을 의미한다." (p.13, C++프로그래밍, 2019)
 




2. 기초

1) 키워드와 식별자
-> 다른 프로그래밍 언어에도 호환되는 개념이나, (사용자가 정의한)네이밍에 대한 규칙이 C++11이후인지 혹은 MS의 C++인지에 따라 약간씩 추가 규칙이 있다 정도?


2) 자료형
 정수형  고정소수점.
 signed/unsigned + char/short int/int/long/long int/long long int
 bool [1byte]
// char이 정수? 그런데 생각해보니 내부에 처리되는건 ASCII 코드값이고, 그건 숫자잖아? bool은 1(참)아니면 0(거짓)
 실수형  부동소수점.
 float [4 byte]
 doubel [8 byte]  //tcpschool.com에서는 long double도 취급.
 
 
3) 변수
-> 최신 컴파일러에서나 어느 자료형이건 기본값으로 흔히들 말하는 시스템 디폴트값으로 나오는거지, 그런 보조 장치가 없으면 알수 없는 쓰레기값이 들어가서 좀 옛날 환경(윈7 현역일때도 들었던 고릿적 환경이지만)에서 작성된 코드에는 0이건 뭐건 값을 초기화 시킨다가 기초 이론으로 알고 있었는데 어디서 보니깐...
'컴퓨터 시스템에서는 어느 언어건 변수를 만들면 디폴트값이 다 똑같이 시작합니다'라는 중간 과정이랄지 전제조건이 많이 생략된 말을 하는 경우가 보이더군요.
//프로그래밍 언어론이나 컴파일러 이론 최신판 다시 보고나서 정리해본다를 기약하며
int yearI = 2023;
unsigned int yearUI = 2023u;
long yearL = 2023L;
unsigned long yearUL = 2023ul;
long long yearLL = 2023LL;
/* unsigned나 long long처럼 변수 용량 늘리는건 C에서 처음 봤던거라 놀랍지는 않음. 그거 빼고는 거진 다른 언어에도 쓰는 형태. */
int month(8);
int day{19};
/* 예? 뭐라고요?
int month = new Integer(8); 축약판?
중괄호는 int[] day = {19};로 본다면 뭐... */
auto second1{10}; //int second1=10; /* '자료형 추론'이라는 정식명칭을 가지고 있는데 템플릿이랑 조합해서 쓰기 좋다는 설명...
var로 선언된 변수처럼 알아서 잘 쓰라고 만든건가?
포인터와 참조자하고 조합불가라고 함.*/
const //자바의 final
constexpr //컴파일시 해당 변수에 값이 있어야 에러가 없다고 함.


4) 연산자
 2항 산술  +  -  *  /  %
//다른 언어에도 있는 (사칙연산+나머지 연산 하나).
 증감 예시.
int a=10;일때
//  b=++a;// a==11, b==11
//  b=a++;// a==11, b==10
//  b=--a;  // a==9, b==9
//  b=a--;  // a==9, b==10
/* C계열에서는 전/후위 증감연산자 동작에 주의를;;; 증감 연산이 변수 앞에 있어야 즉시 반영?
정보처리기사에도 나오는 단골 문제라면서요? */
 논리  ||(논리합)  &&(논리곱)  !(not)
 관계  >  >=  <  <=  ==  !=
/* 다른 언어에도 있. 그러고 보니 자바 스크립트에서는 ==, ===가 별개였;;; */
 비트단위  |(논리합)  &(논리곱)
 ^(배타적 논리합.XOR)  //짝짝이만 1(참)인거
 ~(1의 보수. NOT)
 비트 이동  <<(left 이동)  >>(right 이동)
/* 쉬프트 연산으로 알던거.
좌-쉬프트는 왼쪽으로 N칸씩 옮기면서 일부값 유실과 달리
우-쉬프트는 오른쪽으로 N칸씩 옮기면서 맨앞으로 비트값으로 비는 공간을 자동채움하는 모양새가 그... 그... 텍스쳐 처리에서 clamp모드 연상됨. */
 대입&축약  =  +=  -=  *=  /=  %=  <<=  >>=
 &=  |=  ^=
 그외.  &(주소값)  *(포인터)
 new  delete
 삼항 연산자


5) 자료형 캐스팅
/* int변수[4byte]를 double변수[8 byte]에 대입시킬때 융통성있게(?) 처리해주는건 묵시적. 역방향이면 컴파일러가 경고를 띄우니 변수앞에 강제로 (자료형)을 붙여서 코드상 무슨 자료형인지 수동이 들어가는건 명시적. */
 묵시적 캐스팅 //대입 처리시 값이 저장되는 변수의 형으로 자동 처리 
 명시적 캐스팅 C++에 있는 명시적 캐스팅 연산자는 4종.

dynamic_cast     프로그램 실행중 처리
static_cast         컴파일시 처리
-> "dynamic_cast 연산자보다 static_cast 연산자를 사용했을 때 연산의 속도가 더 빠른다. 따라서 이러한 이유로 dynamic_cast 연산자를 사용해도 되는 상황에서 조차 static_cast 연산자를 사용하는 경우도 적지 않다." (p.638, 윤성우의 열혈 C++ 프로그래밍, 2010)
reinterpret_cast  bit수준에서 처리
const_cast         const속성을 (일시적으로)삭제
-> "const선언으로 인한 형(type)의 불일치가 발생해서 인자의 전달이 불가능한 경우에 유용하게 사용이 된다. ...(중략)... 연산자의 긍정적인 측면이 잘 드러나는 경우에만 제한적으로 사용해야 한다."  (p.641, 윤성우의 열혈 C++ 프로그래밍, 2010)


6) 배열
-> 배열의 인덱스는 0-base.
int myArr[3] = {0, 100, 101};로 선언과 동시에 초기화 하는것이
int myArr[3] {0, 100, 101};도 가능.



 
 
3. 제어문

1) if-else 조건문
if(조건식1)
{
   ... ... ...
}
else
{
   ... ... ...
}
if(조건식1)
{
   ... ... ...
}
else if(조건식2)
{//조건문 처음에 많이들 헷갈리던 그 구간.
   ... ... ...
}
else
{
   ... ... ...
}
 
 
2) switch 조건문
/* switch(수식)에서 수식으로 문자열은 되고 상수등은 왜 안 되는건가 잠시 헷갈렸는데, 다시보니 정수형만 허용이였다.
그리고 case문을 2개 이상 쓰는것이 가능하다를 알기 전과 후로 내 조건문 스타일에 선택지가 훅훅 늘었다. 사실 컬쳐쇼크였지만. */
 
 
3) for 반복문
int[] arr = {1, 10, 11, 100, 101, 111};

for(int loopIdx=0; loopIdx<arr.length; loopIdx++)
{
   ... ... ...
}
int[] arr = {1, 10, 11, 100, 101, 111};

for(int loopParam : arr)
{
   ... ... ...
}
/* 콜렉션 기반이 중요한 그 for문.
향상된 for문 혹은 개선된 for문 등등...
표준 명칭 좀 알고 싶다. */
 
 
4) while/do-while 반복문
/* (JAVA기준)동작은 알지만, 실무에서는 그닥 안 써서 존재만 인식하고 있는 기초 문법중 하나. */
 
 
5) break/continue문
/* for문하고 세트 메뉴라 보는 편. */
 
 
 
 
 
4. 함수와 인수 그리고 매개변수
 
/*  C계열에서는 일정한 동작&기능을 하는 프로그램 코드를 모아둔걸 함수(function)라 하고 JAVA에서는 매소드(method)가 정식 명칭이지만... 그냥 혼용해서 부르고 있다보니 이거나 저거나.?
 JAVA 환경에서는 샘플코드나 생산성 기능으로 자동 생성된 함수에 arg1, arg2로 입력이 되고 "위 함수는 두 개의 정수를 인자로 요구하고 있다. 따라서 Call-by-value 기반의 함수이다." (p.74, 윤성우의 열혈 C++ 프로그래밍, 2010)나 기존에 사용하던 표기도 (함수)인자이긴 한데, 방송대 교재와 MS 문서 기계번역은 '함수 인수'라 하니 인수쪽을 기본 표기로[#링크] */
 

1) argument(아규먼트, 함수 인수)? parameter(파라미터, 매개변수)
/* 함수 입력 양식을 정의해두는 곳에서 '형식 매개변수'고(formal parameter), 함수를 호출하는데서 입력하는 값은 인수 혹은 '실 매개변수'(actual parameter)?*/
 
"Parameters refers to the list of variables in a method declaration. Arguments are the actual values that are passed in when the method is invoked. When you invoke a method, the arguments used must match the declaration's parameters in type and order." (오라클, Passing Information to a Method or a Constructor [#링크])
 
 
2) 디폴트 인수
/* JAVA 기본 문법에는 없음, Build Pattern으로 비스무리 가능하다곤 해도. 다른 함수 오버로딩으로 커버된다고 판단되서 이리 설계된건지? */
*.cpp *.java
void cppFunc(int param1, int param2=1970)
{   ... ... ...   }

funcInCpp(23, 2000);
funcInCpp(53);

/* 끝단 입력은 생략했지만 둘다 같은 함수를 호출. */
void javaFunc(int param1)
{   javaFunc(param1, 1970);   }
void javaFunc(int param1, int param2)
{   ... ... ...   }

javaFunc(23, 2000);
javaFunc(53);
/* 내부 최종점은 같아도 호출시 다른 함수. */
 
 
3) 가변 인수
-> 해당 인수 형태는 참조 서적 2종의 목차에도 없는지라 MS의 자료 기반으로 구성됩니다.
/* 그런데 가변 인수에서는 Functions with Variable Argument고, 다시 봐도 함수 선언구간에서는 아규먼트? 뭐지? [#링크] */
*.cpp [#웹 컴파일] *.java [#웹 컴파일]
#include <iostream>
#include <stdarg.h>//중요!
 
using namespace std;
 
void cppFunc(int param1, int param2, ...)
{    return;    }
 
int main()
{
    int myArr[] {2022, 2023};
    cppFunc(2020, 2021);
    cppFunc(2020, 2021, 2022);
    cppFunc(2020, 2021, 2022, 2023);
    cppFunc(2020, 2021, myArr);
    //cppFunc(2020, myArr);//ERROR. invalid conversion from ‘int*’ to ‘int’
    cout << "call test FIN.";
 
    return 0;
}
public class VariableArguments
{
    private static void javaFunc(int param1, int param2, int ...param3)
    {    return;    }
    
    public static void main(String[] args)
    {
        int myArr[] = {2022, 2023};
        javaFunc(2020, 2021);
        javaFunc(2020, 2021, 2022);
        javaFunc(2020, 2021, 2022, 2023);
        javaFunc(2020, 2021, myArr);
        //javaFunc(2020, myArr);//ERROR.
        
        System.out.println("call test FIN.");
    }
}
 
 
4) inline 함수
inline 반환타입 함수명(형식 매개변수)

/* 함수는 모듈화를 통한 코드 재사용 및 가독성등에는 좋지만, 호출에서 지체되는 처리시간이 존재. 만약 호출빈도가 잦으면서 빠른 실행속도가 필요한 성능이슈가 필요하면?
해당 구간에 함수 내용을 복사&붙여넣기한 하드코딩이라도 해야겠지만, 인라인 함수로 선언하면 코드는 함수인데, 컴파일상 하드코딩처럼 처리한다는건가? */
-> 컴파일시 inline선언이 무시되는 경우.
① 함수 크기가 너무 클때
② 순환 호출
③ 해당 함수에 대한 포인터 사용시
 
/*별도의 게임엔진 없이 순수 안드로이드 프로그래밍으로 미니게임을 구현할때도 UI스레드에 있던 프로토 타입 코드를 함수로 정리하면서 UI상의 처리속도가 빨라졌던 경험을 참조하면, 함수의 크기에 따른 연산처리는 무시무시하다.*/
 
 
 
 

5. 포인터와 참조
 
1) 포인터와 메모리
//이 녀석이 싫어서 JAVA와 C#을 오갔건만, 결국 다시 마주했다. 에라이.
-> 예시 [#웹 컴파일러]
#include <iostream> 

using namespace std;

int main()
{
    int tmpVal1 = 2023;
    int tmpVal2 = 8;
    int *pointerI = &tmpVal1;//주소값 대입
    int *const constPointer = &tmpVal2;//read-only로 만들어버림.
    
    cout << "tmpVal1=" << tmpVal1 << ", &tmpVal1=" << &tmpVal1 << endl;
    cout << "tmpVal2=" << tmpVal2 << ", &tmpVal2=" << &tmpVal2 << endl;
    cout << "pointerI=" << pointerI << ", *pointerI=" << *pointerI << endl;
    cout << "constPointer=" << constPointer << ", *constPointer=" << *constPointer << endl;
    cout << "-----------------" << endl;
    
    pointerI = &tmpVal2;
    //constPointer = &tmpVal1;//ERROR
    cout << "(after pointerI = &tmpVal2)" << endl;
    cout << "pointerI=" << pointerI << ", *pointerI=" << *pointerI << endl;
    cout << "constPointer=" << constPointer << ", *constPointer=" << *constPointer << endl;
    cout << "-----------------" << endl;
    
    int *dynamicPointer = new int;
    cout << "(*dynamicPointer = new int)" << endl;
    cout << "dynamicPointer=" << dynamicPointer << ", *dynamicPointer=" << *dynamicPointer << endl;
    delete dynamicPointer;//메모리 반납
    cout << "(after delete dynamicPointer) ---> " << ((dynamicPointer==nullptr)?"NULL":"exists") << endl;
    cout << "dynamicPointer=" << dynamicPointer << ", *dynamicPointer=" << *dynamicPointer << endl;
    dynamicPointer = nullptr;//이거까지 해야 진짜로 없는 존재가 된다.
    cout << "(after dynamicPointer = nullptr) ---> " << ((dynamicPointer==nullptr)?"NULL":"exists") << endl;
    //cout << "dynamicPointer=" << dynamicPointer << ", *dynamicPointer=" << *dynamicPointer << endl;//none print

    return 0;
}
 
 
2) 참조
-> l-value참조: 대입 연산자 좌측처럼 값이 저장되는 대상을 참조.
-> r-value참조: 대입 연산자 우측처럼 값이 쓰이고 나면 더 이상 가지고 있을 필요가 없는걸 참조.
-> 포인터하고 비슷해도 차이가 있다고 한다.
 "첫째, 참조를 이용하여 값을 읽거나 저장할 때는 변수를 사용하는 형식과 동일하다. [소스코드 2-14]의 3~5행처럼 참조의 이름을 그대로 사용하며, 포인터처럼 '*'와 같은 연산자를 사용하지 않는다.
 둘째, 참조는 초기화를 통해 반드시 어떤 대상을 참조해야만 하므로 아무것도 참조하지 않는 상황은 발생하지 않는다. 포인터는 초기화를 하지 않거나 안 되는 곳을 가리키거나 아무것도 가리키지 않는 상황이 발생할 수 있다.
 셋째, 참조는 초기화를 통해 지정된 참조 대상을 바꿀수 없어 참조의 유효기간 동안 하나의 대상만 참조할 수 있다."   (p.73, C++프로그래밍, 2019)
-> 예시 [#웹 컴파일러]
#include <iostream> 

using namespace std;

int main()
{
    int tmpVal1 = 2023;
    int tmpVal2 = 8;
    
    int &leftRef = tmpVal1;
    cout << "(after) int &leftRef = tmpVal1;" << endl;
    cout << "&leftRef=" << &leftRef << ", leftRef=" << leftRef << endl;
    cout << "tmpVal1=" << tmpVal1 << ", tmpVal2=" << tmpVal2 << endl;
    cout << "--------------" << endl;
    
    leftRef = 1970;
    cout << "(after) leftRef = 1970" << endl;
    cout << "&leftRef=" << &leftRef << ", leftRef=" << leftRef << endl;
    cout << "tmpVal1=" << tmpVal1 << ", tmpVal2=" << tmpVal2 << endl;
    cout << "--------------" << endl;
    
    leftRef = tmpVal2;//tmpVal2의 값만 가져오게 됨.
    tmpVal2 = 9;
    cout << "(after) leftRef = tmpVal2;" << endl;
    cout << "&leftRef=" << &leftRef << ", leftRef=" << leftRef << endl;
    cout << "tmpVal1=" << tmpVal1 << ", tmpVal2=" << tmpVal2 << endl;
    cout << "--------------" << endl;

    return 0;
}

 
3) Call by value(값에 의한 호출)과 Call by reference(참조에 의한 호출)
/* C#으로 치면 함수 선언이랑 호출시 ref/out 붙이는 그거잖아? */
-> 예시 [#웹 컴파일러]
#include <iostream>

using namespace std;

void doExchangeByValue(int param1, int param2)
{//CASE. call-by-value
    int tmp = param2;
    param2 = param1;
    param1 = tmp;
    
    cout << "doExchangeByValue, param1=" << param1 << ", param2=" << param2 << endl;
}

void doExchangeByRef(int &param1, int &param2)
{//CASE. call-by-reference
    int tmp = param2;
    param2 = param1;
    param1 = tmp;
    
    cout << "doExchangeByRef, param1=" << param1 << ", param2=" << param2 << endl;
}

void doExchangeOther(int *param1, int *param2)
{//CASE. doExchangeByRef 동작을 포인터 기반 처리
    int tmp = *param2;
    *param2 = *param1;
    *param1 = tmp;
    
    cout << "doExchangeOther, param1=" << *param1 << ", param2=" << *param2 << endl;
}

int main()
{
    int arg1 = 2023;
    int arg2 = 8;
    
    cout << "(defore)doExchange arg1=" << arg1 << ", arg2=" << arg2 << endl;
    doExchangeByValue(arg1, arg2);
    cout << "(after)doExchange arg1=" << arg1 << ", arg2=" << arg2 << endl;
    cout << "------------------------------" << endl;
    
    cout << "(defore)doExchangeByRef arg1=" << arg1 << ", arg2=" << arg2 << endl;
    doExchangeByRef(arg1, arg2);//
    cout << "(after)doExchangeByRef arg1=" << arg1 << ", arg2=" << arg2 << endl;
    cout << "------------------------------" << endl;
    
    cout << "(defore)doExchangeOther arg1=" << arg1 << ", arg2=" << arg2 << endl;
    doExchangeOther(&arg1, &arg2);//
    cout << "(after)doExchangeOther arg1=" << arg1 << ", arg2=" << arg2 << endl;

    return 0;
}
 
 
 
 
 
기타. 참조자료

1) 서적
윤성우 저. 윤성우의 열혈 C++ 프로그래밍, 오렌지미디어, 2010년.
전중남, 이병래 저. C++프로그래밍, 한국방송통신대학교출판문화원, 2019년.


2) 웹페이지
코딩의 시작, TCP School
Learn / Microsoft C++, C 및 어셈블러
C++ 언어 / C++ 언어 참조 (접속: 2023-08-20)
C++ 언어 / C++ 언어 참조 / 함수 / 가변 인수 목록을 사용하는 함수 (C++) (접속: 2023-08-20)





기타. 변경이력

일자
 변경이력
 2023-08-20  초안 작성.