C, C++

[C언어] C언어 기초 정리 #4 (함수와 포인터)

명도환 2025. 3. 18. 12:14

목차 ]

1. C프로그램 구성

2. 데이터형 / 변수 / 상수

3. 입출력

4. 연산자

5. 제어문

6. 1차원 배열

7. 다차원 배열

8. 함수

9. 포인터의 이해

10. 문자열

11. 메모리 동적 할당

12. 구조체


8. 함수

 

 8.1. 함수

  main함수 안에 모든 프로그램을 작성하면 전체 프로그램은 복잡해지고 이해하기가 어려워진다. 따라서 기능별로 분리된 단위 프로그램으로 나누어 정의함으로써 코드의 재사용이나 유지, 보수가 용이하게 된다. 또한 중복되는 작업을 함수로 정의하면 중복을 피할 수 있게 된다.

 

 8.2. 사용자 정의 함수

  필요에 따라 함수를 정의해서 쓸 수 있는 함수를 사용자 정의 함수라 한다.

 

 8.2.1. 함수의 정의

  ex)

리턴타입 함수명(데이터형 변수1, 데이터형 변수2, ... , 데이터형 변수n)
{
return 값;
}

 

 

- 리턴타입 : int, double, char, void, 등 리턴 시 돌려주는 값의 데이터형을 의미. 리턴 시 돌려주는 값이 없을 경우는 void를 쓰는데 리턴타입이 void인 경우 return은 생략 가능하다.

- 함수명 : 가급적이면 의미 있는 이름으로 정하는 것이 좋으며, 변수명의 규칙과 같다.

- 함수의 괄호 ( () ) : 호출부에서 전달한 값을 받을 변수를 선언한다. 전달하는 개수와 데이터형은 일치해야하며, 없으면 생략 가능하다.

- return : 호출한 곳으로 결과 값을 돌려주며, 함수를 강제 종료 시킨다. return 명령을 만나면 함수의 나머지 내용을 무시하고 함수를 호출한 곳으로 돌아간다.

 

 8.2.2. 함수의 선언

  함수의 선언은 컴파일 시 컴파일러에게 함수의 정보를 알려주는 역할을 하며, 이 정보를 이용하여 컴파일러는 함수사용에 관련된 오류를 검사하게 된다. 함수 선언은 함수호출 이전에 하며, 함수 정의 Header부분에 세미콜론(;)을 추가하면 된다. 함수정의부를 함수 호출 이전에 두면, 함수 선언을 생략할 수 있다.

 

 8.2.3. 함수의 호출

  함수는 함수명으로 호출한다. 이때 함수로 전달하려는 값이 있는 경우 함수의 괄호( () )안에 값을 전달하면 된다. 함수를 호출하면 제어 권은 호출한 함수로 넘어가고 함수의 실행이 완료되면 다시 호출한 곳으로 돌아와 다음 구문을 수행하게 된다. 함수선언을 하게되면 함수들 간의 순서에 영향을 미치지 않는다.

 

 

※ 함수의 호출 과정

1. 인수를 전달하기 위해 메모리에 저장한다.
2. 호출하는 함수의 다음 명령의 주소를 저장해서 리턴 할 수 있게 한다.
3. 함수가 정의되어 있는 주소로 이동한다.
4. 함수의 지역변수 메모리에 할당한다.
5. 함수를 수행한다.
6. 리턴 값을 저장한다.
7. 저장한 주소로 리턴 한다.

 

 

 

 8.3.1. 지역변수와 전역변수

  - 지역 변수 :

             1) 함수 내에서 선언된 변수로 선언된 함수 내에서만 사용 할 수 있다.

             2) 함수가 종료되면 메모리에서 소멸 된다.

             3) 매개변수(인수)도 지역변수에 포함된다.

             4) 초기화하지 않으면 쓰레기 값을 갖는다.

  - 전역 변수 :

             1) 함수 외부에서 선언된 변수로 모든 함수에서 사용할 수 있다.

             2) 프로그램이 실행될 때 메모리가 한번 할당되며 프로그램이 종료되면 소멸한다.

             3) 초기화하지 않으면 0으로 자동 초기화 된다.

 

 8.3.2. 정적 변수

  static을 붙여 선언된 변수를 말한다.

             1) 선언된 함수 안에서만 사용할 수 있다. (지역변수 특징)

             2) 프로그램 실행 시 메모리가 한번 할당되며 프로그램 종료시 소멸. (전역변수 특징)

 

 


 

9. 포인터의 이해

 

 9.1. 포인터

  포인터란 메모리의 주소를 저장하기 위한 변수를 말한다. 포인터가 변수기 때문에 포인터 변수라는 표현을 많이 사용한다.

 

 9.1.1. &연산자

  & 연산자를 이용하면 변수의 주소를 구할 수 있다.

 

 9.1.2. 포인터 변수의 사용

  포인터를 선언할 때는 * 연산자를 사용한다. 이때 포인터도 데이터형이 존재한다. 포인터의 데이터형은 주소를 저장하는 변수의 데이터형과 일치 돼야 한다. 포인터는 데이터형에 상관없이 모두 4byte다.

ex)

