함수를 변수에 저장?

JS나 python을 보면 파라미터로 함수를 던지거나 변수에 함수를 할당하거나 이런 일들을 많이 하는데 처음에는 너무나 골때려서 헷갈렸다. 어떻게 저렇게 함수를 넘길수있지? 함수는 실행하는 놈인데.. 이런걸 First Class Function 이라고 하라나 뭐라나.. 

 

C언어 포큐아카데미

위의 예제처럼 op1, op2를 받고 operator로 함수를 받고 싶은 함수가 있다. 그러니깐 함수를 파라미터로 던져야된다..

도대체 어떻게 할까?

 

 

함수 호출은 주소로의 Jump다

C언어 포큐아카데미

Sub 함수를 호출하는 것은 내부적으로 컴파일 될때 지정된 주소로 Jump 하는 것과 같다. 

 

00E211A6 메모리 주소에 저장된 명령어 call은 sub 함수 호출 즉 0E21040 주소로 jump 하라는 명령어다.

그러니 00E21040과 같은 함수 시작 주소를 담는 변수만 있으면 된다는 말이다. 어?!! 주소를 담는 변수? 그래 바로 포인터다. 함수 포인터!!

 

 

함수 포인터 문법

C언어 포큐아카데미

함수 포인터니깐 function * 이렇게 쓰면 귓빵맹이를 맞는다 

 

C언어 포큐아카데미

함수 포인터는 <반환형> (* 변수명)<매개변수 목록> 이런식으로 사용해야된다!

 

calculate에서 받는 매개변수 x, y는 쉬위니깐 PASS하고 함수를 받을때는 double (* func)(double, double) 이런식으로 해야된다!

 

C언어 포큐아카데미 좋다.

'C' 카테고리의 다른 글

C언어 배열 삽입, 삭제  (2) 2020.01.03
C언어 void*  (0) 2019.12.20
C언어 얕은복사 vs 깊은복사  (0) 2019.12.18
C언어 구조체  (0) 2019.12.18
C언어 포인터 배열  (0) 2019.12.13

 

React - The Complete Guide (incl Hooks, React Router, Redux)

간략 절차

1. Store에서 모든 State를 관리한다.

2. 그런데 Store의 State를 변경하려면 Reducer라는 놈을 통해서 와라

3. Reducer라는 놈을 통해서 오기전에 Action이라는 놈을 통해서 와라

 

사전지식

 

1. Reducer is a pure function which receives the action and the old state as input and spit out an updated state

 - Reducer는 그냥 js Function이다. 이 Function은 action과 old State를 파라미터로 받고 자기가 Type에 따라서 어떤 로직을 수행하고 state를 update한 뒤 return 해준다. 그런데 이 로직은 반드시 동기(Synchronous)여야 한다.

 

2. Action은 아무것도 하지 않는다, 그냥 Reducer로 가기 위한 메신저다.

 

말로 풀어서

React Component에서 Action으로 Dispatch(보내다) 한다. 그럼 Action에서 "아! 이 Component에서 보낸 Type은 xxx이구나!", "action(정확히는 action Type)과 old State를 파라미터로 가지는 Reducer야 여기 action과 old state를 줄테니 new State를 반환해다오" 

 

그러면 Reducer가 update를 하고 "야 방금 나한테 요청한 Component 누구냐?? update 했으니깐 받아가라~ triger"

 

'React' 카테고리의 다른 글

React useCallback 이해하기  (0) 2022.01.13
React(SPA) Auth 로그인 흐름  (0) 2020.01.09
React Form input  (0) 2019.12.16
얕은복사

실제 데이터가 아니라 주소를 복사하는 것을 얕은복사라 한다.

#include <stdio.h>

typedef struct name{
	char* lastname;
	char* firstname;
} name_t;

void main()
{
	char fistname[] = "Lulu";
	char lastname[] = "Lee";

	name_t name;
	name_t clone;

	name.lastname = lastname;
	name.firstname = fistname;

	clone = name;
	name.lastname[0] = 'N';

	printf("origin: %s %s\n", name.firstname, name.lastname);
	printf("clone: %s %s\n", clone.firstname, clone.lastname);

}

// 출력값
// origin: Lulu Nee
// clone: Lulu Nee

골때리게도 name의 lastname만 N으로 바꿨는데 clone의 lastname까지 변경됐다.

clone = name의 실행이 바로 얕은복사 이기 때문이다.

 

