• inline함수

inline함수는 프로그램의 실행 속도를 높이기 위해 c++에서 새로 보강된것

 

함수가 하나 생성되면 함수의 시작 주소가 있을것이며

함수가 호출 될때 그 시작주소로 가서 실행 후 다시 원래 있던 주소로 돌아갈것이다

 

이를 더 자세히 하면

 

1. 함수 호출 명령

2. 함수 호출 명령 다음의 코드 주소를 메모리에 저장 [함수 종료 후 다시 돌아와야하니까]

3. 호출하려는 함수의 매개변수 복사 [파라미터에 넣어야하니까]

4. 호출하려는 함수의 주소로 점프

5. 함수 코드 수행

6. 함수 종료시 함수의 리턴값을 레지스터에 복사

7. 저장해 두었던 코드 주소로 점프

 

이러한 과정들이 있는데

inline은 이러한 문제를 어느정도 해결해준다

 

바로 함수를 호출할때 원본함수코드를 그냥 그자리에 붙여넣어서 

이동할 필요 없이 그 자리에서 읽고 처리한다

define과 비슷한 부분이다

 

이렇게 작성한 코드를 inline화 시키면

 

이런식으로 원본 코드가 온다는 말이다

 

inline의 사용법은 inline 이라는 키워드를 함수의 선언이나 정의 앞에 붙여주면 된다

 

inline이 속도는 굉장히 빨라지겠지만 만약 함수의 크기가 크다면

붙여지는 코드의 양도 그만큼 커져 프로그램이 무거워질 것이다 [ ※재귀함수는 불가능 ]

그렇기 때문에 그 기준을 잘 잡아야 하는데

 

이 기준은 다행히도 컴파일러가 알아서 해준다

얼마나 알아서 잘 해주냐면

 

우리가 inline키워드를 사용해도 컴파일러 판단에 부합하지 않으면 inline화 해주지 않는다

우리가 inline키워드를 사용하지 않아도 컴파일러 판단에 inline화 해준다

 

그러므로 우리가 inline키워드를 사용한다는것은 약간 요청보단 이건 어때? 라는 권유의 느낌 같다.

 

그리구 추가로 inline함수의 함수포인터를 사용해서 함수가 호출되었을때는 일반함수로 호출이 된다

 

이쯤 보면 매크로와 상당히 비슷한데

차이랄게 있다면

매크로는 단순히 코드를 변환시키는거지 값을 전달하지않는다

매크로 함수같은경우 값이 전달되는게아니라 문자 치환이여서

++a * ++a 가 되므로 "++" 연산자가 두번 실행된 후 곱셈이 진행되어 3 * 3으로 9가 나온다.

 


  • 참조변수

c++에 새로운 복합자료형이다

 

reference라고도 하며 미리 정의된 어떤 변수의 식별자 대신 쓸 수 있다

 

이 대신 사용한다는 것은 주소를 참조하여 사용한다는 의미이다 => 복사비용문제 해결

 

생성은 &를 붙여 생성한다

오른쪽 처럼 선언과 동시에 정의를 해주지않으면 생성 할 수 없으며

나중에 참조하는 변수를 바꿀수 없다

 

이 참조변수의 주된 용도는 매개변수에 사용하는데

 

기존 함수가 호출될때 매개변수에 들어온 값을 복사하여 사용하는데

이 참조형을 매개변수로 사용할시에 함수는 같은 주소를 가지고 있는 매개변수를 받기때문에 원본 데이터를 다룰수있다


만약 참조형에 다른 자료형이나 상수가 들어가면 에러가 난다

 

하지만 const를 사용하면 허용이 된다

이는 const일 경우 컴파일러는 임시변수를 생성하기 때문이다

long a를 int형으로 변환시켜 만든 임시변수 를 ra1가 참조하고

ra2 에 들어가는 상수 32를 가지고 있는 int형 임시변수 를 ra2가 참조하게 만든것이다

 

참조형은 참조 대상과 주소 값이 같다는 특성 때문에 참조 대상의 '별칭(alias)'이라고도 불린다. 

 

나무라서 좀 그렇지만 배울게 많다..

https://namu.wiki/w/%EC%B0%B8%EC%A1%B0%EC%97%90%20%EC%9D%98%ED%95%9C%20%ED%98%B8%EC%B6%9C

 

  • 함수포인터

