profile image

L o a d i n g . . .

반응형

매개변수와 실인자의 구분

매개변수(Parameter)

인자(Argument)

#include <iostream>

using namespace std;

int foo(int x, int y);

// parameter: 함수가 어떤 기능을 하는지, 바꿔 주는 역할
int foo(int x, int y)
{
	// like... int x, y;
	return x + y;
} // x and y are destroyed here, 함수가 끝남과 동시에 소멸됨

int main()
{
	int x = 1, y = 2;

	foo(6, 7);	// 6, 7: arguments (actual parameters)
	foo(x, y + 1);	// x에 있는 값이 argument로, 함수의 parameter로 들어간다
	// 항상 x에 있는 값만 전달되는 것은 아니다? (값, 참조, 주소에 의한 전달)

	return 0;
}

 

 

값에 의한 전달

Passing Arguments by Value (Call by Value)

외부로 영향을 끼치지 않는다! (깔끔하다)

#include <iostream>

using namespace std;

void doSomething(int y)
{
	// 함수를 호출할 때 매개변수로 y를 필요로 함, 내부적으로 int y가 선언되고 거기에 5라는 값이 복사됨
	// 주소가 다르기 때문에 함수 안에서 무슨 짓을 하더라도 main에 영향을 줄 수 없음
	cout << "In func " << y << " " << &y << endl;
}

int main()
{
	doSomething(5);

	int x = 6;

	cout << "In main " << x << " " << &x << endl;

	// x라는 변수가 아니고, x 안에 있는 6이라는 value(값)만 argument로써 parameter로 전달
	doSomething(x);
	doSomething(x + 1);

	return 0;
}

 

 

참조에 의한 인수 전달

Passing Arguments by Reference (Call by Reference)

#include <iostream>
#include <cmath>	// sin(), cos()
#include <vector>

using namespace std;

void addOne(int &y)	// int y라면 아무런 의미 없는 함수
{
	cout << y << " " << &y << endl;
	y += 1;
}

// return값을 여러 개 줘야 할 때, reference 사용
// 입력을 보통 앞에, 출력을 reference로 뒤에
void getSinCos(const double degrees, double &sin_out, double &cos_out)
{
	static const double pi = 3.141592;	// static: 함수 안에서 재사용
	//static const double pi = 3.141592 / 180.0;	// 나누기 연산 한 번만 진행

	const double radians = degrees * pi / 180.0;
	//const double radians = degrees * pi;

	sin_out = std::sin(radians);
	cos_out = std::cos(radians);
}

// const 없이는 리터럴만 받지 못함
// ex) foo(6); //error!
void foo(const int &x)
{
	cout << x << endl;
}

// int *&ptr == (int*) &ptr
void foo2(int *&ptr)
{
	cout << ptr << " " << &ptr << endl;
}

void printElements(const vector<int>& arr)
//void printElements(int (&arr)[4])
{

}

int main()
{
	int x = 5;

	cout << x << " " << &x << endl;

	addOne(x);	// x라는 변수 자체가 넘어감
	cout << x << " " << &x << endl;
	cout << endl;

	double sin(0.0);
	double cos(0.0);
	getSinCos(30.0, sin, cos);
	
	cout << sin << " " << cos << endl;
	cout << endl;

	foo(6);
	cout << endl;

	int *ptr = &x;

	foo2(ptr);
	cout << ptr << " " << &ptr << endl;


	//int arr[]{ 1,2,3,4 };
	vector<int> arr{ 1,2,3,4 };
	printElements(arr);

	return 0;
}

 

 

주소에 의한 인수 전달

Passing Arguments by Address (Call by Address)

#include <iostream>
using namespace std;

typedef int* pint;

//void foo(pint ptr)	주소라는 값을 값에 의한 전달한 것, 주소값이 복사가 됨! 포인터 변수도 변수다
void foo(int *ptr)
{
	cout << *ptr << " " << ptr << " " << &ptr << endl;

	//*ptr = 10;	// const int *ptr의 경우, error!
}

void foo2(double degrees, double *sin_out, double *cos_out)
{
	// 같은 변수가 넘어온 것이 아님! main 변수의 주소와 parameter의 주소는 다름
	// => 값에 의한 전달
	*sin_out = 1.0;
	*cos_out = 2.0;
}