name의 구조체에서 char* lastname은 포인터다. 포인터는 주소를 변수로 저장하는 놈이다.

그러므로 구조체 char* lastname은 main함수의 lastname 배열의 주소를 값으로 가지고 있다.

따라서 mian 함수에서 name.lastname[0] ='N'을 하게되면 main 함수의 lastname의 원본 L이 N으로 변경되고 그 주소를 참조하는 변수 clone, name은 둘다 변경 된 것처럼 된다.

 

 

얕은복사 메모리

C언어 포큐아카데미

name과 clone의 lastname 값을 보면 105번 주소를 저장하고 있고 105번에는 L이 저장돼있다.

 

 

C언어 포큐아카데미

name.lastname[0]은 원본을 바꾼다. 나머지 포인터들은 그냥 가르키는 값을 참조 할 뿐이니 둘다 바뀐것 처럼 느껴진다.

 

 

깊은복사

별도의 메모리 공간에 실제 데이터를 복사한다.

//typedef struct name{
//	char* lastname;
//	char* firstname;
//} name_t;

typedef struct name{
	char lastname[64];
	char firstname[64];
} name_t;

void main()
{

	name_t name = {"Lulu", "Lee"};
	name_t clone;


	clone = name;
	name.lastname[0] = 'N';

	printf("origin: %s %s\n", name.firstname, name.lastname);
	printf("clone: %s %s\n", clone.firstname, clone.lastname);
}

// 출력값
// origin: Lee Nulu
// clone: Lee Lulu

얕은복사 코드를 위의 코드로 변경하면 origin 즉 원본의 값만 변경되고 clone 값은 유지된다. 왜냐? 별도의 메모리 공간을 확보한 뒤 거기에 값을 복사했기 때문이다.

 

'C' 카테고리의 다른 글

C언어 void*  (0) 2019.12.20
C 언어 변수에 함수 할당  (0) 2019.12.20
C언어 구조체  (0) 2019.12.18
C언어 포인터 배열  (0) 2019.12.13
C 포인터 const  (0) 2019.12.12
기본
// 선언
struct date {
	int year;
    int month;
    int day;
};


// 사용
struct date date;

date.year = 2020;
date.month = 1;
date.day = 1;

// typedef 선언
typedef struct {
	int year;
	int month;
	int day;
} date_t;

// 사용
date_t date;

날짜는 년,월,일을 모두 표현해야 의미가 있다. 날짜의 year, month, day처럼 의미있는 단위들을 모아서 선언 및 나열 하는 것이 바로 구조체다. (다른언어의 Class와 비슷하다. 하지만 구조체는 함수 즉 메소드를 가지지 않는다.)

 

사용은 struct date 변수명과 같이 사용 하며 구조체의 접근은 . 으로 접근한다. struct date 변수명이 너무 지저분하게 느껴질때는 typedef와 함께 선언해서 사용하면 date_t date와 같이 깔끔하게 사용할 수 있다.

 

 

기계의 입장에서

C언어 포큐아카데미

왼쪽은 단순히 나열, 오른쪽은 구조체를 사용해서 하나로 묶었다. 이때 기계는 구조체를 어떻게 인식할까?

C언어 포큐아카데미

결론은 똑같다. 컴퓨터 입장에서 구조체가 뭔지 모른다 그냥 사람이 편하게 코딩 할 수 있게 묶는 용도일 뿐이다. 이래서 C언어와 같은 언어를 사람이 이해하기 쉬운 하이레벨랭귀지라고 부른단다.

 

 

구조체 매개변수

C언어 포큐아카데미

date라는 구조체를 매개변수로 삼는 do_something_useless라는 함수가  있다. 구조체 매개변수는 값복사 임으로 원본의 구조체를 변경하지 못한다. 매개변수로 구조체를 던질때 그냥 일반 자료형이랑 똑같이 작동한다는 말이다.

 

그렇다면 원본을 변경하기 위해서는 어떻게 해야할까?? 구조체 포인터를 사용 하면된다.

 

구조체 포인터
typedef struct {
	int year;
	int month;
	int day;
} date_t;

void increate_year(date_t* date) 
{
	(*date).year = (*date).year + 1;
}

void main()
{

	date_t date;

	date.year = 2020;
	printf("%d\n", date.year);
	increate_year(&date);
	printf("%d\n", date.year);

}

C언어 포큐아카데미

위와 같이 구조체 주소를 던지고 함수 내부에서는 포인터로 원본에 값을 접근해서 +1 을한다.

 

