ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [컴프실] 4일차
    공부/cpp 2023. 2. 21. 19:33

    2차원배열은 말그대로 평면의 모양을 하고 있다. 표가 행과 열로 이루어져있는 것처럼 2차원 배열도 행과 열로 이루어져있다.

    인덱스가 0부터 시작하므로 0행 0열부터 시작한다.

     

    2차원 배열도 1차원 배열과 마찬가지로 동일한 자료형밖에 넣지 못한다.

     

    2차원 배열 선언

    int a[3][3]; 3행 3열 배열이 나온다.

     

    2차원 배열에 데이터가 저장될 때 실제로 면모양으로 저장되는 것은 아니다.

    메모리는 쭉 연속되어져있는 저장공간이다.

    즉, 메모리가

    0행 0열 0행 1열 0행 2열 1행 0열 1행 1열 1행 2열 2행 0열 2행 1열 2행 2열

    이런식으로 2차원배열이 저장된다.

     

    편하게 머리속으로 생각하기에 면모양의 입체형 배열이라고 생각하지만, 실제로 메모리에는 선모양으로 저장이 된다.

     

    2차원 배열 초기화도 1차원 배열 초기화와 같다.

    int s[3][3] = {

                       {1,2,3}, //0행

                       {2,4,6}, //1행

                       {3.6.9}, //2행

                       };

     

    이런 식으로 하면 된다.

     

    물론 int s[3][3] = {{1,2,3},{2,4,6},{3,6,9}}; 이런식으로 해도 되긴 하지만, 가독성이 나빠진다.

     

    #include <iostream>

    using namespace std;

     

    #define WIDTH 9;   //const int WIDTH =9;

    #define HEIGHT 3; //const int HEIGHT = 3;

    int table[WIDTH][HEIGHT];

    int row, column;

     

    for(row = 0; row < HEIGHT; row++)

                   for(column =0; column < WIDTH; column++)

                                   table[row][column] = (row + 1) * (column + 1);

     

    for(row = 0; row < HEIGHT; row++){

        for(column; column < WIDTH; column++){

            cout << table[row][column] <<",";

            }

        cout << endl;

    }

     

    출력

    1,2,3,4,5,6,7,8,9

    2,4,6,8,10,12,14,16,18,

    3,6,9,12,15,18,21,24,27,

     

    함수와 문자열

    함수란 무엇일까?

    내가 특정한 목적을 위해서 프로그래밍을 하는데, 문제는 이 메인 안에서 코드를 짜는데, 어떤 기능이 여러군데에서 필요하다. 그럴 때 기능을 함수로 구현해서 편의를 위해 사용하는 것이 함수이다.

     

    예를 들어 두개의 숫자를 더하는 프로그램이 있다고 가정한다.

    그럼 2와 3을 더해라라는 프로그램 코드를 작성한다.

    그런데 그 아래줄에서 100과 200을 더하는 것이 필요하다.

    그럼 또 코드를 작성하는데, 위에서 작성한 프로그램 코드와 똑같고, 단지 숫자만 다를 뿐이다.

     

    이렇게 똑같은 기능일 때, 똑같은 코드를 중복해서 작성하면, 쓸데 없이 코드 줄만 늘어나서 가독성이 떨어진다.

    이렇게 내가 동일하게 사용하는 부분 즉, 기능을 독립적으로 메인함수에서 따로 빼서 함수로 만든다.

     

    물론 이 함수는 c, cpp, 파이썬, 자바 등등 기본적으로 제공하는 함수들이 있다.

    하지만, 자신이 직접 만드는 함수들도 있다.

    이것을 user defined function 즉 사용자 정의 함수(기능)

    나는 솔직히 함수라는 말 별로 안좋아한다. 함은 함대할 때 함이고, 수는 셀수 이다.

    뭔 소리인지 모르겠다. 그냥 영어처럼 기능! 이렇게 부르면 안되나...

     

    각설하고, 아까 들어가는 숫자만 다르다고 했었는데, 이때 들어가는 숫자들을 인자, 파라매터(parameter) 라고 부른다.

    이런 인자들은 메인에서 사용자 정의 함수로 전달해준다.

     

    함수 선언

    함수 자료형 함수명(인자, 인자, 인자,....){

    문장

    return 함수값(반환값)}

     

    함수의 데이터 타입은 함수를 모두 수행하고 나온 결과의 데이터 타입을 따른다.

    예를들어 int 형 숫자를 계산했는데, 결과 값으로 char형이 나왔으면 함수의 데이터타입은 char형인 것이다.

    이렇게 결과 값을 반환값이라고 한다. 보통 return 을 써서 나타낸다.

    반환값이나, 인자가 없을 수도 있는데 그럴 때는 void(없음)을 써준다.

     

    함수형은 아무렇게나 지어야하는데 몇가지 제약이 있긴한다. c, cpp에서 이미 사용하는 예약어(키워드라고도 한다.)는 함수명으로 쓰지 못하고, 몇가지 특수문자도 제한된다.

    예약어는 if, string, int, switch 이렇게 프로그래밍 언어 내에서 기본적으로 정의되어있는 단어를 키워드라고 한다.

    함수명으로 if 사용 못하고, string 사용 못한다는 뜻이다.

     

    함수호출

    먼저 메인 위에 함수가 정의 되어있어야한다.

    그리고, 프로그램의 시작점은 무조건 메인이므로, 컴퓨터가 메인에서부터 코드를 쭉 읽다가 함수를 만나면, 함수를 호출한다.

    메인 위에 함수를 정의하는 것도, 메인 아래에 함수가 정의되있으면, 컴퓨터는 멍청해서 함수를 찾지 못하기 때문이다. 메인 위에 정의해야지 컴퓨터가 함수를 불러올 수 있다.

    함수를 호출하면, 함수 내 문장(코드)을 쭉 실행한다음 반환값을 main 함수에 전달해준다.

     

    예를들면

    int main(){

    문장;

    문장;

    문장;

    n = max(2,3);

    문장;

    문장;

    m= max(100,200);

    문장;

    문장;

    return 0;

    }

     

    여기에서 프로그램이 문장을 읽다가 n = max(2,3);이라는 함수가 있는 문장을 만났다. 그럼 main 함수 위에 사용자 정의 함수가 어디있는지 쭉 찾아본다.

     

    int max(int x, int y){

    if(x>y)

        return x;

    else

        return y;

    }

     

    메인 함수 위에 max 사용자 정의 함수가 존재했다. 그럼 아까 n = max(2,3);에서 2와 3을(인자, paramater) int x, int y자리에 전달한다. 물론 이때 전달하는 데이터 타입이 int 형에 맞아야한다. 만일 char x, char y라고 되어 있는데, max(2,3)을 전달했으면 에러가 발생한다.

    그럼 2와 3을 받고, 함수 내의 문장을 실행한다. 2>3이 거짓이므로 else문장을 수행해 y를 결과값(함수값)으로 반환한다. 이 반환한 값은 main 함수의 n에 저장된다.

    n = max(2,3); -> n = 3 이렇게 되는 것이다.

     

    그다음에 문장을 수행하다가, m = max(100,200);을 만나면 100과 200을 main 위에 정의된 사용자 정의 함수에 전달해주고, 다시 반환 값을 m에 저장시킨다.

     

    이렇게 main 함수의 값을 복사하여 사용자 정의 함수에 직접 전달하는 방식을  call by value라고 한다.

     

     

    함수 원형(function prototype)

    아까 컴퓨터가 멍청해서 메인 함수 아래에 있는 사용자 정의 함수를 못찾는다고 했는데, 그 해결책이 있다.

    main 위에 함수 필수 정보를 간략하게 요약해서 main 함수 위에 적는 것이다.

    이 요약 정보를 함수 원형이라고 한다.

    그럼 실제 함수 본체는 main 아래에 있어도 컴퓨터가 인식할 수 있다.

    예를 들어 위에서 소개한 max 함수를 함수 원형으로 작성하면

    int max(int x, int y);

    이런 식으로 main 위에 적으면 된다.

     

    int max(intx, int y);

    int main(){

    }

    int max(int x, int y){

    return 어쩌고;}

     

    이런식으로 써주면 컴퓨터가 알아먹는다.

     

    이렇게 함수 원형을 적으면, 만일 여러함수가 프로그램 내에서 쓰였을 경우 다른 프로그래머 들이 '아 이러이러한 함수들이 쓰이는 구나'라고 알아보기 쉽고, 가독성도 좋아진다.

     

     

    함수 호출 시 인수 전달 방법

    지금까지 배운 함수들은 모두 call by value 방식이었다.

    그런, call by value 방식을 사용했을 때 내가 원하는 결과가 나오지 않을 수도 있다.

    그럴 경우에는 call by reference를 이용한다.

     

    call by value와 call by reference의 차이는

    딱 한가지다. 값을 직접 전달하느냐, 아니면, 값이 있는 위치를 가르쳐주느냐...

     

    무슨 차이냐면

    내가 집에 카드를 놓고 왔다. 그래서 엄마한테 연락해서, "엄마 나 카드 좀 가져다줘."했는데 엄마가 집에서 카드를 복사해서 2개를 만들고, 그 중 새로만든 것을 자식한테 직접 주는게 call by value이고,

    엄마가 "엄마 지금 친구 만나고 있어 어딨는지 알려줄테니 니가 직접 찾아가. 카드는 집 서랍 몇번 째 칸에 있어." 라고 하면

    call by reference이다.

     

    즉, call by value는 main에서 직접 값을 복사하여 함수에게 전달하는 것이고, call by reference는 main에서 함수로 값을 직접 전달하지 않고, 그 값이 있는 메모리 주소(위치)를 가르쳐주면, 함수가 그 메모리 주소로 가서 값을 주섬주섬 사용하는 것이다.

     

    swap() 함수 만들기

    swap(a, b) a와 b의 값을 서로 바꿈

     

    //함수 원형

    int swap(int a, int b);

    //메인 함수

    int main() {

    int m = 100, n = 200;

    cout << "m = "<< m << ", n = " << n << endl;

    swap(m, n);

    cout << "m = "<< m << ", n = " << n << endl;

    return 0;

    }

     

    //swap() 함수

    void swap(int a, int b){

    int tmp; //임시공간 생성

    tmp  =a; // a의 값을 임시공간에 복사

    a = b; // a의 값을 b의 값으로 변경

    b = tmp; // 임시 공간에 저장된 값으로 b의 값을 변경

    }

     

    이렇게 하면, main 에서 m과 n의 값이 변경이 되지 않는다. 왜냐하면 함수에 return문, 즉 반환값이 없기 때문이다.

    또 하나 파이썬은 2개 이상의 값(튜플)이 리턴이 되지만, c언어에는 불가능하다고 알고 있다. 그래서 call by reference 방법을 이용하거나, 머... 구조체 방법을 이용하는 것도 있다는데, 여기서는 call by reference 방법을 이용하겠다. 필자도 구조체에 대해서는 잘 모른다.

     

     

    call by reference 방법

    //함수 원형

    int swap(int* a, int* b);

    //메인 함수

    int main() {

    int m = 100, n = 200;

    cout << "m = "<< m << ", n = " << n << endl;

    swap(&m, &n);

    cout << "m = "<< m << ", n = " << n << endl;

    return 0;

    }

     

    //swap() 함수

    void swap(int* a, int* b){

    int tmp; //임시공간 생성

    tmp = *a; // a의 값을 임시공간에 복사

    *a = *b; // a의 값을 b의 값으로 변경

    *b = tmp; // 임시 공간에 저장된 값으로 b의 값을 변경

    }

     

    이런식으로 *와 & 기호가 생소할지 모르는데 c언어의 포인터 개념이다. 

    cpp에서는 포인터를 이용하지 않고, 참조자를 이용했는데...

    그런데 여기에서는 일단  c언어의 포인터를 먼저 설명하겠다.

     

    call by reference는 main 함수에서 인자를 전달할 때, 값이 아닌 위치를 전달한다.

    &는 위치를 표현할 때 나타내는 기호로 &m 이렇게 하면 m이 있는 메모리 주소를 swap 함수에 전달해준다. 0x61fe1c 머 이런식으로 전달해준다.

     

    그럼 swap 함수에서 0x61fe1c 주소로 가서 m의 값을 찾아서 수정한다.

    이 때 값이 아니라 주소를 받으려면, int *형 포인터변수 a를 사용해줘야한다.

    그냥 일반 int형 변수 a로 받으면 m의 주소를 9로 수정해버려서 m의 값이 이상하게 변경이 된다.

    주소를 받을 때는 포인터 변수를 사용해야한다는 것을 기억하자.

     

    어쨌든 이렇게 사용자 정의 함수에게 인자로 주소를 전달하고, 사용자 정의 함수에서는 인자로 주소를 받아서 직접 그 주소를 찾아가 값을 수정하는 것을 call by reference 라고 한다. 

    이러면 return 문을 사용해서 값을 반환하지 않더라도 m의 값이 수정이 된다.

     

    cpp에서는 참조자를 사용하는데

    int a = 10;

    int &r = a;

     

    이런식으로 사용한다.

    int a = 10; 이라는 것은 그냥 일반 변수를 10으로 초기화한거고,

    int &r = a;는 a의 별명(nickname)이 r이고, &는 r이 변수명이 아니라, 별명이라는 것을 가리키는 역할을 한다.

    &를 cpp에서는 유식한 말로 참조자라고 한다.

    이를 해석하면, '참조자 r은 변수 a의 별명입니다.' 라고 해석할 수 있다.

     

    a = 20 으로 바뀌면, 자동으로 a의 별명인 r도 20으로 바뀐다.

    즉, a와 r은 사람으로 따지면, 동일인물이다. 말그대로 호칭만 다른 것이다.

     

    예를 들어 아무개라는 이름의 사람이 있다.

    그 사람의 별명은 타락파워전사이다.

    만일 아무개가 옷을 입었다. 그럼 타락파워전사도 옷을 입은 것이다.

     

    말그대로 별명 개념이다.

     

    프로그램 예제

    int max(int& a, int& b); //함수원형

    int main(){

    int m = 2, n = 9;

    max(m, n);

    return 0;

    }

    void max(int& a, int& b){

    int tmp;

    tmp =a;

    a = b;

    b =tmp;

    }

     

    이런식으로 사용한다. 즉, 인수로 값을 주는데, 받는 것은 별명으로 받는 것이다.

    포인터랑 다른 점은

    c언어의 포인터는 카드를 엄마가 카드의 위치를 가르쳐주는 것이고,

    cpp언어의 참조자는 내가 그냥 집에 있는 카드를 직접 찾아오는 것이다.

     

    정리

    call by value :  main에서 값을 복사해서 함수에 전달하면 함수는 그 복사본 값에 접근함.

    call by reference : main에서 값이 있는 주소를 전달하면, 함수에서 그 주소로 찾아가 값을 접근함.

    call by reference cpp : main에 있는 원본을 함수에서 직접 찾아옴 이때 원본은 다름이름(별명, 참조자)이 됨.

     

     

    c에서는 참조자 개념이 없지만, cpp에서는 참조자 개념이 있다.

    그래서 c에서는 int& a; 이런 표현이 잘못된 표현지만,

    cpp에서는 int& a; 와 int &a;와 같은 표현이다.

    이때, 주소 나타낼 때의 &a와는 구별해야한다. 앞에 자료형이 붙어 있으면, 주소 &이고, 자료형이 없으면 참조자 &이다.

     

     

    중복함수

    c언어에서는 중복함수를 허용하지 않았지만, cpp에서는 중복함수 즉, 동일한 이름의 함수를 쓸 수 있게 만들어준다.

    하지만, 제약이 있는데, 중복함수의 인수의 자료형이 다르던가, 인수의 자료형이 같으면 인수의 개수가 다르던가 둘 중의 하나는 만족해야한다.

    함수의 자료형은 별로 상관없다.

     

    예를들어 정수를 제곱하는 함수

    int square(int i){

        return i*i;

    }

    를 만들었다 생각해보자

     

    그다음에 실수를 제곱하는 함수

    double square(double i){

        return i*i;

    }

    를 만들었다. 이때, c에서는 같은 square이라는 이름을 가져서 오류가 나지만, cpp에서는 이름은 같지만, 매개변수의 자료형이 같아서 두 함수 모두 이용이 가능하다는 것이다.

     

    디폴트 인수

    말그대로 디폴트 인수이다. 함수의 인수에 값을 저장하면, 메인에서 그 함수를 호출할 때 인수를 넘겨주지 않으면 저장된 값으로 함수를 처리한다.

    예)

    swap(int a = 3, int b = 2){

    int tmp;

    tmp = a;

    a = b;

    b = tmp;

    cout << a <<", " << b << endl;

    }

     

    int main(){

    int m = 1;

    int n = 4;

    swap(4);

    return 0;

    }

     

    이렇게 하면, swap 함수의 인수는 총 2개를 받아야하는데, 한개 밖에 못받았다. 그럼 4는 받았으므로 swap()의 a =4가 되는 것이고, b = 2가 되어 4, 2를 출력한다.

     

    인라인함수

    인라인 함수를 설명하기 전에 함수 호출 과정을 짚고 넘어가야한다.

    main에서 함수를 호출하면 함수의 인수의 개수대로 그 함수를 위한 공간(스택)을 만든다. 하지만, 그 공간(스택) 만드는 시간이 많을 수록 시간이 걸리기 때문에

    인라인 함수라는 개념이 나타났다.

    인라인 함수는 복붙 개념인데 함수를 구성하는 코드를 컴파일 하는 과정에 main 내에 호출된 부분에 그대로 복사 붙여넣기 하여 시간을 줄인다.

     

    예를 들어

    inline int sum(int x, int y){

        return(x+y);

    }

     

    int main(){

        int n;

        n = sum(2,3); //컴파일 과정에서 sum(2,3) 대신, n = 2+3이 들어간다.

        cout << "연산 결과 = " << n << endl;

        return 0;

    }

    '공부 > cpp' 카테고리의 다른 글

    [컴프실] 5일차 -클래스  (0) 2023.02.23
    [컴프실] 5일차  (0) 2023.02.22
    [컴프실] 3일차  (1) 2023.02.18
    [컴프실] 2일차  (0) 2023.02.01
    [컴프실] 1일차  (1) 2023.01.09
Designed by Tistory.