변수의 이름은 어디에 저장 되는가?

김동현·2023년 11월 5일

Question

목록 보기
2/15
post-thumbnail

변수

變數
變 변할 변 數 셈 수

변수란, 하나의 값을 저장할 수 있는 공간이다.

let mut name = "김범석";
name = "김동현";

let mut num = 1;
num = 2;
num = 3;
let name = "김범석";
name = "김동현";

let num = 1;
num = 2;
num = 3;

🤔 여기서, 저 name, num은 어디에 저장될까???

고급언어, 저급 언어

📌 고급언어

  • 우리가 프로그래밍 코딩을 할 때 사용하는 JAVA, Python, JavaScript… 등은 인간이 이해하고 사람을 위해 만든 고급 언어이다.
  • 컴퓨터는 이 고급언어를 이해하지 못한다.
  • 모든 고급 언어의 소스 코드는 컴퓨터가 이해 할 수 있는 저급 언어로 변환된다.

📌 저급 언어

  • 컴퓨터가 이해할 수 있는 0과 1로 이루어진 명령어이다.
  • 저급 언어는 명령어로 이루어져 있다!!
  • 저급 언어는 두 가지로 나뉜다.
  1. 기계어 → 0과 1의 명령어 비트로 이루어진 언어이다.
    1. 0과 1로 이루어진 기계어는 가독성이 떨어지고 길어지기 때문에 십육진수를 사용하여 표현하기도 한다.
    2. 0과 1로 이루어져 있는 기계어는 인간이 해석하기에 너무 어렵다.
  2. 어셈블리어 → 0과 1로 이루어진 명령어(기계어)를 인간이 읽기 편한 형태로 번역한 언어이다.

컴파일 언어

📌 컴파일 방식

  • 고급언어에서 저급언어로 변환하는 한가지 방법!
  • 컴파일 언어는 컴파일러에 의해 소스 코드 전체가 저급 언어로 변환한 뒤 실행되는 고급언어이다.
    • 즉, 컴파일 언어로 작성된 코드는 코드 전체가 저급 언어로 변환된 후 실행된다.
  • 컴파일 과정에서 개발자가 작성한 코드에 오류는 없는지, 실행 가능한 코드인지, 실행하는 데 불필요한 점은 없는 지 등을 따져 소스 코드를 처음부터 끝까지 컴파일한다.
    • ⚠️이 때 오류가 하나라도 있으면 컴파일되지 않게 된다.
  • 컴파일이 성공적으로 수행되면 소스 코드는 컴퓨터가 이해할 수 있는 저급 언어로 변환된다.
  • 이렇게 컴파일러를 통해 저급 언어로 변환된 코드를 목적코드라고 한다!

컴파일

프로그래밍 언어에서 사용하는 변수는 메모리 위치에 이름을 부여한 것이며, 메모리 위치에 데이터를 저장하거나 검색하기 위한 간단한 추상화 수단이다.

C언어나 Rust처럼 컴파일 수행을 마치고 나면, 이 변수는 모두 사라진다.

fn main () {
  let mut num = 1;
  num = num + 1;
  num = num + 2;

  let added = add_num(num, num);

  println!("{}", num);
  println!("{}", added);
}

fn add_num (a:i32, b: i32) -> i32 {
   a + b
}

🤔 다시, 여기서 저 num, a, b는 어디에 저장될까???

  • rustc --emit=asm main.rs