C언어 포큐아카데미

(*date).이 조금 지저분할때는 date->를 사용하면 좀 더 편하게 구조체를 접근할 수 있다.

 

 

값복사 vs 주소만 넘기기

 

C언어 포큐아카데미

위와 같이 int형 5만개의 구조체를 매개변수로 넘긴다면 값복사가 5만개나 되서 50,000 X 4 = 200KB의 스택 메모리를 사용하는 엄청난 일이 벌어진다. 이럴땐 구조체의 주소를 넘겨서 포인터로 간단하게 마무리하자.

'C' 카테고리의 다른 글

C 언어 변수에 함수 할당  (0) 2019.12.20
C언어 얕은복사 vs 깊은복사  (0) 2019.12.18
C언어 포인터 배열  (0) 2019.12.13
C 포인터 const  (0) 2019.12.12
C언어 배열과 포인터 차이점  (0) 2019.12.12
Container State
    state = {
        orderForm: {
            name: {
              elementType: 'input',
              elementConfig: {
                  type: 'text',
                  placeholder: 'Your name',
              },
              value: ''
            },
            street: {
                elementType: 'input',
                elementConfig: {
                    type: 'text',
                    placeholder: 'Your street',
                },
                value: ''
            },
            zipCode: {
                elementType: 'input',
                elementConfig: {
                    type: 'text',
                    placeholder: 'Your zipCode',
                },
                value: ''
            },
            country: {
                elementType: 'input',
                elementConfig: {
                    type: 'text',
                    placeholder: 'Your country',
                },
                value: ''
            },
            email: {
                elementType: 'input',
                elementConfig: {
                    type: 'email',
                    placeholder: 'Your email',
                },
                value: ''
            },
            deliveryMethod: {
                elementType: 'select',
                elementConfig: {
                    option: [
                        {value: 'Fastest', displayValue: 'Fastest'},
                        {value: 'cheapest', displayValue: 'Cheapest'},
                    ]
                },
                value: ''
            },
        }
    }

위와 같은 state가 있다.  orderForm안에 들어갈 input 들은 name, street, zipCode 등이 있다.

 

Input Component
import React from 'react'
import classes from './Input.css'

const input = (props) => {
    let inputElement = null;

    switch (props.elementType) {
        case ('input'):
            inputElement=<input
                className={classes.InputElement}
                {...props.elementConfig}
                onChange={props.changed}
                value={props.value} />
            break;
        case ('textarea'):
            inputElement=<textarea
                className={classes.InputElement}
                {...props.elementConfig}
                onChange={props.changed}
                value={props.value} />
            break;
        case ('select'):
            inputElement=(
                <select
                    className={classes.InputElement}
                    onChange={props.changed}
                    value={props.value}>
                    {props.elementConfig.option.map(option => (
                        <option key={option.value} value={option.value}>
                            {option.displayValue}
                        </option>
                    ))}
                </select>
            )
            break;
        default:
            inputElement=<input
                className={classes.InputElement}
                {...props.elementConfig}
                value={props.value} />
    }

    return (
        <div className={classes.Input}>
            <label className={classes.Label}>{props.label}</label>
            {inputElement}
        </div>
    )
}
export default input

props의 elementType에 따라 그냥 input이되기도, textarea, select가 되기도 한다.

inputElement 변수에 input값을 담는다.

 

Container render
    render() {
        const formElementsArray = []
        for (let key in this.state.orderForm) {
            formElementsArray.push({
                id: key,
                config: this.state.orderForm[key]
            })
        }

        let form = (
            <form onSubmit={this.orderHandler}>
                {formElementsArray.map(formElement => {
                    return (<Input
                        key={formElement.id}
                        elementType={formElement.config.elementType}
                        elementConfig={formElement.config.elementConfig}
                        value={formElement.config.value}
                        changed={(event) =>this.inputChangedHandler(event, formElement.id)}
                    />)
                })}
                <Button btnType="Success" clicked={this.orderHandler}>Order</Button>
            </form>
        )
        if (this.state.loading) {
            form = <Spinner />
        }
        return (
            <div className={classes.ContactData}>
                <h4>Enter your Contact Data</h4>
                {form}
            </div>

        );
    }

1. formElementsArray 변수에 {id, config}의 형태로 push

2. map으로 for문을 돌면서 Input 값을 생성(return) 해준다.

2-1. changed 매개변수로 사용자의 키보드 입력값을 받을 함수를 넘겨준다.

 

