본문 바로가기

IT 지식/c 프로그래밍 언어

형변환, 포인터, 함수에 대해 알아보자

728x90
반응형

목표

형변환, 포인터, 함수에 대해 알아보자

학습할 것

  • 형변환 (타입 캐스팅)
  • 포인터
  • 함수

1. 형변환

연산을 할때 무심결에 소수점과 정수를 같이 계산할 때가 있다. 우리 인간은 그런게 자동으로 되지만, 정수와 소수점의 개념을 나누어 공간을 다르게 사용하는 컴퓨터 입장에서는 공간활용이라는 측면에서 그 자료형을 통일해서 계산해야 된다. 

예를 들어, 정수 7 + 4.3 = 을 계산한다고 하자. 정수 7은 int로 4바이트를 차지하는 공간에 저장되어 있고, 4.3은 double형으로 8바이트를 차지하는 공간에 있다. 이 둘을 같게 하기 위해선 두가지 방법이 존재한다.

첫째는 int 형 7을 double 형 공간으로 올려주는 올림변환이 있다. 

둘째는 double 형 4.3을 int 형 공간으로 내려주는 내림변환이 있다.

첫째는 생각해봐도 넣을 수 있는 공간이 더 널널해지기에 아무 오류가 나지 않는다. 즉, 정보의 손실 없이 그냥 형변환(공간만 넓어지는 것)만 되는 것이다. 그래서 이 작업은 컴파일에서 자동으로 해주는 묵시적형변환을 해준다.

그런데 두번째는 원래 큰 몸집을 가진 정보가 자신의 일부를 덜어내고 강제로 작은 공간에 넣어야 되는 문제가 발생한다. 즉, 정보의 손실이 발생한다. 4.3에서 정수로 만들려면 int 형 4로 저장될 수 밖에 없으니깐 말이다. 이때는 우리가 형변환연산자라는 것을 써서 강제로 내림변환 시켜줘야 한다. 이때 물론 정보손실이 있음을 경고해주는 메세지가 뜬다.

 

형변환 연산자.

(type) 피연산자로 쓴다. 예를 들어 4.3을 int 형으로 변환 시켜주고 싶으면 (int) 4.3 을 하면 4로 바뀐다. 이 방식을 명시적 형변환이라고 한다. 단항연산자인 형변환 연산자는 모든 이항연산자보다 먼저 계산한다. 
예를 들어, (double)7 / 2 는 형변환 (double) 7 -> 7.0을 한 후에 7.0/2를 수행한다. 따라서 결과는 double 형 3.5 이다.

 

2. 포인터

데이터가 들어있는 메모리 공간에는 8비트인 1바이트마다 고유한 주소값이 있다. 즉, 주소값은 0부터 1바이트마다 1씩 증가한다. 

이 주소를 다루는 것이 포인터의 개념인데 도대체 왜 주소를 알아야 하나?????

그 이유는 다음 비유로 설명해볼 수 있다. 어떤 정보를 학생 A 가 알고 있는데 그 소문을 듣고 학생 b,c,d 가 찾아왔다. 그래서 그 정보를 알려줬는데 그 다음에 잘되서 학생 200명이 각각 따로따로 찾아와 학생 A가 일일이 다 전달해줘야 되는 상황이 왔다. 이럴때 그냥 학생 A는 그 정보가 들어있는 사이트를 알려주었다. 

이것이 포인터를 쓰는 이유이다.

프로그래밍에서는 포인터가 매개변수와 배열을 쓸때 유용하게 쓰인다.

 

 

1. 주소연산자 & (ampersand)

어떤 변수 앞에 주소연산자 &를 붙이면 그 변수의 주소를 가르키는 의미가 된다.

예를 들어 int a = 7; 

             printf("%p", &a);

 

의 코드에서 a의 데이터인 7을 출력하는 것이 아니라 a의 주소를 출력한다. 여기서 포인터를 출력할때 형식제어문자 %p를 사용한다.

 

2. 포인터변수

주소록에 친구들의 집주소만을 기록하는 것처럼, 변수들의 주소만을 저장하는 포인터 변수가 있다. 그냥 변수에 저장하면 그것이 일반 숫자로 저장되기 때문에 주소인지 데이터인지 모르기 때문에 반드시 포인터 변수에 저장해놔야 한다. 여기서 강조할 것은 포인터 변수 또한 변수라는 것이다. 어떤 메모리 공간을 차지한다. 즉, 포인터 변수도 주소가 있다. 

 

선언방법을 살펴보자.

(자료형) *변수이름 의 형태로 선언한다. *은 asterisk라고 부른다. 하나의 단항연산자이다.

저렇게 선언된 변수는 모두 포인터 변수이며 어떤 주소를 담고 있다. 간단히 포인터라고 부른다.

 

예를 들어 int *print는 int 포인터 print라고 부른다.

 

여기서 자료형은 주소가 가르키는 공간에 담긴 데이터의 자료형과 같아야 한다. 즉 포인터의 자료형이 아니라 포인터가 가르키고 있는 주소 안에 들어있는 데이터의 자료형을 가르킨다.