void foo3(const int *ptr, int * const ptr2, int *arr, int length)
{
	for (int i = 0; i < length; i++)
		cout << arr[i] << endl;
	arr[0] = 1.0;	// 메모리 주소에 접근해서 값을 변경, []는 de-referencing
	//*ptr = 1.0;	// error!
	*ptr2 = 1.0;

	int x = 1;
	ptr = &x;	// 가능!
	//ptr2 = &x;	// error!
	// 구현하는 중간에 주소를 바꾸는 경우는 적음
}

int main()
{
	int value = 5;

	cout << value << " " << &value << endl;

	int *ptr = &value;

	foo(ptr);
	foo(&value);
	//foo(5);	// error! literal이라서 주소가 없음
	
	cout << &ptr << endl;	// 함수에서 찍은 것과 주소 다름!


	double degrees = 30;
	double sin, cos;

	foo2(degrees, &sin, &cos);	// 주소로 넣어 줘야 함

	cout << sin << " " << cos << endl;

	return 0;
}

 

 

다양한 반환 값들(값, 참조, 주소, 구조체, 튜플)

#include <iostream>
#include <array>
#include <tuple>

using namespace std;

struct S
{
	int a, b, c, d;
};

int getValue(int x)
{
	int value = x * 2;
	return value;
}

int* getValue2(int x)
{
	int value = x * 2;
	return &value;
}

int* allocateMemory(int size)
{
	return new int[size];	// 주소 반환
}

int& getValue3(int x)
{
	int value = x * 2;
	return value;
}

int& get(std::array<int, 100>& my_array, int idx)
{
	return my_array[idx];	// return reference
}

S getStruct()
{
	S my_s{ 1,2,3,4 };
	return my_s;
}

std::tuple<int, double> getTuple()
{
	int a = 10;
	double d = 3.14;
	return std::make_tuple(a, d);
}

int main()
{
	// return by value
	int value = getValue(3);	// getValue(int)에서 return된 값이 복사되어 들어감


	// return by address
	int value2 = *getValue2(3);	// 권장X, getValue2(int)의 value는 함수가 끝나면 사라짐! 안전하지 못함
	int *value3 = getValue2(3);	// 더 위험함!!
	// warning C4172: returning address of local variable or temporary: value
	
	//int *array = new int[10];
	int *array = allocateMemory(1024);
	delete[] array;


	// return by reference
	int value4 = getValue3(5);
	int &value5 = getValue3(5);	// reference로 받으면? 함수가 끝나면 사라지는 지역변수의 주소값을 받음
	// warning C4172: returning address of local variable or temporary: value

	cout << value5 << endl;
	cout << value5 << endl;	// 쓰레기값 호출


	std::array<int, 100> my_array;	// 메모리가 명확하게 잡혀 있음
	my_array[30] = 10;

	get(my_array, 30) *= 10;	// 변수인 것처럼 사용 가능

	cout << my_array[30] << endl;
	cout << endl;


	// return struct
	// 구조체를 만들 때마다 함수를 만들어야 함
	S my_s = getStruct();
	my_s.b;


	// return tuple
	std::tuple<int, double> my_tp = getTuple();
	cout << std::get<0>(my_tp) << endl;	// a
	cout << std::get<1>(my_tp) << endl;	// d


	// C++ 11
	auto[a, d] = getTuple();	// 각각 변수를 선언하면서 받아 줌
	cout << a << endl;
	cout << d << endl;


	return 0;
}

 

 

인라인 함수

inline functions

#include <iostream>
using namespace std;

inline int min(int x, int y)
{
	return x > y ? y : x;
}

int main()
{
	// 프로그램 실행 시, 함수를 가져오고 변수 x,y를 선언하고 계산 후 return 받아서 출력하는 과정을 겪음
	// 짧은데 자주 사용되는 함수의 경우, 이러한 과정을 줄이기 위해 인라인 함수를 사용하기도 함(주로 헤더파일)
	cout << min(5, 6) << endl;
	cout << min(3, 2) << endl;

	// 다음과 같이 작동함
	cout << (5 > 6 ? 6 : 5) << endl;
	cout << (3 > 2 ? 2 : 3) << endl;

	// inline: 강제로 이 함수를 인라인으로 해라!(X) 이렇게 할 수 있으면 해 주세요(O)
	// 요즘 컴파일러들이 자동으로 inline 처리 해 주기도 한다
	// 인라인 함수가 엄청 많고, 컴파일러가 모두 인라인 처리한다면 프로그램이 커짐!

	return 0;
}

 

 

함수 오버로딩

function overloading: 동일한 이름의 함수를 여러 개 만드는 것