함수도 주소를 가지고 있다 그 주소는 함수의 기계어 코드 시작주소를 가르킨다

괄호없이 함수의 이름만 작성하면 주소가 된다

이 함수 주소를 담는 포인터형도 있는데

반환형, 매개변수타입을 함수원형 처럼 써주면 되며, auto를 이용해 편하게 사용할 수 있다

책에서 auto는 배열처럼 리스트 형식을 초기화할 때는 사용할 수 없다고 하는데

기본 우리가 알던 배열 형식은 아니지만 initializer list로 적용이 된다

initializer list로 실행되는 모습

 

함수포인터의 자료형을 다루는걸 알았으니 함수의 매개변수에도 넣을 수 있다

 

함수포인터의 호출 방법은 두가지가 있다

 

1. 포인터이기에 *를 사용하여 함수에 접근해야한다

2. 함수이름이 그 함수를 지시하는 포인터이므로 그 함수를 가르키는 포인터도 함수이름처럼 사용해야한다

위 두가지가 모두 논리적으로 맞기에 둘다 허용된다

 

 

  • 함수와 2차원 배열

함수에 2차원 배열을 매개변수로 넣는것에 대해 설명하는 부분

 

함수의 원형 부분에 2차원 배열의 자료형을 기술할때 행의 개수는 필수로 입력하는게 핵심이다

7

https://ddidding.tistory.com/31

더블포인터에 이차원 배열의 이름이 대입되지 않는다 항목과 비슷한 개념이다

 


  • 함수와 문자열

문자열을 매개변수로 넣으려면 문자열의 첫번째 주소를 넣는다

C 스타일 문자열 예시

 

 


  • 함수와 구조체

구조체는 작성에 따라 메모리가 작을수도 있고 클수도있다

Test를 호출했을 경우 a가 복사되어 st에 대입된다

지금은 a의 크기가 작기에 이렇게 pass by value를 사용해도 괜찮지만

만약 a 크기가 크다면 이 복사과정이 부담이 될 수 있다

 

그래서 주소를 전달해 줄 수 있다

주소를 넘기는 방식중 passing by reference를 C++에서 제시한다고 한다

이건 뒤에 나오니까 뒤에서 정리한다

 

 


  • 함수와 std::array

stl array를 매개변수로 사용하고 Range based for loop를 사용하여 출력

 

 


  • 재귀호출

함수가 자기 자신을 호출할 수 있다 이걸 재귀호출이라 한다

 

간단한 로직은 if문을 통해 끝내는지, 자기자신을 호출하는지 분기를 나눈다

간단한 계승기능

위처럼 재귀함수는 for문으로도 서로 변환이 가능하다

 

재귀함수는 간결한 코드로 표현이 가능하나 스택에 계속 데이터가 쌓여 반복이 커진다면 stack over flow가 발생한다

 

알맞은 때에 사용해야 한다. 이 알맞은 때는 경험으로 이루어지면 될듯싶다

  • 함수

함수가 return을 넘기는 과정

리턴 값을 CPU의 메모리or레지스터에 값을 복사해 저장한다

그 함수를 호출한 프로그램에서 리턴값이 들어있는 주소를 조사한다

※ 배열은 return이 안된다

 

함수의 원형

- 컴파일러에게 내가 저장한 return의 자료형이 이거야 라고 알려줌

- 매개변수의 타입과 개수를 컴파일러에게 알려줌

- 식별자 생략 가능(하지만 쓰자)

 

함수의 호출시 return처리

- 리턴값을 계산하여 리턴값을 CPU에 넣는다

- 넣을 때 함수머리부분의 정의된 자료형으로 넣는다

 


  • 매개변수

함수에 전달되는 값을 실제 매개변수(argument) 라한다

함수에 전달되어 복사받은 값을 저장한 변수를 형식 매개변수(parameter) 라한다

형식 매개변수는 지역변수로 생성된다

argument의 값을 복사해 parameter로 넣는다

 

그렇기에 둘다 값만 같고 다른 변수다

그 이유로 넣은 a와 b의 주소가 다르고 a의 값이 그대로이다

 

그래서 주소를 사용한다

함수의 정의에 있는 int b [ ]는 호출 될 때 새로 생성되었으며 a와 가르키는 배열의 데이터는 같다

 

