C# - 함수 (Function)

2025. 9. 10. 23:50·C#

함수 (Function)

함수라는 걸 처음 배우는 건 중학교때 인가?

y = nx + a 뭐 이런거 본 적이 있을 거다.

그래 그게 함수 맞다.

 

x는 입력값이고 내부에 구현된 로직을 통해 계산된 결과물이 y라는 출력값이다.

프로그래밍에서는 이것을

입력(Input) : 매개변수(Parameter)

출력(Output) : return으로 돌려주는 반환값

이라고 말할 수 있겠다.

 

function의 사전 의미는 기능 이다.

입력에 따라 어떠한 출력을 내는 기능이라고도 볼 수 있겠다.

 

프로그램에서 함수는 기본이자 전부라고 해도 과언이 아니다.

그렇기 때문에 반드시 제대로 배우고 넘어가야 한다.

프로그래밍을 할 생각이라면 죽을때까지 절대로 함께 가야 할 친구이기 때문이다.

 

그러므로 이번 글에서는  함수에 대해 정리해보고자 한다.


함수의 기본 형태

[한정자] [반환형] [함수명] (매개변수들) { 본문 }

이 기본 형태를 지켜, 마름모의 넓이를 구하는 함수를 작성해보자

//   한정자    반환형     함수명        파라미터
public static double CalcDiamond(double d1, double d2)
{
    return d1 * d2 / 2;
}

public static 은 한정자

double 은 반환형

CalcDiamond 는 함수명

괄호 안의 double d1, double d2 는 매개변수(파라미터)

그리고 return d1 * d2 / 2 이 본문에 해당한다

 

프로그래밍을 처음 접하면 반환형? 반환? 리턴? 여기서 막히는 사람들이 많다.

반환한다, 리턴한다, 뱉는다 등등 여러가지로 표현하는데 의외로 이걸 이해하지 못하는 사람이 많은지

교육기관에서 질문도 많으며, 유튜브 영상으로도 많이 보인다.

 

아래 예시들을 통해 파라미터와 리턴에 대해 이해해보자.

 

우선 위에서 했던대 마름모 면적을 구하는 코드를 메인에 작성하면 아래와 같다

using System;

namespace CsharpStudy
{
    internal class Program
    {
        static void Main()
        {
            // 유저에게 입력받을 정보 : 높이 너비
            // 계산하고자 하는 것 : 마름모의 면적

            Console.WriteLine("높이를 입력하라");
            double d1 = 0;
            double.TryParse(Console.ReadLine(), out d1);

            Console.WriteLine("너비를 입력하라");
            double d2 = 0;
            double.TryParse(Console.ReadLine(), out d2);

            double result = d1 * d2 / 2;

            Console.WriteLine($"높이 {d1}, 너비 {d2}의 마름모의 넓이는 {result}");
        }
    }
}

 

위 코드를 파라미터와 리턴값이 있는 함수로 만들어 보자

using System;

namespace CsharpStudy
{
    internal class Program
    {
        static void Main()
        {
            // 유저에게 입력받을 정보 : 높이 너비
            // 계산하고자 하는 것 : 마름모의 면적

            Console.WriteLine("높이를 입력하라");
            double d1 = 0;
            double.TryParse(Console.ReadLine(), out d1);

            Console.WriteLine("너비를 입력하라");
            double d2 = 0;
            double.TryParse(Console.ReadLine(), out d2);

            double result = CalcDiamond(d1, d2);

            Console.WriteLine($"높이 {d1}, 너비 {d2}의 마름모의 넓이는 {result}");
        }
		
        static double CalcDiamond( double d1, double d2 )
        {
            return d1 * d2 / 2;
        }
    }
}

 

CalcDiamond라는 함수에 d1(높이) 와 d2(너비)를 입력하고,

CalcDiamond는 이 계산 결과를 반환(return) 한다

그러면 return 된 함수가 있던 자리,

즉 double result  =  (이곳) 에 결과값이 들어가서 result 라는 변수에 마름모의 너비 값이 들어가게 된다.

 

함수에 매개변수와 반환값이 항상 존재해야하는 건 아니다.

아래와 같이 반환값이 없는 함수도 만들 수 있다

using System;

namespace CsharpStudy
{
    internal class Program
    {
        static void Main()
        {
            PrintJoke();
            Console.WriteLine();
            Console.WriteLine("농담을 평가해주세요");
            string jokeInput = Console.ReadLine();
            GradeMyJoke(jokeInput);
        }

        static void PrintJoke()
        {
            Console.WriteLine("나무를 가장 미워하는 사람은?");
            Console.WriteLine("트린다미어");
        }

        static void GradeMyJoke(string input)
        {
            Console.WriteLine($"Your joke is {input}");
        }
    }
}

// 출력 값
// 나무를 가장 미워하는 사람은?
// 트린다미어
// 농담을 평가해주세요
// (ReadLine에 shit 입력)
// Your joke is shit

 

