C++ 기초플러스 (05. 루프와 관계 표현식 - 07. 함수 - C++의 프로그래밍 모듈)
for 루프
// forstr1.cpp -- for 루프를 사용한 문자열 처리
#include <iostream>
#include <string>
int main()
{
using namespace std;
cout << "단어를 입력하세요: ";
string word;
cin >> word;
// 문자열을 거꾸로 출력한다.
for (int i = word.size() - 1; i >= 0; i--)
cout << word[i];
cout << "\n종료.\n";
// cin.get();
// cin.get();
return 0;
}
부수 효과와 시퀀스 포인트
부수 효과(side effect) 란?
수식을 평가할 때 변수에 저장되어 있는 값과 같은 것이 변경될 때 일어나는 효과를 말한다.
시퀀스 포인트 란?
프로그램의 실행이 다음 단계로 넘어가기 전에 모든 부수 효과들이 확실하게 평가되는 포인트이다.
C++에서 구문에 있는 세미콜론은 시퀀스 포인트를 표시한다.
그것은 구문에서 대입연산자, 증가연산자, 감소연산자에 의해 일어나는 모든 변화가 프로그램이 다음 구문으로 넘어가기 전에 반드시 일어나야 한다는 것을 의미한다.
증가 / 감소 연산자와 포인터
double arr[5] = {21.1, 32.8, 23.4, 45.2, 37.4};
double *pt = arr; // pt는 arr[0], 즉 21.1을 지시한다
++pt; // pt는 arr[1], 즉 32.8을 지시한다
// 접두형 연산자들에 적용되는 오른쪽에서 왼쪽으로의 결합 규칙은
// *++pt의 경우에 (++가 *의 오른쪽에 있기 때문에) ++가 pt에 먼저 적용되고 나서, pt의 새로운 값에
// *가 적용된다는 것을 의미한다.
*++pt; // 포인터 증가, 값을 취한다; 즉, arr[2], 또는 23.4
// pt가 지시하는 값을 먼저 구하고 나서, 그값을 증가시킨다
++*pt; // 지시되는 값 증가; 즉, 23.4를 24.4로 변경
// 지시되는 값 증가
(*pt)++; // 24.4가 얻어진다; 그리고나서 ++연산자가 그 값을 25.4로 증가시킨다.
*pt++; // 원래 주소를 참조하고 나서 포인터 증가
콤마 연산자
// forstr2.cpp -- 문자열 뒤집기
#include <iostream>
#include <string>
int main()
{
using namespace std;
cout << "단어를 입력하세요: ";
string word;
cin >> word;
// string 객체를 실제로 변경한다.
char temp;
int i, j;
for (j = 0, i = word.size() - 1; j < i; --i, ++j)
{ // 블록 시작
temp = word[i];
word[i] = word[j];
word[j] = temp;
} // 블록 끝
cout << word << "\n끝\n";
// cin.get();
// cin.get();
return 0;
}
- ++j , --i : 두 개의 표현식이 하나의 표현식으로 간주된다.
while문
// while.cpp -- while 루프 문
#include <iostream>
const int ArSize = 20;
int main()
{
using namespace std;
char name[ArSize];
cout << "영문 이름을 입력하세요: ";
cin >> name;
cout << "귀하의 영문 이름을 한 줄에 한 자씩:\n";
int i = 0; // 문자열의 첫머리에서 시작
while (name[i] != '\0') // 문자열의 끝자리 처리
{
cout << name[i] << ": " << int(name[i]) << endl;
i++;
}
// cin.get();
// cin.get();
return 0;
}
- while문 조건으로 배열에 널문자 (\0)인지 검사한다.
잠시만 - 시간 지연 루프
예를들어, 메시지를 출력한 뒤에 사용자가 그 메시지를 읽기도 전에 다른 동작으로 후딱 전환하기 전에 시간을 지연시키는 동작을 넣어 사용되어 진다.
// waiting.cpp -- clock()을 시간 지연 루프에 사용한다.
#include <iostream>
#include <ctime> // clock() 함수와 clock_t형이 정의되어있다.
int main()
{
using namespace std;
cout << "지연 시간을 초 단위로 입력하세요: ";
float secs;
cin >> secs;
clock_t delay = secs * CLOCKS_PER_SEC; // 지연 시간을 시스템 단위 클록 수로 변환
cout << "카운트를 시작합니다.\a\n";
clock_t start = clock();
while (clock() - start < delay ) // 시간이 경과할 때까지 대기
; // 세미콜론에 유의
cout << "끝 \a\n";
// cin.get();
// cin.get();
return 0;
}
3초 후
06. 분기 구문과 논리 연산자
레이블로 열거자 사용하기
// enum.cpp -- using enum
#include <iostream>
// create named constants for 0 - 6
enum {red, orange, yellow, green, blue, violet, indigo};
int main()
{
using namespace std;
cout << "컬러 코드 (0-6)를 입력하세요: ";
int code;
cin >> code;
while (code >= red && code <= indigo)
{
switch (code)
{
case red : cout << "입술은 붉었습니다..\n"; break;
case orange : cout << "머리카락은 귤색이었습니다.\n"; break;
case yellow : cout << "신발은 노랑색이었습니다.\n"; break;
case green : cout << "손톱은 초록색이었습니다.\n"; break;
case blue : cout << "스웨터는 파랑색이었습니다.\n"; break;
case violet : cout << "눈은 보라색이었습니다.\n"; break;
case indigo : cout << "분위기는 쪽빛이었습니다.\n"; break;
}
cout << "컬러 코드 (0-6)를 입력하세요: ";
cin >> code;
}
cout << "끝\n";
// cin.get();
// cin.get();
return 0;
}
수를 읽어들이는 루프
// cinfish.cpp -- 수가 아닌 입력으로 루프를 종료시킨다.
#include <iostream>
const int Max = 5;
int main()
{
using namespace std;
// 데이터를 입력받는다
double fish[Max];
cout << "오늘 낚은 물고기의 무게를 입력하세요.\n";
cout << "물고기는 최대 " << Max
<< " 마리까지 낚을 수 있습니다. <입력을 끝내려면 q를 누르세요>.\n";
cout << "fish #1: ";
int i = 0;
while (i < Max && cin >> fish[i]) {
if (++i < Max)
cout << "fish #" << i+1 << ": ";
}
// 평균을 계산한다.
double total = 0.0;
for (int j = 0; j < i; j++)
total += fish[j];
// 결과를 보고한다
if (i == 0)
cout << "물고기를 한 마리도 낚지 못하셨군요.\n";
else
cout << "물고기 "<< i << "마리의 평균 무게는 "
<< total / i << " 그램입니다. \n";
cout << "종료.\n";
// cin.get(); // read end of line after last input
// cin.get(); // wait for user to press <Enter>
return 0;
}
[수가 아닌 값을 입력 예외 처리]
// cingolf.cpp -- 수가 아닌 입력은 무시한다
#include <iostream>
const int Max = 5;
int main()
{
using namespace std;
// get data
int golf[Max];
cout << "골프 점수를 입력하세요.\n";
cout << "총 " << Max << " 라운드 점수를 입력해야 합니다.\n";
int i;
for (i = 0; i < Max; i++)
{
cout << "round #" << i+1 << ": ";
while (!(cin >> golf[i])) { // cin값이 0이 아닌이상 true
cin.clear(); // 입력을 초기화 한다
while (cin.get() != '\n')
continue; // 불량 입력을 제거한다
cout << "골프 점수를 입력하세요: ";
}
}
// 평균을 계산한다
double total = 0.0;
for (i = 0; i < Max; i++)
total += golf[i];
// 결과를 보고한다
cout << "총 " << Max << " 라운드의 평균점수 = "
<< total / Max << endl;
// cin.get();
// cin.get();
return 0;
}
- must i? 입력 시 while( !(cin >> golf[i])) 표현식은 false가 되고 !(cin >> golf[i])는 true가 되어 루프가 진행된다.
07. 함수 - C++의 프로그래밍 모듈
함수 원형과 함수 호출
// protos.cpp -- 함수 원형과 함수 호출
#include <iostream>
void cheers(int); // 함수 prototype : 리턴 값이 없다.
double cube(double x); // 함수 prototype: double형을 리턴한다.
int main()
{
using namespace std;
cheers(5); // 함수 호출
cout << "하나의 수를 입력하세요: ";
double side;
cin >> side;
double volume = cube(side); // 함수 호출
cout << "한 변의 길이가 " << side <<" 센티미터인 정육면체의 부피는 ";
cout << volume << " 세제곱센티미터입니다.\n";
cheers(cube(2)); // 원형 보호에 의해 작동한다.
// cin.get();
// cin.get();
return 0;
}
void cheers(int n)
{
using namespace std;
for (int i = 0; i < n; i++)
cout << "Cheers! ";
cout << endl;
}
double cube(double x)
{
return x * x * x;
}
- 함수안에서 std 이름공간의 멤버들을 사용하는 함수에만 using 지시자를 넣는다.
두 개의 매개변수를 사용하는 또 다른 함수
// lotto.cpp -- 승률 구하기
#include <iostream>
long double probability(unsigned numbers, unsigned picks);
int main()
{
using namespace std;
double total, choices;
cout << "전체 수의 개수와 뽑을 수의 개수를 입력하세요: \n";
while ((cin >> total >> choices) && choices <= total)
{
cout << "당신이 이길 확률은 ";
cout << probability(total, choices); // 승률을 계산한다
cout << "번 중에서 한 번입니다.\n";
cout << "다시 두 수를 입력하세요 (끝내려면 q를 입력): ";
}
cout << "끝\n";
// cin.get();
// cin.get();
return 0;
}
// 이 함수는 numbers개의 수 중에서
// picks개의 수를 정확하게 뽑을 확률을 계산한다
long double probability(unsigned numbers, unsigned picks)
{
long double result = 1.0; // 이 자리에는 지역 변수들이 온다
long double n;
unsigned p;
for (n = numbers, p = picks; p > 0; n--, p--)
result = result * n / p ;
return result;
}
포인터를 배열 이름처럼 사용하는 방법
// arrfun1.cpp -- 배열 매개변수를 사용하는 함수
#include <iostream>
const int ArSize = 8;
int sum_arr(int arr[], int n); // prototype
int main()
{
using namespace std;
int cookies[ArSize] = {1,2,4,8,16,32,64,128};
int sum = sum_arr(cookies, ArSize);
cout << "먹은 과자 수 합계: " << sum << "\n"; // using 사용하지만 std::cout으로 사용할 수 있다
// cin.get();
return 0;
}
// 정수 배열의 합계를 리턴한다
int sum_arr(int arr[], int n)
{
int total = 0;
for (int i = 0; i < n; i++)
total = total + arr[i];
return total;
}
*** cookies는 그배열의 하나의 원소의 주소이다.
배열을 매개변수로 사용하는 것의 의미
** 매개변수 배열은 첫 번째 원소의 주소값을 나타낸다.
// arrfun2.cpp -- 배열 매개변수를 사용하는 함수
#include <iostream>
const int ArSize = 8;
int sum_arr(int arr[], int n);
// using 지시자 대신 std::를 사용한다
int main()
{
int cookies[ArSize] = {1,2,4,8,16,32,64,128};
std::cout << cookies << " = 배열 주소, ";
std::cout << sizeof cookies << " = sizeof cookies\n";
int sum = sum_arr(cookies, ArSize);
std::cout << "먹은 과자 수 합계: " << sum << std::endl;
sum = sum_arr(cookies, 3); // 배열 원소 개수 속이기
std::cout << "처음 세 사람은 과자 " << sum << " 개를 먹었습니다.\n";
sum = sum_arr(cookies + 4, 4); // 배열 원소 개수 속이기
std::cout << "마지막 네 사람은 과자 " << sum << " 개를 먹었습니다.\n";
// std::cin.get();
return 0;
}
// 정수 배열의 합계를 리턴한다
int sum_arr(int arr[], int n)
{
int total = 0;
std::cout << arr << " = arr, ";
std::cout << sizeof arr << " = sizeof arr\n";
for (int i = 0; i < n; i++)
total = total + arr[i];
return total;
}
배열의 내용 출력과 const로 보호하기
배열의 원본을 가지고 작업하는데 배열의 값이 변경되지 않도록 하려면 const 키워드를 형식 매개변수를 선언할 때 사용할 수 있다.
void show_array(const double ar[ ] , int n);
=> 포인터 ar이 상수 데이터를 지시하고 있다는 것을 의미하며, ar을 사용해서는 그 데이터를 변경할 수 없다는 것을 뜻한다.
이것은 원본 배열이 반드시 상수이어야 한다는 의미는 아니고, 다만 show_array() 함수가 ar을 사용하여 그 데이터를 변경할 수 없다는 뜻이다.
배열의 범위를 사용하는 함수
배열을 처리하는 함수에 접근하는 방법은
- 매개변수로 배열의 시작 위치를 지시하는 포인터를 전달한다.
- 매개변수로 배열의 크기를 전달한다.(그 포인터는 배열이 어디에 있는지와, 배열에 들어 있는 데이터의 종류가 무엇인지를 말해준다.)
// arrfun4.cpp -- 배열 범위를 사용하는 함수
#include <iostream>
const int ArSize = 8;
int sum_arr(const int * begin, const int * end);
int main()
{
using namespace std;
int cookies[ArSize] = {1,2,4,8,16,32,64,128};
int sum = sum_arr(cookies, cookies + ArSize);
cout << "먹은 과자 수 합계: " << sum << endl;
sum = sum_arr(cookies, cookies + 3); // 처음 3개의 원소
cout << "처음 세 사람은 과자 " << sum << " 개를 먹었습니다.\n";
sum = sum_arr(cookies + 4, cookies + 8); // 마지막 4개의 원소
cout << "마지막 네 사람은 과자 " << sum << " 개를 먹었습니다.\n";
// cin.get();
return 0;
}
// 정수 배열의 합계를 리턴한다
int sum_arr(const int * begin, const int * end)
{
const int * pt;
int total = 0;
for (pt = begin; pt != end; pt++)
total = total + *pt;
return total;
}
포인터 와 const
const라는 키워드는 포인터에 두 가지 방법으로 사용된다.
- 상수 객체를 지시하는 포인터를 만드는 것이다.(상수 객체를 지시하는 포인터를 사용하여 그 포인터가 지시하는 값을 변경할 수 없다.)
- 포인터 자신을 상수로 만드는 것이다. (상수 포인터를 사용하여 그 포인터가 지시하는 장소를 변경할 수 없다.)
상수를 지시하는 pt 포인터 선언
int age = 39;
const int *pt = &age;
이 선언은 포인터 pt가 const int(이 경우에는 39)를 지시하고 있음을 말해 준다. 그러므로 pt를 사용하여 그 값을 변경할 수 없다. 다시 말해, *pt가 const이므로 변경할 수 없다.
*pt += 1; // pt는 const int를 지시하고 있으므로 사용할 수 없다
cin >> *pt // 마찬가지 이유로 사용할 수 없다
*pt = 20; // pt는 const int를 지시하고 있으므로 사용할 수 없다
age = 20; // age는 const로 선언되지 않았기 때문에 사용할 수 있다
// const 변수의 주소를 const를 지시하는 포인터에 대입하는 것
const float g_earth = 9.80;
const float *pe = &g_earth; // 사용가능
// const 변수의 주소를 일반 포인터에 대입하는것
const float g_moon = 1.63;
float *pm = &g_moon; // 사용할 수 없다
*** const가 아닌 포인터를 const 포인터에 대입하는 것은 한다리 건너는 간접 지시인 경우에만 사용할 수 있다.
int age = 39; // age++는 사용할 수 있다
int *pd = &age; // *pd = 41은 사용할 수 있다
const int *pt = pd; // *pt = 42는 사용할 수 없다
=> const가 아닌 주소나 포인터를 const 포인터에 대입할 수 있다는 규칙은 한다리 건너는 간접 지시자인 경우에만 유효하다
예를 들면, 그 포인터가 기본 데이터형을 지시하는 경우에만 유효하다.
=> 데이터형 자체가 포인터가 아니라면, const 데이터의 주소이든 const가 아닌 데이터의 주소이든 const를 지시하는 포인터에 모두 대입할 수 있다. 그러나 const가 아닌 데이터의 주소는 const가 아닌 포인터에만 대입할 수 있다.
함수와 구조체
// travel.cpp -- 함수와 구조체
#include <iostream>
struct travel_time
{
int hours;
int mins;
};
const int Mins_per_hr = 60;
travel_time sum(travel_time t1, travel_time t2);
void show_time(travel_time t);
int main()
{
using namespace std;
travel_time day1 = {5, 45}; // 5 시간, 45 분
travel_time day2 = {4, 55}; // 4 시간, 55 분
travel_time trip = sum(day1, day2);
cout << "이틀간 소요시간: ";
show_time(trip);
travel_time day3= {4, 32};
cout << "사흘간 소요시간: ";
show_time(sum(trip, day3));
// cin.get();
return 0;
}
travel_time sum(travel_time t1, travel_time t2)
{
travel_time total;
total.mins = (t1.mins + t2.mins) % Mins_per_hr;
total.hours = t1.hours + t2.hours +
(t1.mins + t2.mins) / Mins_per_hr;
return total;
}
void show_time(travel_time t)
{
using namespace std;
cout << t.hours << " 시간, "
<< t.mins << " 분\n";
}
함수를 지시하는 포인터
** 함수를 지시하는 포인터를 선언할 때에도, 그 포인터가 지시하는 함수의 데이터형을 지정해야 한다.
즉, 함수 원형이 그 함수에 대해 제공하는 것과 동일한 정보를 선언이 제공해야 한다.
ex)
double pam(int); // 함수원형
double (*pf)(int); // 함수를 지시하는 포인터 선언
=> pf는 하나의 int를 매개변수로 취하고 double형을 리턴하는 함수를 지시한다
일반적으로, 어떤 함수를 지시하는 포인터를 선언하는 절차는 다음과 같다. 함수의 원형을 먼저 작성한 다음, 함수 이름을
(*pf)형태의 표현식으로 대체한다. 이 경우에, pf는 그 데이터형의 함수를 지시하는 포인터이다.
*pf(int)는 pf( )가 포인터를 리턴하는 함수라는 것을 의미하지만, (*pf)(int)는 pf가 함수를 지시하는 포인터라는 것을 의미한다.
[함수 주소 얻기]
함수 think( )가 있다면 뒤에 붙는 괄호를 빼고 함수 이름만 사용하면 think가 think( )의 주소가 된다.
process(think); // process( )에 think( )의 주소를 전달한다.
=> process( )를 호출하면 process( )함수가 process( ) 내부에서 think( )함수를 불러낸다.
thougnt(think ( )); // thought( )에 think( )의 리턴값을 전달한다.
=> thought( )를 호출하면 think( )함수가 먼저 실행되고, think( )함수의 리턴값이 though( )함수에 전달된다.