근데 Test함수의 정의에 매개변수를 int b[ ]로 배열을 받게끔 사용했다

생성되는것도 배열이 생성될까 궁금했다

 

그래서 사이즈를 받아봤다

 생성되는 타입은 포인터인듯하다

 

이렇게 포인터로 넘어온다.

포인터는 크기에 제한이 없기에 조심하자

 

  • cctype.h

문자관련함수들

 


  • 삼항연산자

구성이다

expression1 ? expression2 : expression3

 

 

expression1이 true면 삼항연산자의 값은 expression2이며, 그 반대인 false면 값은 expression3이된다

 


  • switchcase

어떠한 정수자료형에 따른 분기를 나눌 때 사용하면 편하다

 

구성이다

switch ( integer-expression )

{

     case label1 : statement

     case label2 : statement

     ......

     default      : statement

}

 

integer-expression에 정수형 데이터가 들어가면 이 데이터와 같은 label의 행이 실행된다

 

주의 할 점은 시작된 구문 부터 계속 실행하기 때문에 break를 써주어 탈출해야한다

break를 안 했을 경우

integer-expression은 정수형은 모두 가능하기에 char, enum도 가능하다

 


  • break & continue

break는 나온순간 속해있는 Loop을 탈출한다

2번 반복문안에 break가 실행될때 속해있는 1번,2번 반복문을 탈출하는게 아니라 2번 반복문만 탈출한다

 

 

continue는 continue가 나온순간 뒤의 코드는 무시하고 반복문의 맨 처음으로 돌아가 조건을 확인한다

마찬가지로 속한 Loop을 기준으로 한다

 


  • 간단한 파일I/O

쓰기와 출력의 로직은 비슷하다

open( )으로 파일과 연결 ,연결한 파일에 관한 작업을 하고 , close( )로 파일연결 종료

 

먼저 파일에 쓰기다

<fstream> 에 정의 되어있는 ofstream 클래스는 Output File Stream으로

파일에 데이터를 작성가능한 기능을 가지고 있다

 

파일 읽기

<fstream> 에 정의 되어있는 ifstream 클래스는 Input File Stream으로

파일에 데이터를 프로그램으로 입력해주는 기능을 가진다

파일을 읽고 쓰는 법은 다양하다

 

나중에 더 자세히 나오니 이부분은 일단 어떻게 돌아가는지 로직만 이해하면 될거같다

 

*스트림 나중에 공부

  • End of File

파일의 끝에 도달했을 때 인식하고 프로그램에게 전달할 수 있다

다른 정규문자와 중복되지 않게 #define EOF -1 으로 지정되어있다

stdio.h

 

일단 redirection이라는 개념이 나왔다 File과 관련된 기능같은데

운영체제에서 제공하는 기능이며 입력이나 출력 등을 파일로 한다는 기능 같다

ex) 항상 키보드로 입력을 받았지만 파일의 데이터를 받아 입력으로 처리한다

 

window는 Ctrl + Z 를 누르는 것으로 EOF를 생성한다

^Z = Ctrl + Z

입력한 문자 개수 만큼 1이 출력된다(빈칸,엔터 포함)

두번째 입력했을때(ab^Zcdefg) Ctrl + Z 부분까지 인식하고 1이 3번 나왔다

 

그 뒤의 cdefg도 버퍼에 남았을까 싶어 다시 입력했지만 새로 입력한 문자개수만큼만 나왔다

이유는 EOF를 인식할 경우 그 뒤로는 입력을 받아들이지 않기 때문이다

 

근데 이상한점이 있다 분명 while문에 EOF를 만나면 종료하라고 되어있는데 한번 더 문자열을 입력 받게되는 상황이다

 

이는 밑의 블로그에 자세히 설명되어있다

간단히 하면 다른 문자가 먼저 입력된 상태에서 ctrl + z 를 입력하면 26의 값으로 처리된다고 한다

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=tipsware&logNo=221315155895 

 

[Q&A] EOF를 어떻게 입력하나요?

: C 언어 관련 전체 목차 http://blog.naver.com/tipsware/221010831969 1. 질문 EOF는 'End of file'...

blog.naver.com

ios_base 클래스에서 정의되어 있고
이를 상속받은 iostream, fstream, stringstream 모두 동일하게 작동합니다.

 