이렇게 리턴도 매개변수도 없는 경우,

매개변수만 있는 경우의 함수도 만들 수 있다

물론 매개변수가 없고 리턴만 있는 함수도 만들 수 있다.

using System;

namespace CsharpStudy
{
    internal class Program
    {
        static void Main()
        {
            double returnedPiValue = GetPI(); // 여기 returnedPiValue에 파이값이 들어가고
            Console.WriteLine($"메인에 있는 변수 returnedPiValue 에 저장된 파이값은 {returnedPiValue}");
        }
        
        static double GetPI()
        {
            double pi = 3.141592;
            return pi;
        }
    }
}

// 콘솔 출력
// 메인에 있는 변수 returnedPiValue에 저장된 파이값은 3.141592

 

이렇게만 보면 굳이? 스럽지만

파이 같은 우리가 일반상식으로 알고있는 값이 아닌 내부적으로 매우 복잡한 값을 계산후 반환해줘야 하는 경우나

혹은 파라미터 없이 전역 변수를 통해 계산하는 경우 등 다양한 케이스에서 활용될 수 있다.

 

함수는 정말 사용하기 나름이기 때문에 많은 경우를 접하고, 많이 사용해보면서 응용력을 키워야 한다.


이번에는 더 깊이 들어가서, 함수 호출 시 값을 전달하는 방식에 대해 정리해보자

 

Call by Value - 값에 의한 호출

프로그래밍에서 가장 기본적인 함수 호출 방식이다.

말 그대로, 값 자체를 복사해서 전달한다는 뜻이다.

 

함수를 호출할 때 원본 변수를 통째로 넘기는 게 아니라 그 값만 복사해서 함수의 매개변수로 전달된다.

그래서 함수 안에서 그 매개변수를 바꿔도, 원래 변수에는 아무런 영향이 없다.

 

코드로 예시를 보자

static void AddOne(int num)
{
    num = num + 1;
    Console.WriteLine($"함수 안: {num}");
}

int a = 10;
AddOne(a);
Console.WriteLine($"함수 밖: {a}");

// 결과
// 함수 안: 11
// 함수 밖: 10

 

함수 안에서는 num이 11로 바뀌지만, 함수 밖에서는 a가 여전히 10이다.

왜냐하면 a의 값이 복사되어 num으로 들어갔기 때문이다.

이전 글에서 다룬 값 타입의 내용을 봤다면 감이 더 잘 올 것이다.

 

Call by Reference - 참조에 의한 호출

Call by Value가 “값을 복사해서” 전달하는 거라면,
Call by Reference는 원본을 직접 참조해서 전달하는 방식이다.

즉, 함수에 들어가는 게 “복사본”이 아니라 원본 그 자체다.
따라서 함수 안에서 매개변수를 수정하면, 호출자 쪽 변수도 같이 바뀐다.

C#에서는 ref나 out 키워드를 이용해야 Call by Reference가 된다.
(그냥 쓰면 기본은 전부 Call by Value다.)

static void AddOne(ref int num)
{
    num = num + 1;
    Console.WriteLine($"함수 안: {num}");
}

int a = 10;
AddOne(ref a);
Console.WriteLine($"함수 밖: {a}");

// 출력 결과
// 함수 안: 11
// 함수 밖: 11

 

ref는 함수에 변수를 참조로 전달하겠다고 명시하는 키워드다.

읽기, 쓰기 모두 가능하며, 함수 안에서 바꾸면 호출자의 값도 바로 바뀐다.

 

단, ref를 쓰려면 변수는 호출하기 전에 반드시 초기화 돼있어야 한다.

 

다음은 ref를 이용해서 원본 변수를 스왑하는 기능도 흔히 사용하는 기법이다.

using System;

namespace CsharpStudy
{
    internal class Program
    {
        static void Main()
        {
            int a = 10;
            int b = 20;

            Console.WriteLine($"a는 {a}");
            Console.WriteLine($"b는 {b}");

            swaptTwoNumbersRef(ref a , ref b);

            Console.WriteLine($"a는 {a}");
            Console.WriteLine($"b는 {b}");

        }

        static void swaptTwoNumbersRef(ref int a, ref int b)
        {
            int temp = a;
            a = b;
            b = temp;
            Console.WriteLine($"{a}와 {b} 스왑 완료");
        }
    }
}

// 결과
// a = 20
// b = 10
// 으로 값이 바뀜

 

out도 ref와 마찬가지로 참조 전달이지만 성격이 조금 다르다.

일단 호출 전에 초기화가 필요 없다.

대신 함수 안에서 반드시 값을 할당해야 한다.

 

즉, out은 말 그대로 함수에서 결과를 밖으로 내보내는 기능이다.

아래 코드를 통해 동작을 확인해보자

using System;

namespace CsharpStudy
{
    internal class Program
    {
        static void Main()
        {
            int yourHair;
            bool isBard;

            isBard = inputHair(out yourHair);

            Console.WriteLine($"너의 머리카락은 {yourHair}개다. 그러므로 너는 대머리가 {isBard} 다.");
        }