// rust
__ZN4main7add_num17h2526100505014ec5E:
    .cfi_startproc
    sub sp, sp, #32
    stp x29, x30, [sp, #16]
    add x29, sp, #16
    stur w8, [x29, #-4]
    cset w8, vs
    tbnz w8, #0, LBB10_2
    b LBB10_1
LBB10_1:
    ldur w0, [x29, #-4]
    .cfi_def_cfa wsp, 32
    ldp x29, x30, [sp, #16]
    add sp, sp, #32
    .cfi_def_cfa_offset 0
    .cfi_restore w30
    .cfi_restore w29
    ret
LBB10_2:
    .cfi_restore_state
    adrp x0, _str.0@PAGE
    add x0, x0, _str.0@PAGEOFF
    mov w8, #28
    mov x1, x8
    adrp x2, l___unnamed_11@PAGE
    add x2, x2, l___unnamed_11@PAGEOFF
    bl __ZN4core9panicking5panic17h23f868a19cef4049E
    .cfi_endproc

_main:
    .cfi_startproc
    stp x29, x30, [sp, #-16]!
    mov x29, sp
    sxtw x1, w8
    adrp x0, __ZN4main4main17hb9bf57f50a56af79E@PAGE
    add x0, x0, __ZN4main4main17hb9bf57f50a56af79E@PAGEOFF
    mov w3, #0
    bl __ZN3std2rt10lang_start17ha61c943fd4aad6b8E
    ldp x29, x30, [sp], #16
    ret
    .cfi_endproc

  • Rust 컴파일러는 함수 이름을 몇 가지 규칙에 따라 변경하고 압축하는데, 그 중 하나는 함수 이름을 해시값 또는 다른 일련의 문자로 변환하는 것입니다.
  • __ZN4main7add_num17h2526100505014ec5E는 Rust 코드의 함수 add_num을 가리키는 내부 이름이 됩니다.
  • 위의 어셈블리어를 보면 num이나, a, b와 같은 변수명이 존재하지 않습니다!

😤 변수의 이름은 저장되지 않는 것을 확인했다!
즉, 변수의 이름은 우리가 값을 저장 할 때 편하게 추적하기 위해 추상화한 것으로 메모리 위치에 이름을 붙인 것이다!

이 변수명은 컴파일러의 컴파일 후에 메모리의 주소로 변경되어 사라지게 된다!
-> 변수명이 나오면 추적하고 있던 해당 메모리의 위치를 넣어주는 것아닐까?


C언어 컴파일 보기

// test.c
#include <stdio.h>

int main(void) {
    int num = 12;
    int num2 = 13;

    printf("%d", num2);
    return num2 + num;  // num 변수의 두 배 값을 반환
}
  1. cc -Wall -Werror -Wextra -O3 test.c -o example
  2. otool -tV example

  • 😤 여기서도 num, num2에 대한 변수명도 보이지 않는다!

cc -Wall -Werror -Wextra -O3 test.c -o example
1. cc: C언어 컴파일을 위한 컴파일 명령어
2. -Wall: 컴파일러에게 경고 메시지를 모두 표시하는 옵션
3. -Werror: 경고를 오류로 처리하라는 옵션
4. -Wextra: -Wall 보다 더 많은 경고 생성
5. -03: 최적화 래벨 옵션 3은 최대 수준의 최적화
6. test.c: 컴파일하려는 C 소스코드
7. -o example: 컴파일 된 실행가능한 바이너리 파일 이름 설정

otool -tV example
1. otool: 실행 가능한 바이너리 파일을 어셈블리어로 보이게 해주는 명령어
2. -t: 텍스트 섹션을 디스어셈블 하라고 지시 -> 실행 파일의 코드 섹션
3. -V: 버전 정보 표시
4. example: 프로그램의 실행 파일

변수명

  • 변수명은 프로그래밍 언어에서 메모리의 주소(위치)에 이름을 붙인 것이다!
  • 이 변수는 변경 가능한 값으로 다른 데이터를 넣을 수 있으며, 동일한 자료형 내에서 값을 변경할 수 있는 하나의 값을 저장할 수 있는 저장공간이다!

참고 자료

https://colossus-java-practice.tistory.com/1
https://stackoverflow.com/questions/14612314/how-are-variable-names-stored-in-memory-in-c
https://www.quora.com/Where-is-the-name-of-a-variable-stored-in-C

profile
달려보자

0개의 댓글