예를 들어, int a = 7; 

              int *print;

              print = &a;

 

             double b = 4.3;

             double *ppt;

             ppt = &b;

 

포인터 변수는 가르키는 변수의 종류에 관계없이 크기가 모두 4바이트이다.

 

3. 간접 연산자 * (indirection operator, dereferencing, redirection)

포인터 변수는 주소값을 데이터로 갖지만, 그 주소값에 담긴 데이터를 읽어올 수도 있다. 그렇게 간접적으로 한단계 더 걸쳐서 주소의 원래 데이터를 읽어내는 방법이 간접연산장 * 이다. 위의 포인터 연산자인 * asterisk와 혼동하지 말자.

예를 들어

*print 는 말그대로 변수 data를 의미한다.

print 는 data의 주소를 의미한다. 

 

 

 

 

3. 함수

++함수의 기본원리++

입력값을 받아서 출력값을 내는 것이 함수의 기본 원리이다. 그러니깐 그 과정이 어떻게 되든 입력값과 출력값이 반드시 있어야 한다. 

 

전체적으로 프로그래밍은 모두 함수이다. 우리가 맨처음부터 쓰고 있었던 int main() 역시 가장 큰 틀의 함수이다. 그 안에 또 함수를 쓰고 써서 원하는 동작을 만드는 것이 프로그래밍 그 자체이다. 함수는 우리가 직접 정의해 사용할 수 있는 사용자 정의 함수 와 이미 만들어져서 우리가 편리하게 쓸 수 있는 여러 필요한 기능이 있는 라이브러리 함수가 있다. 

 

1. 함수를 쓴다는 것

 

함수를 배우고 쓴다는 것은 앞으로 프로그래밍을 좀 더 구조적으로 짤 수 있다는 의미이다. 구조적을 짠다는 의미는 크게 두가지이다.

첫째 주어진 문제를 여러 작은 문제로 쪼개서 풀 수 있다는 뜻이다. -(이걸 절차적 프로그래밍이라고 한다.) 함수 하나당 하나의 작은 문제를 풀어가는 것으로 생각하면 된다.

둘째, 함수는 함수의 인자값이 변해도 그 구조는 변하지 않아서, 반복되는 행위를 매번 짜는 중복을 피할 수 있다. 한번 정의된 함수는 여러번 호출이 가능하다.  

 

2. 함수정의

함수는 함수헤더와 함수몸체로 구성되어 있다.

함수 헤더에는 함수의 반환형, 함수이름, 그리고 매개변수 목록이 존재한다. 함수는 결국 출력값을 받아서 나오는데 그 출력값의 자료형을 반환형이라고 한다. 그리고 매개변수는 함수에 집어넣는 입력값이다. (위에 그림 참조) 함수 안에서 이 매개변수의 데이터값을 이용해 함수를 작동시켜 출력값을 내겠다는 소리이다. 

함수 몸체는 함수 내에서 수행해야할 문장들을 써놓은 것이다. 함수 몸체는 중괄호로{ 시작해 중괄호로} 끝난다. 

 

3. 반환형과 return

함수 몸체 마지막에 대부분 return 을 써서 데이터 값을 함수로 보낸다. 이 데이터의 자료형에 따라 우리가 아는 자료형을 모두 쓸 수 있다. 그런데 딱히 보낼 최종 데이터값이 없으면( 예를 들어 그냥 함수가 어떤 문장을 출력하는 경우) 반환형은 void 라고 쓰면 된다. 반환형이 int 정수형인 경우에는 생략하여 함수이름만 쓸수도 있지만 권장사항은 아니다.

 

4. 함수선언과 함수호출

함수선언이라는 것은 앞으로 이 함수를 내가 쓰겠다라고 미리 컴퓨터에게 알려주는 행위이다. 우리가 변수를 쓰기전에 변수를 선언해서 그 변수에 메모리 공간을 컴퓨터가 미리 할당해 놓고 데이터값을 받을 준비를 하고 있는 것처럼 말이다. 그런데 함수정의에서 그 함수를 미리 알려준 것이 아닌가?? 함수정의를 통해 알려줬는데 왜 다시 함수를 선언해야 할까? 꼭 함수 선언을 해야지만 그 함수를 작동할 수 있는 것은 아니다. 함수 정의를 main 함수 위에 명시해준다면 굳이 함수를 선언해주지 않아도 main에서 쓸수 있다. 

 

예를 들어 보자.

 

ㅏㅁ

 

처음에 이런 결과를 만들려고 할때 함수를 배우지 않았을 때 우리는 저렇게 printf 구문으로 일일이 다 손으로 쳐서 만들었다. 그런데 현업에서는 작업에서 많은 수정이 필요하다. 디자이너 분께서 갑자기 위에 별은 50개 넣어주고 그 밑에 이름들은 40개로 만들어주세요. 라고 요청이 들어왔다고 해보자. 우리는 그 별들과 이름을 일일이 다 쳐 넣고 있어야 될까?? 함수는 이런 귀찮은 반복 작업을 줄여준다. 

 