#include <iostream>
#include <string>
using namespace std;

int addInt(int x, int y)
{
	return x + y;
}

int addDouble(double x, double y)
{
	return x + y;
}

// 함수 오버로딩!
int add(int x, int y)
{
	return x + y;
}

double add(double x, double y)
{
	return x + y;
}

/*
error! return 타입만이 다르다면 오버로딩 불가능!
int add(double x, double y)
{
	return x + y;
}
*/

// 매개변수로 값을 돌려받는 방법
void getRandom(int &x) {}
void getRandom(double &x) {}

typedef int my_int;

// 같은 타입으로 인식됨! error C2084: function 'void print(int)' already has a body
void print1(int x) {}
void print1(my_int x) {}

void print2(char *value) {}
void print2(int value) {}
void print2(const char *value) {}	// "a" 예제 해결!

void print3(unsigned int value) {}
void print3(float value) {}

int main()
{
	addInt(1, 2);
	addDouble(1.0, 2.0);
	// int인지 double인지 신경 쓰고 싶지 않아 => 매개변수는 다르지만 작동이 같은 경우, 오버로딩 사용

	add(1, 2);
	add(3.0, 4.0);

	// 이름이 같아도 매개변수가 다르면 다른 함수로 동작함
	// 주어진 인자와 가장 잘 맞는 매개변수인 함수를 찾아서 실행함
	// 따라서 여기서 어떤 add를 사용할지 '컴파일 타임'에 결정되어야 함

	int x;
	getRandom(x);	// 입력인지, 함수에 값만 전달하는지 헷갈림

	// int x = getRandomInt(x);	// return type int인 경우
	// int x = getRandom(int());


	print2(0);	// print(int)
	print2('a');	// print(int)
	//print2("a");	// error!


	// error C2668: 'print3': ambiguous call to overloaded function
	//print3('a');
	//print3(0);
	//print3(3.141592);

	// 의도대로 명확하게 적으면 모호성을 제거할 수 있음! => 애초에 오버로딩할 때 명확하게 구현하기
	// 이름으로 구분하는 게 억지로 사용하는 것보다 좋은 경우도 있음, 주석 달기
	print3((unsigned int)'a');
	print3(0u);
	print3(3.141592f);
	

	return 0;
}

 

 

매개변수의 기본값

#include <iostream>
#include <string>

using namespace std;

// 기본값을 설정할 경우, 한 군데서만 해야 함!! (declaration or definition)
void print(int x = 10, int y = 20, int z = 30);	// in header, 보통 여기에 많이 설정함

//void print(int x, int y = 20, int z) error!
void print(int x, int y, int z)
{
	cout << x << " " << y << " " << z << endl;
}

void print2(std::string str) {}
void print2(char ch = ' ') {}

void print3(int x) {}
void print3(int x, int y = 20) {}


int main()
{
	print();
	print(100);
	print(100, 200);
	print(100, 200, 300);

	print2();	// char 타입
	
	//print3(10);	// error! error C2668: 'print3': ambiguous call to overloaded function

	return 0;
}

 

 

함수 포인터

#include <iostream>
#include <array>
#include <functional>

using namespace std;

int func(int x)
{
	return 5;
}

int goo()
{
	return 10;
}

bool isEven(const int &number)
{
	if (number % 2 == 0) return true;
	else return false;
}

bool isOdd(const int &number)
{
	if (number % 2 != 0) return true;
	else return false;
}

//void printNumbers(const array<int, 10> &my_array , bool print_even)
void printNumbers(const array<int, 10> &my_array, 
	bool (*check_fcn)(const int &) = isEven)	// 기본값 설정도 가능
{
	for (auto element : my_array)
	{
		if (check_fcn(element) == true) cout << element << " ";
		//if (!print_even && element % 2 == 1) cout << element << " ";
	}
	cout << endl;
}

typedef bool(*check_fcn_t)(const int &);
//using check_fcn_t = bool(*)(const int &);	// using도 가능

void printNumbers2(const array<int, 10> &my_array,
	check_fcn_t check_fcn = isEven)	// typedef 및 using 사용 가능
{
	// ...
}

void printNumbers3(const array<int, 10> &my_array,
	std::function<bool(const int &)> fcnptr)
{
	// ...
}