cin은 EOF를 인식했을 때 ios_base 클래스에 정의되어 있는 eofbit, failbit 이라는 비트를 1로 설정한다고 한다

이는 ios_base를 상속받은 iostream, fstream, stringstream 모두 사용한다

 

그래서 EOF를 만났는지 안만났는지 알수있는법은 저 비트를 확인하면 되는데

 

eofbit는 cin.eof( ) 를, failbit는 cin.fail( ) 을 사용하면 알수있다

 


  • 2차원배열

말그대로 2차원처럼 보이는 배열이다

초기화

 

사실은 1차원으로 쭉 나열되어있긴하다

 

일차원배열과 비슷하게 [0]과 [0][0]의 주소는 같다 

 


  • if

조건에 따라 실행하는 구문이 생길때 사용한다

 

구성은

if ( test-condition )

   statement

 

test-condition이 참이면 statement를 수행한다

거짓이면 statement를 건너뛴다

 

거짓인 경우 다시 다른 조건을 확인하는 else if

 

거짓인 경우 무조건 수행하는 else

 


  • 논리표현식

한가지 이상의 조건이 논리연산자를 통해 맞는지(True) 아닌지(False) 값을 나타내는 표현식

 

 

1. || (논리합, OR) - 두개의 조건을 검사하며 둘중 하나라도 True면 True리턴 , 

                         그리고 시퀀스 포인트이기에 좌변의 부수효과가 있다면 || 에서 무조건 실행되고 우변이 평가된다

 

b는 0으로 초기화 되어 있다

 

첫조건 b++ > 0 에서  ++는 접미에 있기에 > 보다 연산자 순위가 낮기에 >먼저 실행된다

 

그럼 0 > 0 이므로 false

 

그후에 || 가 시퀀스 포인트이므로 증가연산자가 실행된다(이때  b = 1)

 

그리고 다음 조건인 b > 0 을 실행한다

 

두번째 조건에서는 b가 1이기에 1 > 0 이므로 최종으로 true이다

 

 

 

2. && (논리곱, AND) - 두가지 조건을 검사하고 모두 True여야 연산의 결과가 True이다

 

 

 

3. ! (논리부정, NOT) - 하나의 조건의 값을 뒤집는다 ( True -> False, False -> True )

                             &&, || 보다 우선순위가 높은 편이다

 

논리부정은 0(Null)인지 아닌지 체크하는데 편리하다

 

 

  • 복합구문&콤마연산자

여러구문을 하나의 구문처럼 간주하는 것

 

중괄호를 사용해 블럭을 생성하는 방법

 

콤마연산자를 이용한 방법이 있다

 

복합구문의 예로 for의 Loop Body는 하나의 구문이어야 하는데

복합구문을 이용해 여러 구문을 실행하게 하는것이였다

콤마연산자도 마찬가지로 복합구문을 만들 수 있다

다만 밑에 처럼 선언과 같이 사용하면 변수 이름을 분리시킨다 하여 분리자 라고 한다

위에 출력값이 20 과 40이다

콤마 연산자는 왼쪽의 구문을 먼저 실행하고 오른쪽의 구문을 실행하는 시퀀스 포인트라고 한다

그래서 a = 20이 들어가고 b = 20 * 2이 되어 40이 출력이 되었다

 


  • 관계표현식

각 연산의 결과가 bool형으로 나온다

 


  • tpyedef ( define & using )

typedef는 기존 데이터형의 별명을 만드는 방법중 하나다( 그외 define, using)

 

구성이다

typedef typeName 식별자

사용하는 방식은 내가 경험하면서 알야겠지만

일단 복잡한 자료형을 간단히 나타내는데 괜찮을거 같고 자료형의 크기가 중요할때 사용해도 괜찮을거 같다

 

define으로도 가능하다

 

#define 을 한 줄에 다 쓸수 없을 때

\ 문자를 이용하여 다중 줄로 선언할 수 있다.

단, 다중 줄로 선언할 경우 마지막 줄 에는 \ 문자를 붙이지 않는다.

 

언리얼엔진 헤더파일중 일부분

 

다만 define을 사용하면 이런문제가 있다고 한다

myDatatype으로 생성한 변수 b는 왜 오류가 나지 않을까?