inputChangedHandler 함수
    inputChangedHandler = (event, inputIdentifier) => {
        const updatedOrderForm = {
            ...this.state.orderForm
        }
        const updatedFormElement = {
            ...updatedOrderForm[inputIdentifier]
        }
        updatedFormElement.value = event.target.value
        updatedOrderForm[inputIdentifier] = updatedFormElement
        this.setState({orderForm: updatedOrderForm})
    }

Input Component의 onChange event가 발생하면 매개변수로 이벤트 발생 input값과 그 input 값을 찾기 위한 inputIdentifier을 받는다.

 

immutable하게 state를 업데이트 해준다.

 

Form Submit
    orderHandler = (event) => {
        event.preventDefault()
        this.setState({loading: true})
        const formData = {}
        for (let formElementIdentifier in this.state.orderForm) {
            formData[formElementIdentifier] = this.state.orderForm[formElementIdentifier].value
        }
        const order = {
            ingredients: this.props.ingredients,
            price: this.props.price,
            orderData: formData
        }
        axios.post('/orders.json', order)
            .then(res=> {
                this.setState({loading: false})
                this.props.history.push('/')
            })
            .catch(error => {
                this.setState({loading: false})
            })
    }

1. render 함수의 form Tag에서 onSubmit으로 orderHandler 함수 정의

2. state의 orderForm을 immutable하게 formData 변수로 생성

3. axios post Call 실행

'React' 카테고리의 다른 글

React useCallback 이해하기  (0) 2022.01.13
React(SPA) Auth 로그인 흐름  (0) 2020.01.09
React Redux 기본  (0) 2019.12.19

포인터 배열이란 무엇인가..... 말 그대로 포인터 변수를 저장하는 배열이다.

int nums[3]은 int를 저장하는 배열 int* nums[3] int*를 저장하는 배열이다.

 

 

C언어 포큐아카데미

num_pointers라는 포인터 배열은 nums1, nums2, num3의 주소를 저장하는 배열이다. 

num_pointers[0][0], num_pointers[0][1] 과 같이 2차원 배열처럼 접근 할 수있다.

 

그럼 2차원 배열이랑 포인터 배열이랑 똑같네?? 아니!!!!

 

2차원 배열과 포인터 배열이 같다?(아니다)

에러(C언어 포큐아카데미)

 

matrix라는 2차원 배열을 만들고 do_magic에서는 포인터 배열로 받았다 왜냐? 둘은 똑같으니깐

이렇게 하면 컴파일 에러가 난다!. 왜냐? 2차원 배열과 포인터 배열은 다르니깐

 

2차원 배열은 한덩어리 메모리라 주솟값이 저장된 곳이 없다. 반면 포인터 배열은 각각의 요소에 주소가 저장된다.

(밑에서 더 자세히)

 

 

수정(C언어 포큐아카데미)

위와 같이 2차원 배열은 2차원 배열로 받아야한다. 그래야 컴파일러가 offset을 이해할 수 있다.

 

 

 

#include <stdio.h>


int main(void)
{
    const int nums[3][5] = {
        { 35, 50, 65, 24, 71 }, 
        { 52, 93, 30, 18, 83 },
        { 72, 53, 41, 88, 69 }
    };
    const int* nums2[3];
    
    nums2[0] = nums[0];
    nums2[1] = nums[1];
    nums2[2] = nums[2];
    
    printf("nums[0] address: %p\n", (void*)nums[0]);
    printf("nums[1] address: %p\n", (void*)nums[1]);
    printf("nums[2] address: %p\n", (void*)nums[2]);
    printf("nums[2]'s offset from nums[0]: %d\n", nums[2] - nums[0]);         /* 10 */
    printf("nums[1]'s offset from nums[0]: %d\n", nums[1] - nums[0]);         /* 5 */
    
    printf("\n");
    
    printf("nums2[0] address: %p\n", (void*)&nums2[0]);
    printf("nums2[1] address: %p\n", (void*)&nums2[1]);
    printf("nums2[2] address: %p\n", (void*)&nums2[2]);
    printf("nums2[2]'s offset from nums2[0]: %d\n", &nums2[2] - &nums2[0]);   /* 2 */
    printf("nums2[1]'s offset from nums2[0]: %d\n", &nums2[1] - &nums2[0]);   /* 1 */
    
    return 0;
}

nums는 2차원배열, nums2는 포인터 배열이다. 주목해야 할 점은 offset(얼마나 떨어져있냐)의 값이다.