1) 데이터형 * 포인터명;

 - 기능 : 데이터형이 일치하는 변수의 주소를 저장하는 포인터 변수 선언

 - 사용 예 : int *ip; // int형의 주소를 저장할 수 있는 포인터 ip를 선언

2) 포인터명 = &변수;

 - 기능 : 변수의 주소를 포인터변수에 저장한다.

 - 사용 예 : ip = &i; // i의 주소를 포인터 변수 ip에 저장한다.

** 포인터가 변수의 주소를 저장하면 “포인터는 변수를 가리킨다.”

 

 9.1.3. * 연산자를 이용한 간접 접근

  포인터 변수 앞에 * 연산자를 쓰면 포인터가 가리키는 곳의 값을 접근할 수 있다. 포인터를 이용한 메모리 접근 방식을 간접접근(Indirect Access)이라 하는데 간접접근을 위해 포인터에 데이터 형이 존재해야 한다.

※ * 연산자의 쓰임

연산자 용   도 형   식 사용 예
* 곱하기 변수 또는 상수 a * b;
x * 3;
포인터
주소를 저장하는 변수
데이터형 *변수명 int *ip;
double *dp;
간접 접근
포인터가 가리키는 값
*포인터명 *ip;
*dp;

 

 

 

 9.2. 배열과 포인터

- 배열이름도 포인터다.

배열이름은 배열의 시작주소를 가리키는 포인터다. 즉, 배열이름은 배열의 첫 번째 주소를 말한다.

 

 9.3. 포인터 연산

- 포인터는 연산 시 가리키는 데이터형의 크기만큼 증가 및 감소한다.

 

 9.3.1. 배열의 시작주소를 저장한 포인터의 연산

- 포인터는 가리키는 데이터형의 크기만큼 증/감되기 때문에 배열의 시작주소를 저장한 포인터는 포인터 연산으로 배열에 접근할 수 있다.

 

 9.4. 배열이름의 연산

  배열이름도 포인터기 때문에 배열이름을 1씩 증가하면 배열의 증가되는 주소를 알 수 있다.

배열의 이름은 배열의 시작주소기 때문에 1씩 증가해서 다음 자료 주소에 접근 할 수 있으며, 그 주소를 간접접근하면 포인터가 가리키는 값에 접근할 수 있기 때문이다.

 

* C언어에서는 포인터가 가리키는 값에 접근(간접접근)시 * 연산자를 이용한다. 그러나 []연산자를 이용하는 것도 가능하다. 간접접근 시 * 연산자와 [] 연산자의 쓰임은 같다.

ex)        int[3] = {1, 2, 3};

             int *p = a;

변수 접근 방법 p[0] *p 또는 *(p+0) *a 또는 *(a+0) a[0] 1 &a[0] a 또는 a+0 p 또는 p+0 주소 접근 방법
p[1] *(p+1) *(a+1) a[1] 2 &a[1] a+1 p+1
p[2] *(p+2) *(a+2) a[2] 3 &a[2] a+2 p+2

 

 

 

 9.4.1. 배열이름과 포인터의 차이점

  배열이름은 상수 포인터기 때문에 배열의 시작주소 변경은 불가능 하다.

 

 9.5. 포인터와 함수의 데이터전달 방법

  데이터전달 방법에는 값에 의한 전달(call by value)과 주소에 의한 전달(call by address)이 있다

 

 9.5.1. 값에 의한 전달(call by value)

  전달한 변수의 복사본이 생성되어 복사본이 변경돼도 전달 변수에는 아무런 영향을 미치지 않는 방식이다.

 

 9.5.2. 주소에 의한 전달(call by address)

  전달한 변수의 주소를 포인터에 저장하는 방식으로 포인터는 간접접근으로 전달 변수를 변경할 수 있는 방식이다.

* 포인터는 여러 개의 값을 동시에 변경하는 경우 유용하게 사용된다.

 

 9.6. 포인터 배열

  포인터 배열이란 이름 그대로 포인터들의 배열을 말하는 것으로 같은 타입의 포인터가 여러 개 필요한 경우 포인터를 배열로 선언한다.

  ex) char *ch1, *ch2, *ch3; -> char *ch[3];

 

 9.7. 더블 포인터(double pointer)

  싱글포인터(포인터)는 변수의 주소를 저장하기 위한 포인터라면 더블 포인터는 싱글포인터의 주소를 저장하는 포인터이다.

  ex) 데이터형 **포인터명;

             - 포인터의 주소를 저장하기 위한 포인터다.

             - 사용 예 : int **p;

 

 9.8.1 2차원 배열과 포인터

  1. 배열이름은 배열의 시작주소를 가리키는 포인터다.

  2. 배열이름[행]은 행의 시작주소다.

  3. 배열이름 증 / 감 연산 시 행 단위로 이동한다.

 

 9.8.2. 2차원 배열이름의 포인터 타입

  1차원 배열이름은 배열이름 연산 시 증 / 감 되는 바이트가 항상 같기 때문에 데이터형이 같은 경우 포인터 타입도 일치한다. 2차원 배열이름은 포인터 연산 시 증 / 감 되는 바이트가 일치하지 않기 때문에 포인터 타입이 달라진다.