int main()
{
	func(1);		// 이 함수가 어느 주소에 있는지 알아내고, 그 프로그램을 실행시킴
	cout << func << endl;	// 함수의 주소 출력

	int(*fcnptr)(int) = func;	//fcnptr은 변수명임, 변경 가능
	cout << fcnptr(1) << endl;	// 함수 실행

	//fcnptr = goo;	func() => func(int) 수정 후에는 error!
	//cout << fcnptr() << endl;


	std::array<int, 10> my_array = { 0,1,2,3,4,5,6,7,8,9 };

	printNumbers(my_array);
	printNumbers(my_array, isOdd);


	// C++ 11
	std::function<bool(const int &)> fcnptr = isEven;
	printNumbers3(my_array, fcnptr);


	return 0;
}

 

 

스택과 힙 the stack and the heap

메모리는 여러 구역으로 나누어 사용된다.

code: 우리가 작성한 프로그램

bbs: uninitialized data segment, 0으로 초기화된 global & static 변수

data: initialized data segment, global & static 변수

stack: 지역변수, 비교적 속도가 빠름, 사이즈가 작음(stack overflow)

heap: 사이즈가 큼, 함수에서 delete를 하지 않으면 heap의 메모리를 가지고는 있으나 사용하지 못하게 됨(메모리 누수)

 

 

std::vector를 스택처럼 사용하기

#include <iostream>
#include <vector>

using namespace std;

int main()
{
	// int *v_ptr = new int[3]{1, 2, 3}; 와 비슷할 것임
	std::vector<int> v{ 1, 2, 3 };

	v.resize(2);

	// size, capacity? capacity만큼의 용량을 가지고 있고 그 중 size만 사용한다

	for (auto &element : v)
		cout << element << " ";
	cout << endl;

	cout << v.size() << " " << v.capacity() << endl;	// 출력: 2 3

	//cout << v[2] << endl;		runtime error!
	//cout << v.at(2) << endl;	runtime error!

	int *ptr = v.data();

	cout << ptr[2] << endl;	// 출력: 3

	// 직접 동적 메모리 할당시 resize를 하려면,
	// 메모리 2개를 받아 놓고 3개 중 2개를 복사하고, 원래 3개짜리를 delete해야 함
	// vector의 경우, 작은 쪽으로 리사이즈할 때 메모리는 그대로 가지고 있되 접근을 차단함

	// reserve: 메모리의 용량을 미리 확보해 놓음
	// 뒤에 새로 원소를 추가할 때, 따로 메모리 확보하는 과정을 거칠 필요가 없기 때문에 속도가 빠름
	v.reserve(1024);	

	for (unsigned int i = 0; i < v.size(); i++)
		cout << v[i] << " ";
	cout << endl;

	cout << v.size() << " " << v.capacity() << endl;
	

	return 0;
}

#include <iostream>
#include <vector>

using namespace std;

void printStack(const std::vector<int> &stack)
{
	for (auto &e : stack)
		cout << e << " ";
	cout << endl;
}

int main()
{
	std::vector<int> stack;
	//stack.reserve(1024);

	stack.push_back(3);
	printStack(stack);

	stack.push_back(5);
	printStack(stack);

	stack.push_back(7);
	printStack(stack);

	stack.pop_back();
	printStack(stack);

	stack.pop_back();
	printStack(stack);

	stack.pop_back();
	printStack(stack);

	return 0;
}

 

 

재귀적 함수 호출

Recursive Function Call

#include <iostream>
using namespace std;

void countDown(int count)
{
	// 같은 코드를 따로따로 실행시키고 있는 것, 코드는 다른 곳에 저장되어 있고 주소를 찾아서 호출함
	// 스택 오버플로우 주의!
	cout << count << endl;

	if(count > 0)
		countDown(count - 1);
}

int sumTo(int sumto)
{
	if (sumto <= 0)
		return 0;
	else if (sumto <= 1)
		return 1;
	else
		return sumTo(sumto - 1) + sumto;
}

int main()
{
	countDown(2);

	cout << sumTo(10) << endl;

	return 0;
}
#include <iostream>
using namespace std;

// 0 1 2 3 5 8 13 21 ... fibo

int fibo(int num)
{
	if (num == 0) return 0;
	else if (num == 1) return 1;
	else return fibo(num - 1) + fibo(num - 2);
}

int main()
{
	int num;

	for (int i = 0; i <= 20; i++)
	{
		cout << fibo(i) << endl;
	}

	return 0;
}

 

 

방어적 프로그래밍의 개념

#include <iostream>
#include <string>

using namespace std;