        static bool checkBard(int hair)
        {
            if (hair > 0) return false;
            else return true;
        }

        static bool inputHair(out int yourHair)
        {
            yourHair = 0;
            return checkBard(yourHair);
        }
    }
}

// 0 입력시
// 너의 머리카락은 0개다. 그러므로 너는 대머리가 true 다

 

yourHair 값이 메인에서 inputHiar 함수로 전달되고, 함수 안에서 yourHair에는 0이 할당되어 out을 통해 메인의 yourHair 변수 또한 0이 되었다.

 

표로 정리해보자

구분 Call by Value Call by Reference
전달되는 것 값의 복사본 원본 변수의 참조
함수 안에서 수정 시 원본에 영향 없음 원본이 그대로 바뀜
안전성 높음 (원본 보호) 낮음 (원본 훼손 가능)
성능 값이 작을 때 유리 큰 값도 부담 없이 전달 가능

네임스페이스 (namepsace)

프로그래밍을 하다 보면, 점점 코드가 길어지고 파일이 많아진다.

처음에는 Program.cs 하나로 시작하지만, 시간이 지나면 Player.cs, Enemy.cs, UIManager.cs ... 수십 수백개의 파일이 생기게 된다.

파일을 안 만들면 되지 않냐고?

소스코드 파일 1개에 몇천 몇만 라인이 되는 코드를 보고싶나...?

난 싫다...

 

그런데 문제는 이렇게 많아진 클래스들이 이름이 겹칠 수 있다는 것이다.

예를들어 Player라는 이름은 게임 프로젝트라면 너무나 흔하다.

내 프로젝트에도 있을 수 있고, 외부 라이브러리를 가져다 썼더니 거기에도 Player 클래스가 있을 수 있다.

 

이때 서로 이름이 충돌하지 않도록 이름 공간을 만들어주는 게 바로 네임스페이스다.

 

이름 충돌 방지

같은 이름의 클래스라도 네임스페이스가 다르면 서로 다른것으로 취급된다.

 

코드 구조 정리

관련 있는 클래스들을 하나의 묶음으로 관리할 수 있다.

예를들어 MyGame.System, MyGame.UI, MyGame.Enemy 처럼 계증 구조를 만들 수 있다.

 

읽기 편리

using 키워드를 쓰면, 긴 네임스페이스 경로를 매번 다 적지 않아도 된다.

React에서 import 하는 것과 비슷하다.

 

아래 코드를 통해 어떤식으로 사용하는지 보도록 하자.

// Player.cs
namespace MyGame.System
{
    public class Player
    {
        public void Jump()
        {
            Console.WriteLine("점프!");
        }
    }
}
// Main.cs
using System;
using MyGame.System; // 네임스페이스 불러오기

class Program
{
    static void Main()
    {
        Player p = new Player();
        p.Jump();  // "점프!" 출력
    }
}

 

클래스 또한 C#으로 프로그래밍을 한다면 평생 써야하기 때문에 꾸준히 숙련도를 높여가도록 하자.

'C#' 카테고리의 다른 글

C# - 배열 심화 문제 - 인벤토리 크기 지정하기  (0) 2025.09.11
C# - 배열 심화 문제 - 2차원 배열 swap  (0) 2025.09.11
C# - 값 타입 (Value Type), 참조 타입 (Reference Type)  (0) 2025.09.10
C# - 배열 ( Array )  (0) 2025.09.08
C# - 콘솔로 별 찍기 (다이아몬드 편)  (0) 2025.09.08
'C#' 카테고리의 다른 글
  • C# - 배열 심화 문제 - 인벤토리 크기 지정하기
  • C# - 배열 심화 문제 - 2차원 배열 swap
  • C# - 값 타입 (Value Type), 참조 타입 (Reference Type)
  • C# - 배열 ( Array )
ellug
ellug
Web / App / Game Dev
  • ellug
    dev.ellug
    ellug
  • 전체
    오늘
    어제
    • 분류 전체보기 (95)
      • Javascript (React, Next) (1)
      • C# (66)
      • Graphics (1)
      • Unity (25)
      • 프로그래머스 (0)
        • Lv.0 (0)
        • Lv.1 (0)
        • Lv.2 (0)
        • Lv.3 (0)
      • AI Agent (0)
      • StableDiffusion (0)
      • ETC (1)
      • 잡담 (0)
      • 유머 (0)
  • 블로그 메뉴

    • 홈
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    추상 클래스
    터미널
    네트워크
    유니티
    list
    스택
    Queue
    큐
    class
    게임
    struct
    useState
    Abstract
    C#
    Photon
    inputsystem
    Network
    function
    interface
    stack
    delegate
    알고리즘
    unity
    firebase
    console
    콘솔
    Design Pattern
    클래스
    자료구조
    최적화
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
ellug
C# - 함수 (Function)
상단으로

티스토리툴바