18 - Array And Pointer 2
<학습하기>
배열의 변수들을 반복하는 방법.
1. 인덱스를 사용하는 방법.
int cityindex;
for(cityindex = 0; cityindex < 10; ++cityindex)
FunctionCall (&cityPrice [cityindex]);
2. 포인터를 사용하는 방법. - 포인터 연산을 이용하는 방식.
포인터연산
1) 포인터 ± 정수
포인터에 정수를 더하거나 뺀다.
단, 포인터는 해당 포인터 타입의 크기단위로 증가하거나 감소한다.
2) 포인터 - 포인터
두 포인터 사이의 거리를 구한다.
단, 두 포인터 사이의 거리 역시 해당 포인터타입의 크기단위로 나타낸다.
????부분에서 응용해보자.
-> 현재 포인터의 위치가 배열에서 몇번째 변수인지 알 수 있다.
struct City *cityPnt; //City 구조체의 포인터를 사용.
for (cityPnt = cityPrice; /*????*/ cityPnt - cityPrice < 10; ++cityPnt)
FunctionCall (cityPnt);
해석하기.
● cityPnt = cityPrice
cityPrice는 배열 이름이므로 배열이 시작되는 위치 (주소)
또는 cityPrice[0]의 위치 (주소)를 의미한다.
그러므로 cityPnt는 cityPrice[0]을 가리키고 있다.
● cityPnt - cityPrice < 10
배열의 시작부터 cityPnt가 얼마나 멀리 있는지 계산한다.
포인터의 특성에 의해 struct City의 크기단위로 계산되므로
결과적으로 cityPnt가 배열의 몇번째 변수 위치인지 알 수 있다.
● ++cityPnt
cityPnt에 1을 더하는 연산이지만,
포인터의 특성으로 포인터가 가리키는 타입의 크기만큼 증가한다.
(struct City의 크기)
const 키워드
상수형 변수를 만드는 키워드
변수이지만 값을 바꿀 수 없는 변수를 만들 때 사용한다.
변수가 만들어질때 단 한번 값을 대입할 수 있다.
ex)
int const constVar = 10;
//constVar이란 변수의 값은 10으로 고정.
printf("constVar : %d\n", constVar);
//constVar의 값을 참조는 가능.
constVar = 100;
//컴파일 에러.
//const 키워드로 만들어진 변수이므로 값을 바꿀 수 없다.
함수의 인수로 받을 경우 함수 안에서 값 수정 불가.
main()
{
FuncCall(10);
FuncCall(20);
}
void FuncCall (int const intVar);
{
//intVar는 처음에는 10, 두번째는 20으로 고정된다.
intVar = 0; //컴파일 에러.
}
포인터의 경우, const의 대상이 두가지이다.
int *intVar = 10; //이 intVar의 주소는 12345라고 가정하자.
●일반 포인터
int *intPnt = &intVar;
//intPnt에는 12345라는 값이 들어간다.
*intPnt = 10;
//intVar이란 변수를 찾아가서 intVar에 10을 대입하라.
intPnt = NULL;
//intPnt에 저장되어 있던 값을 지워라.
●포인터 자체의 const - 포인터에 저장된 12345라는 값을 절대 바꿀 수 없다.
int *const int Pnt = &intVar;
//intPnt에는 12345라는 값이 들어감.
*intPnt = 10;
//허용 - intVar이란 변수를 찾아가서 intVar에 10을 대입하라.
intPnt = NULL;
//불가 - intPnt에 저장된 12345라는 값을 지우려 하므로 컴파일 에러.
const 뒤쪽이 intPnt이므로 inPnt = NULL; 불가.
●포인터가 가리키는 주소에 대한 const - 포인터가 가리키는 곳 - intVar의 값은 절대 바뀔 수 없다.
int const *intPnt = &intVar;
//intPnt에는 12345라는 값이 들어감.
*intPnt = 10;
//불가 - intVar의 값을 바꾸려 하므로 컴파일 에러.
intPnt = NULL;
//허용 - intPnt자체의 값은 바꿀 수 있음.
const 뒤쪽이 *intPnt이므로 *intPnt = 10; 이 불가.
● 둘 다 불가
int const *const intPnt = &intVar;
//intPnt에는 12345라는 값이 들어감.
*intPnt = 10;
//불가 - intVar의 값을 바꾸려 하므로 컴파일 에러.
intPnt = NULL;
//불가 - intPnt의 저장된 12345라는 값을 지우려하므로 컴파일 에러.
const가 *앞 뒤에 모두 붙었으므로 둘 다 불가.
const 키워드를 사용하여 데이터 테이블 (내용이 바뀌지 않는 구조체 변수 또는 배열) 만들기.
int const *const intPnt = &intVar;
//intPnt에는 12345라는 값이 들어감.
*intPnt = 10;
//불가 - intVar의 값을 바꾸려 하므로 컴파일 에러.
intPnt = NULL;
//불가 - intPnt의 저장된 12345라는 값을 지우려하므로 컴파일 에러.
const가 *앞 뒤에 모두 붙었으므로 둘 다 불가.
const 키워드를 사용하여 데이터 테이블 (내용이 바뀌지 않는 구조체 변수 또는 배열) 만들기.
● 멤버 변수 const화
모든 멤버변수를 const로 만들어 초기화 이외의 값을 금지한다.
struct TestClass
{
int const age;
char const *const name
};
main()
{
TestClass err;
//초기화가 안되어 있으므로 컴파일 에러.
TestClass test = {15, "이름"};
//test라는 구조체 변수 생성
test.age = 10;
//에러
test.name = "name";
//에러
}
단, 모든 멤버변수들을 const화 시키는 것은
모든 구조체 변수들을 const화 시키는 것이나 마찬가지이므로
구조체 변수를 다른 목적으로 사용하기가 힘들어진다!
●구조체 자체를 const화
struct TestClass
{
int age;
char *name;
}
main()
{
struct Test const err;
//err라는 변수를 수정할 수 없는데, 초기화가 안되어 있으므로 사용 불가.
//그러나 컴파일은 통과.
//그러니 const를 쓸 떄는 항상 초기화를 해야한다는 것을 잊지말자!
struct Test const constTest = {20, "홍길동"};
//"홍길동"으로 초기화 된 구조체 변수 만들어짐.
//내부 수정 불가.
struct Test normal;
//자유롭게 사용 가능한 구조체 변수 만들어짐.
일반변수에 대한 const나, 포인터의 * 앞에 있는 const의 경우, const가 형식명 앞에 올 수 도 있다.
int const constVar = 10;
const int constVar = 10;
int const *constPnt;
const int *constPnt;
int *const constPnt;
//이 경우 const를 앞으로 뺄 수 없음.
<실습하기>
#include <stdio.h>
// 당신은 무역상으로서 컴퓨터와 카메라, 와인을 가지고 인천항에 도착했다.
// 10개 도시에 대해 다음과 같이 각 물품의 판매가가 정의되어 있다.
// 시간상 단 하나의 도시에서만 물품을 팔 수 있다.
// 각 재고량을 입력해서 어느 도시로 가는 것이 최대이익이 되는지 계산하라.
/*
//신입 프로그래머의 추가 ㅋㅋ..
//만약 프로그램이 끝났다고해서 아래와 같이 장난을 쳤다고 생각해보자.
city->cityName = "출력완료";
city->price.camera =0;
city->price.computer =0;
city->price.wine =0;
*/
//모든 변수를 const화 하면 신입박멸!
//신입이 농간치면 컴파일 에러가 뜨겠지 이제.
//근데 이렇게 하면 구조체를 다른목적으로 사용할 때 오류가 남.
//ex. stock사용 불가능.
struct Goods
{
short computer;
short camera;
short wine;
};
struct City
{
char *cityName;
struct Goods /*const*/ price; //city 안에 지정된 값은 바뀌어서는 안됨.
};
struct City /*const*/ cityPrice[] =
{
//각 도시의 물품 판매가이므로 바뀌어서는 안됨.
//근데 이렇게 하면 에러남.
//cityPnt = cityPrice에서 경고. (불허해야하는데 허용해줌..)
{"서울", 200, 66, 16},
{"인천", 208, 67, 19},
{"강릉", 254, 61, 15},
{"부산", 212, 66, 17},
{"전주", 225, 77, 16},
{"대구", 247, 71, 16},
{"경주", 238, 64, 17},
{"대전", 257, 63, 18},
{"광주", 235, 63, 15},
{"울산", 238, 77, 17}
};
void WritePriceInCity (struct City *city);
int BenefitInCity (struct City *cityPrice, struct Goods *stock);
main()
{
int cityIndex;
struct Goods stock;
//현재 내가 가지고 있는 물품량을 저장할 구조체.
//for 재고량 입력.
//뭐 int computer; int camera; int wine; 이렇게 정의해도 되지만 구조체가 더 편리!
struct City /*const*/ *cityPnt; //const를 사용한 에러를 수정하려면 매우 복잡..!
//cityPrice가 const형이므로 포인터도 const형이 되어야함.
//밑에서 저장한 포인터도 const형으로 바꿔줘야함.
//cityIndex 대신 배열의 변수를 반복하기 위한 포인터.
//for 이익 계산.
// 각 도시의 물건 값 출력
puts("각 도시에서의 판매가");
for(cityIndex = 0; cityIndex < 10; ++cityIndex)
WritePriceInCity (&cityPrice [cityIndex]);
//각 도시에서의 물건 값
//Struct City city >> Struct City *cityPnt로 바꾸었기 때문에 주소로 바꾸어서 전달.
//cityPrice >> &cityPrice
puts(""); //출력 후 한줄 올림.
//cityPnt를 응용한다면
//for (cityPnt = cityPrice; //cityPrice는 배열의 시작위치이며 &cityPrice[0]과 같다*/
/*cityPnt - cityPrice < 10; //cityPnt - cityPrice 는 두포인터 사이의
거리이며 struct City단위의 거리를 의미한다.*/
//++cityPnt //cityPnt를 1더하면 배열의 다음 변수로 이동.
//writePriceInCity(cityPnt);
// 재고량 입력
// computer, canmera, wine을 만들어야 하는데, struct Goods 안에 이미 이 변수들이 만들어져 있으므로
// 이 구조체 변수를 만들어 대신 사용할 수 있다.
printf("컴퓨터와 카메라, 와인의 재고를 입력해 주세요.");
scanf("%hd %hd %hd", &stock.computer, &stock.camera, &stock.wine);
// 그냥 stock.computer 하면 안됨. 주소를 전달해야지.
// stock안의 멤버변수들은 모두 short형으로 2바이트 변수들이다.
// 그러나 %d는 int형으로 4바이트에 저장한다..
// 즉 메모리 오버가 생긴다..
// 2바이트에 저장하라는 명령을 내려야 한다!!
// %d >> %hd (Half Digit) 로 수정한다.
puts("재고량");
printf("컴퓨터 : %d대\n",stock.computer);
printf("카메라 : %d대\n",stock.camera);
printf("와인 : %d병\n",stock.wine);
puts(""); //줄 바꾸기
// 각 도시마다 얼마의 이익을 얻을 수 있을 것인지 계산.
puts("각 도시에서의 이득");
//for (cityIndex = 0; cityIndex < 10; ++cityIndex)
//cityIndex 말고 한번 다르게 해볼까.
//배열의 변수들을 반복하는 방법? >> Note 확인
for (cityPnt = cityPrice; cityPnt - cityPrice < 10; ++cityPnt)
{
int benefit = BenefitInCity(cityPnt, &stock);
//cirtPnt의 도시에서 얼마의 이득을 얼을 수 있는가를 계산하는 함수.
//cityPnt의 도시에서 재고를 팔았을 떄의 이득을 계산하자.
//여기서도 그냥 cityPnt, stock하면 안된다.
//주소를 전달해야하니까 cityPnt, stock >> cityPnt, &stock
printf("%s : %d만원\n",cityPnt ->cityName, benefit);
}
puts("");
// 최고 이득을 올릴 도시를 찾아라.
/*
첫째 도시(cityPrice[0])를 최고이득을 올릴 도시로 가정.
둘째 도시(cityPrice[1])부터 반복.
최고 이득 도시보다 큰 도시가 나오면 그 도시를 최고이득을 올릴 도시로 가정.
*/
{//위에다가 변수하기 귀찮으니까 블럭을 새로 생성하여 변수를 만들자.
struct City *maxProfitCity; //최고 이득을 올릴 도시로 가정할 포인터.
int maxProfit; //maxProfitCity에서 올릴 수 있는 수익.
//첫째 도시 (cityPrice[0])를 최고이득을 올릴 도시로 가정.
maxProfitCity = &cityPrice[0];
maxProfit = BenefitInCity(maxProfitCity, &stock);
//maxProfitCity에서 재고를 팔아 얻을 수 있는 수익.
//둘째 도시(cityPrice[1])부터 반복.
for(cityPnt = cityPrice + 1; cityPnt - cityPrice < 10; ++cityPnt)
//왜 cityPrice +1인가?
//첫째 도시는 가정으로 사용했기 때문에
//둘째도시 (cityPrice[1]) 부터 반복하려면 +1을 해야함.
{
//cityPnt에서의 이득 계산
int profit = BenefitInCity(cityPnt, &stock);
//최고 이득 도시보다 큰 도시가 나오면 그 도시를 최고이득을 올릴 도시로 가정.
//if (profit > BenefitInCity(maxProfitCity, &stock))
if (profit > maxProfit) //maxProfitCity에서의 이득은 이미 maxProfit에 들어가 있음.
{
maxProfitCity = cityPnt; //cityPnt가 가장 높은 이윤을 얻을 가능성
//maxProfitCity가 바뀌었으므로 maxProfit역시 바뀌어야 함.
maxProfit = profit;
//profit은 cityPnt에서의 이득이 이미 계산되어 있으므로 maxProfit에 대입한다.
}
}
printf("최대 이윤 도시 : %s에서 %d만원\n",maxProfitCity ->cityName ,maxProfit);
}
}
void WritePriceInCity (struct City const /*신입 기각!*/ *city)
// struct City city 형태로 받는다면 호출하는 쪽의 구조체를 그대로 복사하게 된다.
// 만약 struct City가 매우 크다면 구조체를 복사하는데 컴퓨터가 느려질 가능성이 있다.
// 그러므로 대부분의 경우 구조체 전체를 전달하는 것보다 주소를 전달하는 것이 좋다.
// struct City city >> struct City *cityPnt
{
printf("이름 : %s 컴퓨터 : %d만원 카메라 : %d만원 와인 : %d만원\n", city->cityName,
city->price.computer, city->price.camera, city->price.wine);
/*
//여기서 잠깐!
//신입 프로그래머의 추가 ㅋㅋ..
//만약 프로그램이 끝났다고해서 아래와 같이 장난을 쳤다고 생각해보자.
city->cityName = "출력완료";
city->price.camera =0;
city->price.computer =0;
city->price.wine =0;
*/
//신입이 이렇게 하면 망함.
//이렇게 행동안하도록 지시하는것. >> const키워드 >> Note확인
//이제 위에 const를 해 놓았으므로 신입이 농간을 치면 컴파일 에러 ㅎ
/*
city를 인수로 받을 때, const *city형태로 받음.
이것은 *city가 고정된다는 의미이므로
city가 가진 주소의 구조체를 훼손하지 못한다.
*/
}
int BenefitInCity (struct City *cityPrice, struct Goods *stock)
{
return
cityPrice ->price.computer * stock ->computer +
cityPrice ->price.camera * stock ->camera +
cityPrice ->price.wine * stock ->wine;
/*
//신입프로그래머의 추가
cityPrice[0].price.computer =0;
cityPrice[0].price.camera =0;
cityPrice[0].price.wine =0;
*/
//이렇게 하면 망함.
//이렇게 행동안하도록 지시하는것. >> const키워드 >> Note확인
}
댓글
댓글 쓰기