이유는 int*가 아닌 int형으로 생성되었기 때문이다

 

myDatatype a, b 를 전처리기에서 변환하면

int *a, b 로 변환되기에

a는 int * 으로 생성되고

b는 int 로 생성 되었기 때문이다

 

그렇기에 자료형의 별칭을 만드려면 define은 비추다

 

그리고 typedef말고 using을 사용해서도 정의할 수 있다

 

그럼 typedef와 using의 차이는 무엇인가?

templete의 사용 유무다

using 같은 경우 템플릿을 사용가능하나

typedef 같은 경우 템플릿을 사용하지 못한다


  • 문자열 비교

c 스타일 - strcmp( const char * 1, const char * 2) 사용

 

훨씬 편한 string은 == 사용하여 비교 가능

 


  • while

루프 몸체와 조건 검사부분으로 이루어진 반복문

 

for과 같이 조건이 true면 루프몸체를 실행한다

 


  • do while

while로직이 "조건검사 ─> 루프몸체 실행" 이라면

 

do while은 "루프몸체 실행 ─> 조건검사" 으로 실행부터하고 검사한다

 

조건이 true면 다시 몸체 실행을 하는 것 도 같다

 


  • Range based for loop

C+11에서 추가된 for의 형태

조건부 구성이다

for( range_declaration : range_expression )

 

range_declaration - 변수의 선언, range_expression의 배열 원소 타입으로 지정한다

range_expression - begin( ), end( ) 함수가 정의된 객체 혹은 braced init list

 

 

braced init list는 중괄호를 사용한 초기화 문법인데 아마 이걸 사용한 데이터를 뜻하지 않나 싶다

단순 동적배열에 begin이나 end가 내부적으로 정의 되어 있는지 모르겠지만

(아마 안되는 듯 싶은데.. 검증해야한다)

일단 braced init list내에 속해 있기에 가능한거같다

 

밑의 링크는 braced init list

https://en.cppreference.com/w/cpp/language/list_initialization

 

range_declartion은 range_expression원소와 자료형이 달라도 자동적으로 캐스팅을 해줌으로

같지는 않더라도 최소 캐스팅이 가능한 자료형이여야한다

 

밑의 링크는 ranged based for이 설명된 사이트

https://en.cppreference.com/w/cpp/language/range-for

밑의 Explanation은 위의 사이트에서 나온 설명이다

정말 기존 for문에서 새로운 코드가 아니라 내부적으로는 위처럼 기존 for문이 작동하나 보다

 

 

보통 밑의 코드처럼 사용하는데

auto를 이용해 자료형을 받는데 편리함을 취하고

&를 이용해 복사비용을 줄이면서 값까지 변경 할 수 있는 방식을 많이 사용하는 듯 하다

 

조건부 배열 부분에 배열이름을 사용했다

처음에는 배열이름은 곧 주소이기도 하니까 포인터를 사용 할 수 있지 않을까 생각을 했지만

그렇게 되면 for문은 그 주소가 가진 배열의 크기를 알지 못하기에 사용 할 수 없다

그렇기 때문에 range_expression부분에 들어오는 데이터에 크기를 알수있는 데이터만 가능한가보다

시도했는데 안된다

그러다 함수내에서 매개변수 받아  ㅅㅂ 어떻게 사용하지? 하다가 이렇게 자료형을 구체적으로 적어주는 걸 생각했다

*가 아닌 &를 사용한건 배열 그 자체의 자료형을 가져오려 사용했다

 

근데 이렇게 사용하다가는 너무 불편할거 같아 템플릿을 이용했다

편안..

 

braced init list 에 있는것중 이런것도 있어 가능한지 확인했다

 

  • stl vector & stl array

기존 정적배열의 관리를 쉽게해주는 템플릿 클래스

 

에서 가변적으로 크기를 쉽게 조정 할 수 있게 만든 템플릿 클래스

 

 

vector의 경우 배열의 크기를 런타임중에도 계속해서 변경 할 수 있는 특징이 있다

내부적으로 new delete를 사용한다

 

array의 경우 크기는 컴파일타임시 초기화를 해야 해 크기가 정적이나

대입연산자의 가능, 배열 크기 이상의 값을 참조했을 때 예외를 반환하는등 좀 더 편하게 다룰 수 있다

 


  • 표현식