2차원 배열의 경우 nums[0]과 nums[1]은 5만큼 떨어져있다. 그 말을 풀어서 이야기하면 nums[0]인 35와 nums[1]인 52 사이에 5개의 메모리 블럭이 존재한다고 보면 되겠다.(좀 더 정확히 하면 4바이트 * 5 = 20바이트 만큼 떨어짐)

 

반면 포인터 배열의 경우 nums2[0]과 nums[1]의 offset은 1이다. 왜냐하면 각각의 메모리 블럭에 주소가 저장되어 있기 때문이다.(하나의 포인터의 크기만큼 떨어져있다)

 

- C언어 포큐 아카데미 복습 -

'C' 카테고리의 다른 글

C언어 얕은복사 vs 깊은복사  (0) 2019.12.18
C언어 구조체  (0) 2019.12.18
C 포인터 const  (0) 2019.12.12
C언어 배열과 포인터 차이점  (0) 2019.12.12
C 스택 메모리 해부(어셈블리어)  (3) 2019.12.09

const 변수를 불변하게 만들고 싶을때 사용한다.(immutable)

요즘 js, React쪽에서는 웬만한 변수는 다 const로 바르고 그게 아닌 경우에만 let(mutable)로 선언한다.

 

C에서도 const를 당연히 사용하는데 포인터에 사용할 때 귀여운 상황이 벌어진다.

 

1. 포인터가 담은 값(주소)를 불변하게 할 것인가

2. 포인터가 담은 값(주소)로 간뒤에 그 값을 불변하게 할 것인가(* 로 접근)

 

 

주소냐, 값이냐

C언어 포큐아카데미

왼쪽의 int* const p = &num은 포인터의 값 즉 주소를 변경하는 것을 막는 코드이다.(1번)

오른쪽의 const int* p = &num은 포인터가 가르키는 값을 변경하는 것을 막는 코드이다.(2번)

실제로 2번 값을 막는 것이 훨씬 많이 쓰이며 중요하다고 한다.

 

'C' 카테고리의 다른 글

C언어 구조체  (0) 2019.12.18
C언어 포인터 배열  (0) 2019.12.13
C언어 배열과 포인터 차이점  (0) 2019.12.12
C 스택 메모리 해부(어셈블리어)  (3) 2019.12.09
C언어 Char, int 정리  (0) 2019.12.05

배열은 주소다. 포인터 변수도 주소를 값으로 가진다.

 

배열과 포인터는 맥락상 거의 같다. 하지만 포인터에서는 먹히고 배열에서는 안되는게 몇가지가 있다.

 

이 차이점을 분명히 하면서 포인터에 대한 이해도를 높이자.(포큐아카데미 설명이 정말 자세하다)

 

sizeof

C언어 포큐아카데미

sizeof으로 크기를 알고 싶을때 포인터는 변수 임으로 4바이트(사용하는 bit수에 따라 다름)를 반환값으로 가지지만 배열은 해당 원소의 개수 X 자료형의 크기를 반환한다.

 

 

배열 문자열

C언어 포큐아카데미

배열로 Monday 라는 문자열을 만들게 되면 day1 변수는 스택 메모리에 저장되며 값 또한 실제로 존재한다.

day[0]은 "M" day[1]은 "o" ... 이런식으로 물론 변경도 가능!

 

 

포인터 문자열

C언어 포큐아카데미

포인터로 day2 문자열 변수를 만들게 되면 day2라는 변수는 스택에 저장 되지만 값으로는 "Monday" 문자열의 주소값을 가짐 또한 "Monday" 문자열의 메모리 저장 공간은 데이터 영역임.(값 변경 불가능 ReadOnly)

 

 

포인터, 배열 대입 차이점

배열로 만든 문자열 수정 가능 BUT 포인터로 만든 문자열 수정 불가

 

배열은 상수다! 이말을 기억하면 안되는것들을 판단할때 참 편하다. 위 처럼 포인터변수는 값을 대입할 수 있지만 배열 상수는 불가!

 

 

포인터 변수는 증감을 할 수 있지만 배열 상수는 불가!

 

 

'C' 카테고리의 다른 글

C언어 구조체  (0) 2019.12.18
C언어 포인터 배열  (0) 2019.12.13
C 포인터 const  (0) 2019.12.12
C 스택 메모리 해부(어셈블리어)  (3) 2019.12.09
C언어 Char, int 정리  (0) 2019.12.05

+ Recent posts