공부 기록/C

20240422 C언어 - 포인터

bumm 2024. 4. 23. 09:14

포인터를 써서 변수의 값에 10을 더하는 경우

#include <stdio.h>  // 표준 입출력 관련 기능을 사용하기 위해 stdio.h 헤더 파일을 포함시킵니다.

// 'add_ten' 함수 선언: 이 함수는 정수형 포인터를 매개변수로 받아 그 값을 10 증가시킵니다.
void add_ten(int *pa);

int main(void)
{
    int a = 10;  // 'a'라는 이름의 정수 변수를 선언하고 10으로 초기화합니다.
    
    add_ten(&a);  // 'a'의 주소를 'add_ten' 함수에 전달합니다. 이로써 함수 내에서 'a'의 값을 변경할 수 있습니다.
    printf("a : %d\n", a);  // 'a'의 값을 출력합니다. 'add_ten' 함수에서 10이 추가되어 20이 출력됩니다.
    
    return 0;  // 프로그램이 성공적으로 종료되었음을 운영 체제에 알리기 위해 0을 반환합니다.
}

// 'add_ten' 함수의 정의
void add_ten(int *pa)
{
    *pa = *pa + 10;  // 포인터를 통해 접근한 변수의 값에 10을 더합니다. 즉, 메인 함수에서 전달된 'a'의 값이 20이 됩니다.
}

 

void에 대한 추가 설명

`void` 키워드는 C 언어에서 특별한 의미를 가지며, 여러 상황에서 사용됩니다. 함수 정의에서 `void`를 사용하는 경우는 두 가지 주요한 의미가 있습니다:

1. **함수의 반환 타입으로서의 `void`**:
   - 함수가 값을 반환하지 않을 때, 반환 타입으로 `void`를 사용합니다. 이는 함수가 호출 완료 후에 어떤 값도 반환하지 않음을 명시적으로 나타냅니다. 즉, 함수의 실행이 끝난 후에 특별히 받아야 할 결과값이 없을 때 사용됩니다. 예를 들어, `add_ten` 함수는 주어진 포인터를 통해 변수의 값을 변경하지만, 호출한 곳으로 추가적인 값을 반환하지는 않습니다.

2. **매개변수 목록에서의 `void`**:
   - 함수가 매개변수를 전혀 받지 않을 경우, 매개변수 목록에 `void`를 사용할 수 있습니다. 이것은 함수 정의에서 매개변수가 없음을 명확하게 표현하기 위해 사용됩니다. 예를 들어, `int main(void)`에서 `void`는 `main` 함수가 아무런 입력 매개변수를 받지 않음을 나타냅니다.

`add_ten` 함수의 경우, 반환 타입이 `void`인 것은 함수가 실행 후 어떠한 값도 반환하지 않으며, 주어진 매개변수(여기서는 포인터)를 통해 어떤 작업을 수행하고 끝난다는 것을 의미합니다. 이러한 함수들은 주로 입력된 데이터를 변경하거나, 어떤 작업을 수행한 후 결과를 반환하지 않고 종료되는 경우에 사용됩니다.

 

왜 여기선 값을 복사해서 전달하지 않고 주소를 전달한 걸까?

1) 값을 복사해서 전달하는 방식 : 원본 데이터 보존 가능, 안정성 높음

2) 주소 전달 방식 : 원본 데이터 변경 가눙 / 사용법 복잡, 변경하면 안되는 중요한 데이터를 다룰 때 사용하면 문제가 생길 수 있음

 

call by value(값에 의한 호출) // call by reference(참조에 의한 호출)

1) call by value : 함수를 호출할 때 변수의 값을 복사하여 인수로 주는 방식 => 

 

2) call by reference : 호출 함수의 변수를 피호출 함수에서 매개변수의 이름으로 직접 사용하는 방식 => 하나의 저장 공간을 서로 다른 함수에서 2개의 이름으로 쉽게 공유할 수 있음

ㄴ> 다만 C에서는 call by reference을 구현 가능한 문법 형식이 없음. 포인터를 사용해서 비슷한 효과를 낼 뿐.(하지만 이 방법 또한 결국 call by value임. 주소 값을 주고받으므로)

 

주소를 반환하여 두 정수의 합을 계산하는 코드

#include <stdio.h>  // 표준 입출력 관련 함수를 사용하기 위해 stdio.h 헤더 파일을 포함시킵니다.

// 'sum' 함수 선언: 이 함수는 두 정수의 합을 계산하고 결과의 주소를 반환합니다.
int *sum(int a, int b);

int main(void)
{
    int *resp;  // 정수형 포인터 'resp'를 선언합니다. 이 포인터는 나중에 'sum' 함수에서 반환된 주소를 저장할 것입니다.
    
    resp = sum(10, 20);  // 'sum' 함수를 호출하면서 10과 20을 인수로 전달합니다. 반환된 결과의 주소는 'resp'에 저장됩니다.
    printf("두 정수의 합 : %d\n", *resp);  // 'resp' 포인터가 가리키는 주소에 저장된 값을 출력합니다. 여기서는 30이 출력될 것입니다.
    
    return 0;  // 프로그램이 성공적으로 종료되었음을 운영 체제에 알리기 위해 0을 반환합니다.
}

int *sum(int a, int b)
{
    static int res;  // 정적 지역 변수 'res'를 선언합니다. 이 변수는 함수가 종료된 후에도 그 값이 유지됩니다.
    
    res = a + b;  // 입력받은 두 정수 a와 b의 합을 계산하여 'res'에 저장합니다.
    
    return &res;  // 'res' 변수의 주소를 반환합니다. 정적 변수이기 때문에 함수 외부에서도 안전하게 접근할 수 있습니다.
}

추가 설명

이 프로그램에서 주목할 점은 sum 함수 내에서 정적 지역 변수 res를 사용한다는 것입니다. 정적 지역 변수는 함수 호출이 종료되어도 메모리에 남아 있으므로, 이 변수의 주소를 반환하고 이 주소를 통해 값을 참조하는 것이 안전합니다.

비정적 지역 변수를 사용하면 함수가 종료될 때 해당 변수의 메모리 공간이 해제되므로, 반환된 주소를 통해 접근하는 것이 위험할 수 있습니다. 이 경우, 다른 데이터에 의해 해당 메모리 공간이 재사용될 수 있기 때문입니다.

이 방식은 함수가 계산한 결과를 직접 반환하는 대신 결과의 주소를 반환하여, 호출자가 그 주소를 통해 결과에 접근할 수 있도록 합니다. 이는 특히 큰 데이터 구조를 반환할 때 유용할 수 있습니다, 그러나 이 경우에는 단일 정수 값을 다루므로, 함수가 그 값을 직접 반환하는 것도 충분히 합리적인 선택이 될 수 있습니다.