위의 코드에서 별의 개수를 입력받아 그 수 만큼 나타내는 함수코드를 만들어보자.

함수 print_stars를 만들어 원하는 갯수만큼 별이 찍히도록 기능을 좀 더 일반화시켰다.

 

그런데도 굳이 선언을 해야 될때는 함수의 정의를 main 함수 뒤에 놔둘때 이다. 그렇게 하면 더 깔끔한 코딩을 구현할 수 있다. 

예를들면, 

위에서 함수 선언하고 밑에서 함수 정의를 내린다.

 

함수를 이제 쓸려면 함수를 호출하면 된다. 함수이름과 함께 괄호 안에 적절한 매개변수를 집어넣으면 된다. 

 

*** 링킹오류와 컴파일 오류 이해하기, 객체지향 이해의 실마리***
만약 함수원형만을 선언하고 밑에 함수의 정의가 없다면 어떻게 될까??

이때 컴파일은 된다. 컴파일은 문제가 없다고 뜬다. 그런데 링크에서 오류가 난다. 즉, 함수 원형자체로 존재하여도 컴파일 문법상 아무 지장없이 기계어로 변환이 된다는 소리이다. 나머지 정의가 없는 것은 링크를 통해 짜집기 해서 발생되는 논리적인 오류라는 것이다. 

 

 

5. 함수의 매개변수(parameter) 와 인자 (argument)

 함수 정의에서 기술되는 매개변수 목록의 변수를 형식매개변수라고 한다.(formal parameters)

 즉 좀 더 일반화된 기술문장을 말한다. 

 

출처:https://amagrammer91.tistory.com/9

인자는 함수호출시에 함수의 괄호안에 실제 넣는 데이터값이다. 즉, 매개변수에 직접적인 데이터를 집어넣은 것이 인자(argument)라고 한다. 이것을 실매개변수 (actual parameters) 또는 실인자 또는 인자라고 한다.

 

6. 지역변수 (local variable)

 지역변수는 함수 안에서 선언되고 작동되는 변수이다. 더 세부적으로는 중괄호 안에서 {} 선언되고 그 밖을 벗어나면 다시 그 밖에서 선언된 변수로 대체되면서 사라지는 변수이다. 변수의 작동 범위가 중괄호{} 안쪽이라는 소리이다. 반면에 중괄호 밖에서 선언된 변수는 지역변수와 반대로 중괄호 밖에서는 모두 그 변수의 작동범위 하에 있다고 말할 수 있다. 다음 예제를 통해 알아보자.

첫번째 a 선언에서 함수 min 에 1과 2를 인자로 대입했다. 

그러고 나서 변수 a의 메모리 주소를 출력해봤다.

그 밑에서는 중괄호 안에다가 a를 새로 선언하고 함수 min 에 다른 인자를 넣어 새롭게 데이터를 변형시켰다.

그렇게 되면 원래 있던 첫번째 변수 a의 값이 바뀐 것일까??

그렇지 않다.

중괄호 안의 변수 a의 주소를 출력해보면 그 바깥의 변수 a와는 다른 주소에 들어가 있음을 볼 수 있다. 즉, 다른 공간을 할당받은 서로다른 주소이다. 그러나 중괄호 안에서의 변수 a 는 중괄호가 끝나면 효력이 끝나고 사라지기 때문에 그 밖의 a와 중복되지 않는다.

 

 

+ 더 궁금해서 알아본 것

 

- 컴퓨터는 -1을 어떻게 인지할까?( 어떻게 비트로 표현할까?)

  A: 결론부터 말하자면 -1은 (1의보수)+1이다. 1의 보수라는 것을 이용한다. 어떤 수 a의 보수라는 것은 a의 이진수 모든 비트에서 0은 1로 1은 0으로 바꾼 수이다. 예를 들어, 1을 short로 지정한 변수(2바이트(16bit))에 이진수로 표현하면 00000000 00000001이 된다. 여기서 0과 1을 모두 뒤집은 것이 1의 보수이다. 즉, 11111111 11111110 이 된다. 여기서 +1을 더하면 11111111 11111111이 되는데 이것이 -1이 된다.

참고로 부호가 있는 signed short 자료형이라고 했기 때문에 맨 앞 최상위 비트의 0과 1은 부호를 의미한다. 

그렇다면 -10은 어떻게 표현할 수 있을까?

먼저 10의 이진수 표현을 나타내면 00000000 00001010 이다. 10의 보수는 11111111 11110101이다. 여기에 1을 더한 값인 11111111 11110110이 -10이 된다. 

Q. 공식은 알겠는데 왜 보수를 취하고 왜 거기에다가 +1을 더할까???

 

 

728x90
반응형

'IT 지식 > c 프로그래밍 언어' 카테고리의 다른 글

동적할당이란 무엇인가?  (0) 2021.10.10
구조체란 무엇인가  (0) 2021.10.08
C 언어란 무엇인가  (1) 2021.10.03