값, 또는 값 + 연산자 의 조합으로 이루어진 명백한 하나의 값을 가지는 식

 

일단 지금까지 공부한걸로는 세미콜론을 뺄때 값이 있는 것들은 표현식이 되는거 같다

 

선언, 반복문 등은 표현식이 아니다

리턴문도 표현식이 아니라고 하는데 이건 좀 더 봐야할듯

 


  • for

구성

// 세 개 표현식(expression)으로 이루어진 제어부분
for( Loop Initialization ; Loop Test         ; Loop Update ) 
    =init-expression 	   =cond-expression    =loop-expression 
{
      Loop Body;
}

 

순서

Loop Init ─> Loop Test  ─True─> Loop Body ─> Loop Update ─> Loop Test ....

                           │

                           └──False─> 종료

 

 

제어 부분의 세가지 부분은 모두 생략 가능하다 ( 모두 생략시 무한루프 )

 

 

init-expression, loop-expression은 ' , ' 으로 여러 구문을 사용할 수 있다

※' , '를 cond-expression에 사용을 해도 오류는 나지 않으나 아마 정상 작동 안될 확률99%다

 

 

init-expression에 선언한 변수는 for문 안의 지역변수다

for문이 종료되면 사라진다

※단 구형c++ 경우 루프 앞에 선언한 것으로 인식해 삭제하지 않는 경우도 있다

 

 

책보다 문서가 더 정리가 잘되어있다

https://docs.microsoft.com/ko-kr/cpp/cpp/for-statement-cpp?view=msvc-160#remarks 

 

for 문 (c + +)

Microsoft Visual Studio c + +의 표준 c + +에 대 한 참조입니다.

docs.microsoft.com

 


  • 증가 연산자 & 감소연산자

두개 모두 접두(++a), 접미(a++) 방식으로 사용 가능

 

접두식으로 했을 경우와 접미로 사용했을 경우 연산자 순서가 차이가 많이 나니 주의

 

 

 

한 구문에 너무 많이 사용하면 한대 맞을 수 도있다

int a = 3 * a++ - (34 - --a)/++a;

 

포인터와 함께 사용할 수 있다

접두형 끼리 붙었을때 (++*a) 오른쪽에서 왼쪽으로 실행하면 된다고 생각하면 된다

 

++*a를 예로 *가 ++보다 오른쪽에 있으니 먼저 실행한다.

= a포인터에 들어있는 값( a[0]의 값  )

 

그 후에 왼쪽에 있는 ++가 왼쪽에 있었으니 다음으로 실행된다

여기서 ++가 되는 대상은 단순 a가 아닌 ' * ' 를 실행시킨 *a라는 점

= *a의 값에 ++ (a[0]은 1이며 ++하면 2가 된다)

 


  • 부수 효과 & 시퀀스 포인트

간단하게 부수효과는 수식이 진행될 때 변수에 값이 변경되는 효과

 

 

시퀀스 포인트는 부수효과,특히 접미어 방식이 일어나는 시점을 명확히 알 수 있게 해주는 시점을 나타낸다고 보면 된다

포인트로는 수식의 끝, 세미콜론, While루프의 검사 조건 ...등이 있다

 

위의 코드중 접미에 붙은 ++ 가 일어나는 순서와 시점은 구체적으로 알 수 없다

 

다만 시퀀스 포인트(위의 코드에서 ' ; ' )에서 모두 실행이 되었다고 알수있다

 

이 지점에 왔을 때는 모든 부수효과가 실행 완료 됐다 라고 알려주는 것 이게 시퀀스 포인트의 의미

 


  • 접두어 & 접미어 방식

모두 부수효과가 나타난다 가정시에

 

접두어 방식은 기본값에 부수효과를 적용하고 리턴한다

 

접미어 방식은 기본값을 복사하고 복사한 값에 부수효과를 적용하고 리턴한다

 

이 차이가 있으므로 접두어 방식이 조~~~~~~~~금 낫다고 한다

 

 

나중에 클래스사용시 ++나 -- 를 오버로딩하여 사용할 수 있는데

 

크기가 큰 클래스는 복사비용이 꽤 들테니 둘다 사용해서 상관없다면 접두어 방식을 사용하는 습관을 가지자

 

 

+ Recent posts