int main()
{
	// syntax error
	//int x


	// semantic errors
	int x;
	cin >> x;

	if (x >= 5)	// x > 5
		cout << "x is greater than 5" << endl;


	// violated assumption
	string hello = "Hello, my name is Jack jack";

	/*
	int ix;
	cin >> ix;	// index보다 큰 수가 들어오면 런타임 에러!
	cout << hello[ix] << endl;
	*/

	cout << "Input from 0 to " << hello.size() - 1 << endl;

	while (true)
	{

		int ix;
		cin >> ix;	// index보다 큰 수가 들어오면 런타임 에러!

		if (ix >= 0 && ix <= hello.size() - 1)
		{
			cout << hello[ix] << endl;
			break;
		}
		else
			cout << "Please try again" << endl;
	}


	return 0;
}

 

 

단언하기 assert

디버깅 시, 컴파일러 도움을 받을 때 사용

 

release 모드에서는 아무것도 일어나지 않음, runtime에 체크함

#include <iostream>
#include <cassert>	// assert.h
#include <array>

using namespace std;

void printValue(const std::array<int, 5> &my_array, const int& ix)
{
	assert(ix >= 0);
	assert(ix <= my_array.size() - 1);

	cout << my_array[ix] << endl;
}

int main()
{
	int number = 5;

	// ...
	// 주석만 남겨 놓는다면 직접 값을 찍어 봐야 함
	assert(number == 5);
	
	std::array<int, 5> my_array{ 1,2,3,4,5 };
	printValue(my_array, 100);


	int x = 5;
	const int x2 = 5;
	//assert(x == 5);	// 가능!
	//static_assert(x == 5);	// error! 컴파일 타임에 달라질 수 있기 때문에 불가능
	static_assert(x2 == 5, "x should be 5!");


	return 0;
}

 

 

명령줄 인수 command line arguments

#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
	for (int count = 0; count < argc; count++)
	{
		cout << argv[count] << endl;
	}

	return 0;
}

 

프로젝트 우클릭 > Properties > Debugging

 

#include <iostream>
#include <string>

using namespace std;

int main(int argc, char *argv[])
{
	for (int count = 0; count < argc; count++)
	{
		std::string argv_single = argv[count];

		if (count == 1)
		{
			int input_number = std::stoi(argv_single);
			cout << input_number + 1 << endl;
		}
		else
			cout << argv_single << endl;
	}

	return 0;
}

https://www.boost.org/

 

Boost C++ Libraries

Welcome to Boost.org! Boost provides free peer-reviewed portable C++ source libraries. We emphasize libraries that work well with the C++ Standard Library. Boost libraries are intended to be widely useful, and usable across a broad spectrum of applications

www.boost.org

 

 

생략부호 Ellipsis

#include <iostream>
#include <cstdarg>	// for ellipsis

using namespace std;

// count: parameter로 들어올 argument들의 개수
// 사용하기 위험하고 디버깅 힘듦...
double findAverage(int count, ...)
{
	double sum = 0;

	va_list list;
	va_start(list, count);

	for (int arg = 0; arg < count; arg++)
		sum += va_arg(list, int);	// int로 변환

	va_end(list);

	return sum / count;
}

int main()
{
	cout << findAverage(1, 1, 2, 3, "Hello", 'c') << endl;	// 1만 들어감
	cout << findAverage(3, 1, 2, 3) << endl;
	cout << findAverage(5, 1, 2, 3, 4, 5) << endl;
	cout << findAverage(10, 1, 2, 3, 4, 5) << endl;	// error! 개수 맞춰 줘야 함

	return 0;
}

 

 

해당 포스트는 '홍정모의 따라하며 배우는 C++' 강의를 수강하며 개인 백업용으로 메모하였습니다.

인프런: https://www.inflearn.com/course/following-c-plus

 

홍정모의 따라하며 배우는 C++ - 인프런

만약 C++를 쉽게 배울 수 있다면 배우지 않을 이유가 있을까요? 성공한 프로그래머로써의 경력을 꿈꾸지만 지금은 당장 하루하루 마음이 초조할 뿐인 입문자 분들을 돕기 위해 친절하고 자세하게 설명해드리는 강의입니다. 초보로 시작하더라도 중급을 넘어 고급 프로그래머로 가는 길목에 들어서고 싶으시다면 최고의 디딤돌이 되어드리겠습니다. 여러분의 꿈을 응원합니다! 초급 프로그래밍 언어 C++ 온라인 강의 C++

www.inflearn.com

반응형
복사했습니다!