Chapter 01. C 언어복습및 C++ 기초 박종혁교수 UCS Lab Tel: 970-6702 Email: jhpark1@seoultech.ac.kr SeoulTech 2018-2 nd 프로그래밍입문 (2)
C 언어복습 구조체, 포인터, 기타
01 구조체정의 02 구조체변수정의 03 구조체초기화 구조체
4 구조체의정의 학생의정보를보관하기위한구조체정의 // 'StudentInfo' 라는이름의구조체를정의 struct StudentInfo { char bloodtype; // 혈액형 int stdnumber; // 학번 float grade; // 평점 }; 구조체를정의하는방법
5 구조체변수의정의 // StdudentInfo 구조체타입의변수 2 개를정의한다. StudentInfo si1; StudentInfo si2; 두명의학생정보를보관하기위해서두개의구조체변수를정의 메모리구조
6 구조체 vs 구조체변수 구조체는구조체변수의모양 (layout) 을명시하는설계도와같은역할 ex) 앞에서본구조체정의의의미 컴퓨터야, 앞으로 StudentInfo 라는구조체의모양 (layout) 에대해서알려줄게. 그구주체는 bloodtype, stdnumber, grade 라는변수가들어갈거야 구조체변수를정의할때실제로정보를보관할수있는공간이마련 ex) 앞에서본구조체변수정의의의미 컴퓨터야, 앞에서정의한 StudentInfo 라는구조체알지? 그때알려준모양 (layout) 으로변수를하나만들어다오
7 멤버에접근하기 구조체에포함되는각변수 - 멤버혹은멤버변수 ex) StudentInfo 구조체의멤버변수들 bloodtype, stdnumber, grade StudentInfo si1; si1.bloodtype = 'O'; si1.stdnumber = 20031128; si1.grade = 3.5f; 멤버변수의값을얻어오거나변경하는방법
8 구조체의초기화 구조체변수를정의함과동시에모든멤버들을초기화할수있음 struct StudentInfo { char bloodtype; int stdnumber; float grade; }; // 혈액형 // 학번 // 평점 int main() { // StudentInfo 구조체타입의변수 2 개를정의한다. StudentInfo si1 = { 'O', 20031128, 3.5f }; StudentInfo si2 = { 'A', 19961219, 2.3f };..
9 구조체의대입 구조체변수끼리대입을하면각멤버변수들이 1:1 로복사된다. struct Point { int x; int y; }; Point pt1 = { 30, 50}; Point pt2; // x 좌표 // y 좌표 pt2 = pt1; // pt1 을 pt2 에대입한다. cout << "pt1 = ( " << pt1.x << ", " << pt1.y << ")\n"; cout << "pt2 = ( " << pt2.x << ", " << pt2.y << ")\n"; 실행결과
10 구조체정의 2 구조체를정의하는동시에구조체변수를정의할수있다. struct Point { int x; int y; } pt1 = { 30, 50}, pt2; 이런경우에는구조체의이름을생략할수있다. struct // 구조체의이름생략 { int x; int y; } pt1 = { 30, 50}, pt2;
01 포인터의기본 02 포인터와 Const 포인터
12 포인터의개념 포인터변수 - 다른변수를가리키는변수 포인터변수에는다른변수의주소값을저장할수있다. 예 ) 포인터변수 A 가다른변수 B 의주소값을보관하고있다면, 포인터변수 A 가변수 B 를가리키고있다고말한다.
13 포인터변수에변수주소보관하기 포인터변수 p 가변수 a 를가리키도록만드는예 // 일반적인변수를정의한다. int a = 123; // 포인터변수를정의한다. int* p; // p 가 a 를가리키도록만든다. p = &a; // 관련정보를출력한다. cout << "&a = " << &a << "\n"; cout << "p = " << p << "\n"; cout << "&p = " << &p << "\n"; 실행결과
14 포인터변수의정의 포인터변수를정의할때는가리키고자하는변수의타입을지정해두어야한다. 다양한타입의포인터변수 char c = 'C'; char* pc = &c; float f = 700.5f; float* pf = &f; bool b = true; bool* pb = &b; short int s = 456; short int* ps = &s;
15 void 포인터 void* p; void* 타입의포인터변수는어떤타입의변수라도가리킬수있음 그러므로, void 포인터가가리키는데이터의크기나종류를알아낼수없음.
16 포인터안전하게사용하기 잘못된주소를가진포인터를사용하는것은매우위험하기때문에, 포인터를사용할때는다음의가이드라인을따른다. 포인터변수는항상 0 혹은 NULL 값으로초기화한다. 포인터변수를사용하기전에는 0 혹은 NULL 값을가지고있는지확인한다. // 포인터변수를정의하고초기화한다. int* p = NULL; // 이상태에서포인터를사용해보자. if (NULL!= p) *p = 30; // p 가변수를가리키게만들자 int a = 100; p = &a; // 이상태에서포인터를사용해보자. if (!p) *p = 30;
17 Const 속성을가진변수 변수의값이변경되는것을막기위해서 Const 속성을사용해서변수를정의할수있다. // const 속성을가진변수를정의한다. const int a = 123; // a 의값을바꾸려고했으므로컴파일에러발생!! a = 456; 배열의크기값을보관하기위해서 Const 변수를사용한예 // 배열의크기를 const 변수에보관한다. const unsigned int arraysize = 100; // 배열을정의한다. char characters[ arraysize ] = {0}; // 배열을사용한다. for (int i = 0; i < arraysize; ++i) characters[i] = i + 1;
18 Const 와포인터 (1) 포인터변수에 Const 속성을부여하는경우 포인터변수자체 포인터변수가가리키는변수
19 Const 와포인터 (2) 포인터변수에 Const 속성을적용한 3 가지경우비교 포인터변수가가리키는변수가 const 인경우 int i1 = 10; int i2 = 20; const int* p = &i1; p = &i2; // OK *p = 30; // FAIL int i1 = 10; int i2 = 20; 포인터변수자신이 const 인경우 두변수모두 const 인경우 int i1 = 10; int i2 = 20; const int* const p = &i1; p = &i2; // FAIL *p = 30; // FAIL int* const p = &i1; p = &i2; // FAIL *p = 30; // OK
C 언어의복습을유도하는확인학습문제 1 20
C 언어의복습을유도하는확인학습문제 2 21
C 언어의복습을유도하는확인학습문제 3 22
23 C 언어의복습을유도하는확인학습문제 1 문제 1 의답안
24 C 언어의복습을유도하는확인학습문제 2 문제 2 의답안
25 C 언어의복습을유도하는확인학습문제 3 문제 3 의답안
26 Call-by-value & Call-by-reference 값을전달하면서호출하게되는함수이므로이 함수는 Call-by-value 이다. 이경우함수외에 선언된변수에는접근이불가능하다. 값은값이되, 주소값을전달하면서호출하게되는함수이므로이함수는 Call-by-reference 이다. 이경우인자로전달된주소의메모리공간에접근이가능하다!
27 Call-by-address? Call-by-reference! 포인터 ptr 에전달된주소값의관점에서보면 이는 Call-by-value 이다. 주소값을전달받아서외부에있는메모리공간 에접근을했으니이는 Call-by-reference 이다. C++ 에는두가지형태의 Call-by-reference 가존재한다. 하나는주소값을 이용하는형태이며, 다른하나는참조자를이용하는형태이다.
C++ 기초
29 printf 와 scanf 출력의기본형태 : 과거스타일! iostream.h 헤더파일의포함 cout << 출력대상 ; cout<< 출력대상 1<< 출력대상 2<< 출력대상 3; cout<<1<<'a'<<"string"<<endl;
30 출력의기본형태 : 현재스타일! iostream 헤더파일의포함 std::cout << 출력대상 ; std::cout<< 출력대상 1<< 출력대상 2<< 출력대상 3; std::cout<<1<<'a'<<"string"<<std::endl;
31 입력의기본형태 : 과거스타일! iostream.h 헤더파일의포함 cin>> 입력변수 ; cin>> 입력변수 1>> 입력변수 2>> 입력변수 3; cin>>val1;
32 입력의기본형태 : 현재스타일! iostream 헤더파일의포함 std::cin>> 입력변수 ; std::cin>> 입력변수 1>> 입력변수 2>> 입력변수 3; std::cin>>val1;
33 예제 1) 기본적인 C++ 프로그램 /* 소스 : SimpleC++.cpp cout 과 << 연산자를이용하여화면에출력한다. */ #include <iostream> // cout 과 << 연산자포함 // C++ 프로그램은 main() 함수에서부터실행을시작한다. int main() { std::cout << "Hello\n"; // 화면에 Hello 를출력하고다음줄로넘어감 std::cout << " 첫번째맛보기입니다."; return 0; // main() 함수가종료하면프로그램이종료됨 } Hello 첫번째맛보기입니다.
34 주석문과 main() 함수 주석문 개발자가자유롭게붙인특이사항의메모, 프로그램에대한설명 프로그램의실행에영향을미치지않음 여러줄주석문 - /*... */ 한줄주석문 - // 를만나면이줄의끝까지주석으로처리 main() 함수 C++ 프로그램의실행을시작하는함수 main() 함수가종료하면 C++ 프로그램종료 main() 함수의 C++ 표준모양 int main() { // main() 의리턴타입 int... return 0; // 0 이아닌다른값으로리턴가능 } main() 에서 return 문생략가능 int main() {... // return 0; // 개발자의편리를위해 return 문생략가능 } void main() { // 표준아님... }
35 #include <iostream> #include <iostream> 전처리기 (C++ Preprocessor) 에게내리는지시 <iostream> 헤더파일을컴파일전에소스에확장하도록지시 <iostream> 헤더파일 표준입출력을위한클래스와객체, 변수등이선언됨 ios, istream, ostream, iostream 클래스선언 cout, cin, <<, >> 등연산자선언 #include <iostream>... std::cout << "Hello\n"; std::cout << " 첫번째맛보기입니다.";
36 화면출력 cout 과 << 연산자이용 std::cout << "Hello\n"; // 화면에 Hello 를출력하고다음줄로넘어감 std::cout << " 첫번째맛보기입니다."; cout 객체 스크린출력장치에연결된표준 C++ 출력스트림객체 <iostream> 헤더파일에선언 std 이름공간에선언 : std::cout 으로사용 << 연산자 스트림삽입연산자 (stream insertion operator) C++ 기본산술시프트연산자 (<<) 가스트림삽입연산자로재정의됨 ostream 클래스에구현됨 오른쪽피연산자를왼쪽스트림객체에삽입 cout 객체에연결된화면에출력 여러개의 << 연산자로여러값출력 std::cout << "Hello\n" << " 첫번째맛보기입니다.";
37 예제 2) cout 과 << 를이용한화면출력 #include <iostream> double area(int r); // 함수의원형선언 double area(int r) { // 함수구현 return 3.14*r*r; // 반지름 r 의원면적리턴 } int main() { int n=3; char c='#'; } std::cout << c << 5.5 << '-' << n << "hello" << true << std::endl; std::cout << "n + 5 = " << n + 5 << '\n'; std::cout << " 면적은 " << area(n); // 함수 area() 의리턴값출력 #5.5-3hello1 n + 5 = 8 면적은 28.26 true 는 1 로출력됨
38 함수오버로딩 함수오버로딩 ( 이름의중복 ) 파일의확장자는.C 이다! 무엇이문제? int function(void){ } return 10; int function(int a, int b){ } return a+b; int main(void) { } function(); function(12, 13); return 0;
39 함수오버로딩 함수오버로딩이란? 동일한이름의함수를중복해서정의하는것! 함수오버로딩의조건 매개변수의개수혹은타입이일치하지않는다. 함수오버로딩이가능한이유 호출할함수를매개변수의정보까지참조해서호출 함수의이름 + 매개변수의정보
40 함수오버로딩의예 int function1(int n) { } int function1(char c) { } int function2(int v) { } int function2(int v1, int v2) { }
41 디폴트매개변수 디폴트매개변수란? 전달되지않은인자를대신하기위한기본값이설정되어있는변수
42 디폴트매개변수 vs 함수오버로딩 디폴트매개변수와함수오버로딩을잘못정의한예 #include<iostream> int function(int a=10){ } return a+1; main 함수를변경 int function(void){ } return 10; int main(void) { std::cout<<function(10)<<std::endl; return 0; } 컴파일러는혼동 : 에러발생
43 인-라인함수 매크로함수를통한인-라인 인-라인화된함수 장점! : 실행속도의향상 단점! : 구현의어려움 #include <iostream> #define SQUARE(x) ((x)*(x)) int main(void) { std::cout<< SQUARE(5) <<std::endl; return 0; }
44 inline 선언에의한함수의인 - 라인화 컴파일러에의해서처리 매크로함수의장점을그대로반영 구현의용이성제공 컴파일러에게최적화의기회제공 #include <iostream> inline int SQUARE(int x) { return x*x; } int main(void) { std::cout<<square(5)<<std::endl; return 0; }
45 이름공간 (namespace) 이름공간이란? 공간에이름을주는행위! "202호에사는철수야 " #include <iostream> void function(void) { std::cout<<"a.com 에서정의한함수 "<<std::endl; } void function(void) { std::cout<<"b.com 에서정의한함수 "<<std::endl; } int main(void) { function(); return 0; }
46 namespace 개념 이름 (identifier) 충돌이발생하는경우 여러명이서로나누어프로젝트를개발하는경우 오픈소스혹은다른사람이작성한소스나목적파일을가져와서컴파일하거나링크하는경우해결하는데많은시간과노력이필요 namespace 키워드 이름충돌해결 2003년새로운 C++ 표준에서도입 개발자가자신만의이름공간을생성할수있도록함 이름공간안에선언된이름은다른이름공간과별도구분 이름공간생성및사용 namespace kitae { // kitae 라는이름공간생성... // 이곳에선언된모든이름은 kitae 이름공간에생성된이름 } 이름공간사용 이름공간 :: 이름
이름공간의적용 47
아하! std 란 namespace! 48
49 편의를위한 using 선언! using A_COM::function; using namespace A_COM;
50 예제 ) C++ 프로그램에서키입력받기 #include <iostream> using namespace std; int main() { cout << " 너비를입력하세요 >>"; int width; cin >> width; // 키보드로부터너비를읽어 width 변수에저장 cout << " 높이를입력하세요 >>"; int height; cin >> height; // 키보드로부터높이를읽어 height 변수에저장 } int area = width*height; // 사각형의면적계산 cout << " 면적은 " << area << "\n"; // 면적을출력하고다음줄로넘어감 너비를입력하세요 >>3 높이를입력하세요 >>5 면적은 15
51 표준 C++ 헤더파일은확장자가없다 표준 C++ 에서헤더파일확장자없고, std 이름공간적시 #include <iostream> using namespace std; 헤더파일의확장자비교
52 범위지정연산자기반전역변수접근 int val=100; int val=100; int main(void) { int val=100; val+=1; return 0; } int main(void) { int val=100; ::val+=1; return 0; }
53 참조자 (Reference) 의이해 변수의선언으로인해서 num1 이라는 이름으로메모리공간이할당된다. 참조자의선언으로인해서 num1 의메 모리공간에 num2 라는이름이추가로 붙게된다. 참조자는기존에선언된변수에붙이는 별칭 이다. 그리고이렇게참조자가만들어지면 이는변수의이름과사실상차이가없다.
54 예제 #include <iostream> using namespace std; int main() { int var; int &ref = var; // 참조자선언 var = 10; cout << "var 의값 : " << var << endl; cout << "ref 의값 : " << ref << endl; ref = 20; // ref 의값을변경하면 var 의값도변경된다. cout << "var 의값 : " << var << endl; cout << "ref 의값 : " << ref << endl; } return 0; var 의값 : 10 ref 의값 : 10 var 의값 : 20 ref 의값 : 20
55 참조자관련예제와참조자의선언 num2는 num1의참조자이다. 따라서이후부터는 num1으로하는모든연산은 num2로하는것과동일한결과를보인다. 실행결과 참조자의수에는제한이없으며, 참조자 를대상으로참조자를선언하는것도가 능하다.
56 참조자의선언가능범위 불가능한참조자의선언의예 상수대상으로의참조자선언은불가능하다. 참조자는생성과동시에누군가를참조해야한다. 포인터처럼 NULL 로초기화하는것도불가능하다. 정리하면, 참조자는선언과동시에누군가를참조해야하는데, 그참조의대상은기본적으로변수가되어야한다. 그리고참조자는참조의대상을변경할수없다. 변수의성향을지니는대상이라면참조자의선언이가능하다. 배열의요소역시변수의성향을지니기때문에참조자의선언이가능하다. 실행결과
57 포인터변수대상의참조자선언 ptr 과 dptr 역시변수이다. 다만주소값을 저장하는포인터변수일뿐이다. 따라서이렇 듯참조자의선언이가능하다. 실행결과
58 참조자와포인터의활용 num ref ptr pref dptr dpref 12 1024 1028 주소 : 1024 주소 : 1028 포인터변수 : ptr, dptr 참조자 : ref, pref, dpref
59 참조자와포인터비교 참조자는반드시선언과동시에초기화 int &ref; // 오류! 포인터는변경될수있지만참조자는변경이불가능하다. int &ref = var1; ref = var2; // 오류! 참조자를상수로초기화하면컴파일 int &ref = 10; // 오류!
60 참조자를이용한 Call-by-reference 매개변수는함수가호출될때선언이되는변수이므로, 함수 호출의과정에서선언과동시에전달되는대상으로초기화된 다. 즉, 매개변수에선언된참조자는여전히선언과동시에초기 화된다. 참조자기반의 Call-by-reference!
61 참조자를통한효율성향상 객체의크기가큰경우, 복사는시간이많이걸린다. 이때는참조자로처리하는것이유리
62 const 참조자 함수의호출형태 함수의정의형태 함수의정의형태와함수의호출형태를보아도값의변경유무를알수없다! 이를알려 면 HappyFunc 함수의몸체부분을확인해야한다. 그리고이는큰단점이다! 함수 HappyFunc 내에서참조자 ref 를이용한값의변경은허용하지않겠다! 라는의미! 함수내에서참조자를통한값의변경을진행하지않을경우참조자를 const로선언해서, 다음두가지장점을얻도록하자! 1. 함수의원형선언만봐도값의변경이일어나지않음을판단할수있다. 2. 실수로인한값의변경이일어나지않는다.
63 동적메모리할당 : new & delete malloc 을대한하는메모리의동적할당방법! 크기를바이트단위로계산하는일을거치지않아도된다! free 를대신하는메모리의해제방법! new 연산자로할당된메모리공간은반드시 delete 함수호출을통해서소멸해야한다! 특히이후에공부 하는객체의생성및소멸과정에서호출하게되는 new & delete 연산자의연산자의연산특성은 malloc & free 와큰차이가있다!
64 C++ 의표준헤더 : c 를더하고.h 를빼라. 이렇듯 C 언어에대응하는 C++ 헤더파일이름 의정의에는일정한규칙이적용되어있다. 표준 C 의 abs 함수 대응하는 C++ 의표준 abs 함수 이렇듯, 표준 C에대응하는표준 C++ 함수는 C++ 문법을기반으로변경및확장되었다. 따라서가급적이면 C++ 의헤더파일을포함하여, C++ 의표준함수를호출해야한다.
65 실전문제 1) 최대한의사탕사기 철수가가지고있는돈으로최대한의사탕을사려고한다. 현재 1000 원이있고사탕의가격이 300 원이라고하자. 최대한살수있는사탕의개수와나머지돈은얼마인가?
정답 ) #include <iostream> using namespace std; int main() { int money; int candy_price; 66 cout << " 현재가지고있는돈 : "; cin >> money; cout << " 캔디의가격 : "; cin >> candy_price; // 최대한살수있는사탕수 int n_candies = money / candy_price; cout << " 최대로살수있는캔디의개수 =" << n_candies << endl; } // 사탕을구입하고남은돈 int change = money % candy_price; cout << " 캔디구입후남은돈 =" << change << endl; return 0;
67 실전문제 2) 최대한의사탕사기 우리나라는섭씨온도를사용하지만미국에서는화씨온도를사용한다. 화씨온도를섭씨온도로바꾸는프로그램을작성하여보자.
68 정답 ) #include <iostream> using namespace std; int main() { double f_temp = 60; double c_temp; c_temp = (5.0 / 9.0) * (f_temp - 32); cout << " 화씨온도 " << f_temp << " 도는섭씨온도 " << c_temp << " 입니다." << endl; return 0; }
69 참고문헌 뇌를자극하는 C++ 프로그래밍, 이현창, 한빛미디어, 2011 열혈 C++ 프로그래밍 ( 개정판 ), 윤성우, 오렌지미디어, 2012 객체지향원리로이해하는 ABSOLUTE C++, 최영근외 4 명, 교보문고, 2013 C++ ESPRESSO, 천인국, 인피니티북스, 2011 명품 C++ Programming, 황기태, 생능출판사, 2018 어서와 C++ 는처음이지, 천인국, 인피니티북스, 2018
Q&A
추가참고자료 - 참조자부연설명 - 포인터사용없이힙에접근하기
참조자추가설명 72 반환형이참조이고반환도참조로받는경우 반환의과정에서일어나는일은다음의경우와같다.
73 반환형은참조이나반환은변수로받는경우 반환의과정에서일어나는일은다음의경우와같다.
74 참조를대상으로값을반환하는경우 참조자를반환하건, 변수에저장된값을반환하건, 반환형이참 조형이아니라면차이는없다! 어차피참조자가참조하는값이나 변수에저장된값이반환되므로! 반환형이참조형인경우에는반환되는대상을참조자로그리고 변수로받을수있다. 그러나반환형이값의형태라면, 참조자로그값을받을수없다!
75 잘못된참조의반환 이와같이지역변수를참조의형태로반환하는것은문제의소지 가된다. 따라서이러한형태로는함수를정의하면안된다. 에러의원인! ref 가참조하는대상이소멸된다!
76 const 참조자의또다른특징 에러의원인! 이를허용한다는것은 ref 를통한값의변경을허용한다는뜻이되고, 이는 num을 const로선언하는이유를잃게만드는결과이므로! 해결책! 따라서한번 const 선언이들어가기시작하면관련해서몇몇변수들이 const 로선언되어야하는데, 이는프로그램의안전성을높이는결과로이어지기때 문에, const 선언을빈번히하는것은좋은습관이라할수있다.
77 어떻게참조자가상수를참조하냐고요! const 참조자는상수를참조할수있다. 이유는, 이렇듯, 상수를 const 참조자로참조할경우, 상수를메모리공간에임시적으로저장하기때문이다! 즉, 행을바꿔도소멸시키지않는다. 이러한것이가능하도록한이유! 이렇듯매개변수형이참조자인경우에상 수를전달할수있도록하기위함이바로이 유이다!
포인터를사용하지않고힙에접근하기 78 변수의성향을지니는 ( 값의변경이가능한 ) 대상에대해서는 참조자의선언이가능하다. C 언어의경우힙영역으로의접근을위해서는반드시포인터를사용해야만했다. 하지만 C++ 에서는 참조자를이용한접근도가능하다!