Sad Puppy 3 개발자 아지트 :: 개발자 아지트

3장 시간복잡도

시간복잡도는 구현한 알고리즘의 성능을 나타내는 일종의 지표이다. 

입력크기에 대한 연산 횟수의 상한을 의미한다. 

시간복잡도는 낮을 수록 좋다. 

 

  • 입력 크기에 따른 연산 횟수의 추이를 활용해 시간 복잡도를 표현하는 방법을 점근적 표기법이라 한다. 
  • 시간 복잡도를 빅오 표기법으로 나타내려면 데이터 개수 N에 대해 연산 횟수를 일반화한 후 최고차항을 남기고 나머지 차수는 신경쓰지 않는다. 

 

1주차의 공부 주제는 다음과 같다. 

 

  • 코딩 테스트를 준비하기 전에 알아야할 팁
  • 코딩 테스트를 효율적으로 준비하기 위해서 어떻게 해야하는가
  • 코딩 테스트 준비를 위해 프로그래머스를 적극 활용하기

 

코딩 테스트를 준비하기 전에 알아야할 팁

1) 코딩테스트 준비를 위한 사이트 선정 

 

코딩테스트의 문제들을 풀기위한 사이트를 선정해야한다. 

선정 기준은 다음과 같은 기준을 고려하면 좋다. 

  • 타인의 풀이를 볼 수 있는가?
  • 내가 생각한 테스트 케이스를 추가할 수 있는가? 

테스트에 필요한 코드를 작성하기 전에 문제 분석 단계에서 테스트 케이스를 고려할 때, 충분히 생각하여 테스트 케이스를 추가하여 공부하는 것이 좋다. 

 

 

2) 문제 풀다 막힐 경우에 취해야할 태도

 

코딩테스트 준비를 위해 문제를 풀 때, 문제를 다 풀지 못했을 경우가 생길 수 있다. 

이런 경우 상실감에 그냥 다른 문제 풀이로 넘어가거나, 그날 공부를 접지 말고 이것을 생각해봐야한다. 

 

내가 이 문제를 풀기위해서 어디까지 생각을 했는가?

 

이것을 생각하고 어떤 알고리즘을 적용하려고 했는지, 그렇게 생각한 근거는 무엇인지, 어떻게 구현하려고 했는지를 자신만의 공간에 기록해두는 것이 좋다 .

 


3) 나무보단 숲을 보자 = 코딩 테스트 준비를 위해 한정된 시간안에 효율적으로 준비를 마치자 

 

효율적으로 코딩테스트를 준비하기 위해서 문제 풀 때, 문제가 잘 안풀려서 1시간 이상 넘어가는 경우가 있다.

이럴 경우, 시험 보듯 공부하는 것이 좋다. 아무리 잘 안풀리는 문제라도 1시간 이상 고민하게 되면 코딩 테스트라는 시험 준비 자체에 효율이 떨어진다. 1시간이 넘어가면 다른 사람의 코드를 참고하든 하는 것이 좋다. 

 

4) 코딩 테스트 준비에 있어서 요행을 바라는것은 도둑놈 심보

 

짧은 시간 공부해서는 절대로 코딩 테스트에 통과할 수 없다. 

통상적으로 코딩 테스트는 최소 한 달에서 두 달 정도를 매우 집중해서 공부해야한다. 

 

5) 개념이나 원리 공부후 자신만의 언어로 개념 정리 하면 도움이 된다. 

 

코딩 테스트를 효율적으로 준비하기 위해서 어떻게 해야하는가

1)자신이 가장 잘 할 수 있는 프로그래밍 언어를 선택하자. 

2)문제 분석하는 방법을 연습하라 

3)의사 코드로 설계하는 연습을 몸에 익히자

4)문제를 푸는 사이트의 환경이 실제 시험 장소에서 제공하는 환경과 비슷한 프로그래머스 사이트 사용을 추천한다. 

'CodingTest > 자료구조 & 알고리즘' 카테고리의 다른 글

그래프(DFS, BFS)  (0) 2024.04.19
그래프 최단 경로 구하기(다익스트라)  (0) 2024.04.08
문제풀이시 시간복잡도 체크  (1) 2024.03.08
[2주차]3장 시간복잡도  (0) 2024.01.24
게시판의 목차  (0) 2024.01.24

코딩테스트는 Python언어로 준비할 예정이다. 

나는 벼락치기를 막고, 꾸준함을 효과적으로 지속하기 위하여, 스터디에 참가했다. 

이 게시판에서는 장 별로 공부한 내용을  정리한 게시글을 업로드할 예정이다. 


1주차
01. 코딩 테스트 효율적으로 준비하기 

02. 프로그래머스 완벽 활용 가이드

 

2주차

03. 알고리즘의 효율 분석

04. 코딩 테스트 필수 문법

05. 배열

 

3주차 

06. 스택

07. 큐

 

4주차 

08. 해시

09. 트리

 

5주차

10. 집합

11. 그래프

 

6주차

12. 백트래킹

13. 정렬

 

7주차

14. 시뮬레이션 

15. 동적 계획법

 

8주차

16. 그리디

 

문자열

 

  • 문자열 표현법
  • UTF-8 구조
  • 문자열 동작 원리 

 

go언어에서는 UTF-8 문자코드를 사용함(한글이나 한자 사용에 문제 없음)

UTF-8문자 코드는 유니코드로써, 가변 길이 문자 인코딩 방식임

 

 

go언어에서 문자열은 큰따옴표나 백쿼드(`)로 묶어 표시한다. 

 

package main

import (
	"fmt"
)

func main() {
	str := "내 이름은 \t'김정자'\n"

	fmt.Println(str)

	str2 := `"야 내 이름은", 김 정 자 야. ^^`
    // 백쿼트로 문자열을 묶을 경우, 특수 문자가 동작하지 않음
	fmt.Println(str2)
}

// 큰 따옴표를 통해 여러 줄 표현시 \n을 사용해야함
// 백쿼트를 통해 여러 줄 표현시 아무것도 필요 없음

 

 

UTF-8문자 코드 

 

다국어 문자를 지원하고, 자주 사용되는 영문자나 숫자 등 일부 특수 문자를 1바이트로 표현함. 

(그 외의 다른 문자들은 2~3바이트로 표현)

ANSI 코드와 1:1대응이 가능하여, 바로 변환된다는 장점이 있음 

 

 

문자를 사용하는 경우 - rune 타입 사용

 

go에서는 문자 하나만 표현할 때 rune타입을 사용함

UTF-8 문자 값은 3바이트로 이루어져 있음, Go에서 문자 표현시 3바이트 정수 타입은 제공하지 않으므로, 4바이트 정수 타입인 int32타입의 별칭 타입인 rune타입을 사용해야함. 

 

package main

import (
	"fmt"
)

func main() {
	var chr rune = '야'
	// 사실 rune 타입은 int32타입과 같은 타입임
	fmt.Printf("%T\n", chr) // int32 출력
	fmt.Printf("%c", chr)   // 야 출력
	fmt.Println(chr)        // 50556 출력(chr 값)
}

 

 

len()으로 문자열이 차지하는 메모리 크기(문자열 크기를 확인)를  할 수 있음

 

package main

import (
	"fmt"
)

func main() {
	// 문자열은 아래와 같이 각 문자의 코드 값의 배열로 나타낼 수 있음 
	runes := []rune{72, 101, 108, 108}
	fmt.Println(string(runes)) // Hell

}

 

 

또한 string타입과 []byte 타입 간 상호 변환이 가능하다. 

 

문자열 순회는 byte단위로 한글자씩 순회하거나,

[]rune 타입으로 변환 후 한 글자씩 순회하거나, 반복문을 통해 순환할 수 있다. 

 

파이썬과 같이 문자열 연산은 +, += 연산자, 같은지 비교시 ==, !=를 사용할 수 있다. 

문자열에 대한 대소 비교시, >, <, >=, <=를 사용하고, 첫 글자부터 하나 씩 값 비교해서 유니코드 값을 비교함 

 

 

문자열 구조

 

type StringHeader struct {
	Data uintptr // 문자열의 데이터가 있는 메모리 주소를 나타냄 = 포인터
    Len int // 문자열의 길이 
}

 

만약 string끼리 대입할 경우, 대입 대상 변수에 기존 변수의 data, len값만 복사됨(= 문자열 자체는 복사되지 않음)

 

Go언어에서 string타입의 문자열의 전체는 변경 가능하지만, 일부만 변경할 수 없다. 

만약, string타입을 슬라이스 타입으로 타입 변경후(기존 문자열의 주소와는 다른 주소를 가리키는 상태로 변경), 일부 문자를 변경한 후 출력한다면 해당 문자열은 변경후 출력 가능함. 

 

문자열을 합산할 경우, 만약 문자열 a와 문자열 b가 있으면, 문자열 a와 b를 합친 문자열 c가 생성됨(a, b, c의 모든 메모리 주소는 서로 다른곳을 가리킴)

문자열 합산이 자주 일어날경우, 메모리가 낭비됨. 

이런 경우 strings패키지의 Builder을 이용하는 방법이 있음.

 

go에서 이렇게 하는 이유는, 버그를 방지하기 위함임 

 

패키지

  • 패키지 정의
  • 외부 패키지 사용하기
  • Go 모듈 알아보기 

 

패키지는 코드를 묶는 최대 단위임.

Go로 만든 모든 프로그램은 패키지를 묶어 만들게됨. 패키지를 통해 함수, 구조체, 상수 등을 외부에서 사용할 수 있음

 

함수를 통해 코드 블록을 묶고, 구조체로 데이터들을 묶고, 패키지를 통해 함수와 구조체 등 그외의 코드들을 묶음.

main 패키지는 다른 패키지들과는 다르게 프로그램의 시작점을 포함한 패키지임. 

 

Go의 프로그램은 main 패키지(필수)하나와 그 외 여러 외부 패키지(선택)으로 구성됨. 

 

 

그 외 여러 외부 패키지는 외부에서 가져다 쓰는 라이브러리와 같다. 

 

패키지 찾는 곳

https://golang.org/pkg/ 

 

Standard library - Go Packages

Discover Packages Standard library Version: go1.22.1 Opens a new window with list of versions in this module. Published: Mar 5, 2024 License: BSD-3-Clause Opens a new window with license information. Jump to ... Directories Directories ¶ Show internal Exp

pkg.go.dev

위 사이트를 통해 표준 패키지 목록 확인이 가능함

 

Go언어에서 많이 사용되는 패키지는 Awesome Go에서 찾아보면 좋다. 

https://github.com/avelino/awesome-go 

 

GitHub - avelino/awesome-go: A curated list of awesome Go frameworks, libraries and software

A curated list of awesome Go frameworks, libraries and software - avelino/awesome-go

github.com

 

 

패키지 사용하기 

 

// import 예약어로 임포트 하고, 원하는 패키지 경로를 따옴표로 묶어 써줌
import "fmt"

 

import {
	"fmt"
    "os"
}

 

이런식으로 다른 패키지를 가져오면 가져온 패키지에서 외부로 노출하는 함수나 구조체, 변수, 상수 등을 사용할 수 있음

 

외부 노출 여부는 이름의 첫 글자가 대문자면 노출한다는 의미이다. 

패키지 명은 가져오는 패키지 경로의 가장 마지막 폴더이름이다. 

 

fmt.Println("hi")

 

패키지를 통한 호출은 .을 통해 할 수 있다. 

 

package main

import ( // 둘이상의 패키지는 소괄호로 묶어줌
	"fmt"
	"math/rand" // 경로가 있는 패키지 사용
)

func main() { 
	fmt.Println(rand.Int()) // 경로가 있는 패키지 접근시 마지막 폴더명만 사용함
}

 

 

패키지 명이 겹칠경우, 별칭을 주어 구분한다. 

 

import {
	"text/template" // template 패키지
    "html/template" // 역시 같은 이름의 template 패키지
}

 

별칭을 쓸 경우 패키지명 앞에 사용

 

import (
	"text/template" // template 패키지
    htemplate "html/template" // 별칭 htemplate, 해당 패키지는 htemplate로 사용 가능함
)

 

사용하지 않는 패키지를 포함해야 하는 경우, 패키지 앞에 _ 을 붙여준다. 

 

 

import로 패키지를 포함시킬 경우, go build를 통해 파일을 빌드할 때, 해당 패키지를 찾아서 포함한 후 실행 파일을 생성함

해당 패키지는 다음과 같은 경로에서 확인할 수 있다. 

 

1. Go 설치 경로

2. GOPATH\pkg

3. 현재 폴더 아래의 위치

 

Go 모듈

Go 모듈은 Go 패키지들을 모아놓은 Go 프로젝트 단위이다. 

Go 1.16 버전부터 Go모듈 사용이 기본이 됨.

이전까지의 Go 코드는 GOPATH/src 폴더 아래에 있어야 했지만, Go 모듈이 기본이 되면서 모든 Go 코드는 Go모듈 아래에 위치해야

 

go build를 위해서, Go 모듈 루트 폴더에 go.mod파일이 있어야함.

go.mod 파일에는 모듈 이름, Go 버전, 필요한 외부 패키지 등이 명시되어 있음 

 

Go언어에서 go build를 통해 실행 파일을 만들 때, 

 go.mod와 go.sum(외부 저장소 패키지 버전 정보를 담고있음) 파일을 통해 외부 패키지와 모듈 내 패키지를 합쳐서 만듦

 

go mod init [패키지명]

 

위 명령어 실행시 go.mod파일이 생성됨 

 

히히 실습 완료

 

 

패키지를 만들때

Go언어에서 패키지명을 만들때, 쉽고 간결하게 이름 짓기를 권장한다. 

모든 문자를 소문자로 할 것을 권장한다. 

 

패키지 초기화

패키지를 임포트 할 때, 컴파일러는 패키지 내 전역변수를 초기화 한다. 그 후, 패키지에 init()함수가 있다면 호출해서 패키지를 초기화 한다. init()함수는 입력 매개변수가 없고, 반환값이 없는 함수여야 한다. 

 

 

해당 글은 [Tucker의 Go 언어 프로그래밍] 15장~17장을 읽고 공부한 글입니다. 

 

해당 글은 MacOS(M1) 환경을 기준으로 작성되었음. 

배열

C에서 사용하는 배열개념이랑 같다. 

배열에 담을 수 있는 요소는 처음에 초기화 한후에 고정됨(파이썬 리스트로 구현한 배열이랑 다름)

 

Go에서 배열 선언

var 변수명 [요소 개수]타입

// 5개의 float64타입의 원소를 가진 t라는 이름을 가진 배열 선언
var t [5]int

 

 

이렇게 선언만 하고 별도의 초깃값을 짖어하지 않으면 각 요솟값은 int 타입의 기본값 0으로 초기화 됨 

 

예제 작성전에 참고해야 할 것 

 

더보기

Go 1.16 버전부터 Go 모듈 사용이 기본이 됐음. 

 

Go 1.16 버전 이전에는 Go 모듈을 만들지 않는 코드는 모두 $GOPATH/src 폴더 밑에 있어야 했지만,

Go 모듈 사용이 기본이 되면서 모든 Go코드는 Go 모듈 밑에 있어야 한다. 

 

따라서, 모든 예제를 빌드하기 전에 $go mod init 을 통해 Go 모듈을 만들어 줘야함. 

 

아래의 명령어를 통해 대상 파일(폴더)에 대한 Go 모듈을 만들어준다. 

$go mod init [파일위치/파일명]

 

아래의 명령어를 통해 실행 파일을 만들어준다.

$go build 

 

파일을 실행한다. 

$./[파일명]

 

직접적인 사용 방법 예시

 

1. 배열을 반복문을 통해서 출력하기 

package main

import "fmt"

func main() {
	var t [5]float64 = [5]float64{23.0, 25.9, 22.8, 24.9, 21.2}

	for i := 0; i < 5; i++ {
		fmt.Println(t[i])
	}
}

 

 

배열 변수 선언 및 초기화 

var nums [5]int

 

별도 초기값을 정하지 않음으로, 요소값은 int타입의 기본값인 0으로 초기화됨 

 

month := [3]string{"jan", "feb", "mar"}

 

 

배열의 일부만 초기화 할 경우, 나머지 값들은 해당 타입의 기본값으로 초기화됨

var temps [5]float64{11.1, 22.2}

 

 

배열 요소 개수 생략하는 방법은 다음과 같음

 

x := [...]int{10, 20, 30}

 

주의할점. 배열을 선언할 때 개수는 항상 상수를 사용해야함. 

 

배열은 인덱스를 통해 읽고 쓸 수 있음

 

range를 통한 배열 순회

package main

import "fmt"

func main() {
	var t [5]float64 = [5]float64{23.0, 25.9, 22.8, 24.9, 21.2}

// i는 인덱스 v는 값. 
// 만약 인덱스가 필요없는 경우 _를 이용해 인덱스를 무효화 할 수 있음
	for i , v := range t{
		fmt.Println(i, v)
	}
}

 

 

배열의 요소는 연속된 메모리로 되어있음 (차원이 중첩되어도 마찬가지임)

 

요소 위치 = 배열 시작 주소 + (인덱스 * 타입 크기)

 

 

배열 크기 = 타입크기 * 항목 개수

 

구조체

  • 구조체 선언
  • 구조체 변수 사용
  • 구조체 특징
type  타입명  struct {
	필드명 타입 
    ...
    필드명 타입
}

// type 키워드를 사용함으로서 새 사용자 정의 타입을 정의함 
// 타입의 종류 struct 
// 중괄호{ } 안에 해당 구조체에 속한 필드들을 적어줌 
// 각 필드는 필드명과 타입을 기입

 

예시

type Sandwitch struct {
	Bread string
    Source string
    Price  int
}

 

Sandwitch 타입 구조체 변수 선언 

 

var egg Sandwitch

 

 

각 필드에 대한 접근은 . 연산자를 통해 접근한다. 

 

package main

type Sandwitch struct {
	Price  float64
	Bread  string
	Source string
	Cnt    int
}

func main() {
	var eggSandwitch Sandwitch
	eggSandwitch.Bread = "Wheat"
	eggSandwitch.Price = 6400.1
	eggSandwitch.Source = "salt"
	eggSandwitch.Cnt = 1
}

 

구조체 변수 초기화 

 

초깃값을 생략하면 모든 필드가 기본값으로 초기화 됨

 

다음과 같이 모든 필드를 순서대로 초기화 할 수 있음

 

var sandwitch Sandwitch = Sandwitch{ 100.0, "flat", "salt", 1 }

 

만약 필드 일부만 초기화할 경우, 초기화 되지 않은 나머지 변수에는 기본값으로 초기화 됨

 

구조체를 포함하는 구조체

 

type Sandwitch struct {
	Price  float64
	Bread  string
	Source string
	Cnt    int
}

type VIPSandwitch struct {
	SandwitchInfo Sandwitch
	Gifts string
}

 

 

포함된 필드 이용시 . 하나를 통해 접근이 가능함

package main

import "fmt"

type Sandwitch struct {
	Price  float64
	Bread  string
	Source string
	Cnt    int
}

type VIPSandwitch struct {
	Sandwitch
	Gifts string
    Price float64 // 필드가 중복되는 경우 호출할 때, 
}

func main() {
	sandwitch := Sandwitch{6400.1, "Wheat", "salt", 1}
	vip := VIPSandwitch{
		Sandwitch {100.0, "flat", "oliveOil", 2},
		"Cookie",
	}

	fmt.Printf("샌드위치 %s", sandwitch.Bread)
	fmt.Printf("샌드위치 %s", vip.Bread)
    fmt.Printf("샌드위치 가격: %d", vip.Sandwitch.Price)
    fmt.Printf("vip 샌드위치 가격: %d", vip.Price)
    

}

 

구조체 크기

구조체 변수가 선언되면, 컴퓨터는 구조체 필드를 모두 담을 수 있는 구조체의 메모리 공간을 할당함

 

type Student struct{
	Age int
    Score float64
}

 

 

만약 위와같은 구조체 Student가 정의 되어 있다고 했을때, 

 

var student Student

 

 

int 타입 Age는 8바이트 , float타입 Score는 8바이트로 총 16바이트의 크기가 필요함

student는 16바이트의 크기를 가지는 구조체임 

 

구조체의 객체는 대입연산자만으로도 필드값을 복사할 수 있음

Go 내부에서는 필드 각각이 아닌 구조체 전체 필드가 한 번에 복사됨.

 

메모리 정렬

 

메모리 정렬은 데이터에 접근할 때, 접근 효율을 높이기 위해 메모리를 일정 크기로 정렬하는 것을 말한다. 

 

레지스터는 실제 연산에 사용되는 데이터가 저장되는 곳인데, 레지스터 크기가 4바이트면 32비트 컴퓨터고,

레지스터 크기가 8바이트면 64비트 컴퓨터이다. 

 

레지스터 크기가 8바이트란 말은, 연산 한번에 8바이트 크기를 연산할 수 있다는 것이다. 

따라서 데이터가 레지스터 크기과 같은 크기로 정렬돼 있으면, 데이터를 읽어올 때 효율적이다. 

 

만약 

type Student struct {
	Age int32 // 4바이트 
    Score float64 // 8바이트
}

var student Student

 

인 경우, 

student의 메모리 주소가 240번지이면, Age의 시작 주소 또한 240이 된다. 

이때 Age의 바로 뒤에 Score의 메모리 주소를 할당하면 시작은 244가 된다. 

244는 8의 배수가 아니므로 메모리 접근 시에 효율이 떨어진다. 

 

이 때문에, 프로그램 언어에서 Student 구조체 할당 시, Age와 Score을 4바이트 띄워서 할당한다. 

그러면 Score의 메모리 주소 시작은 248이 된다. 

 

이런 방법을 메모리 패딩이라고 한다. 

 

메모리 낭비를 줄이기 위해서는 8바이트 보다 작은 필드는 8바이트 크기를 고려해서 몰아서 배치하는 것이 효율적이다. 

 

메모리 공간이 작인 임베디드의 프로그램이라면 패딩을 고려하는게 좋다. 

 

프로그램에서 구조체는 관련 데이터를 묶어 응집도를 높이고 재사용성을 증가시킨다. 

 

*결합도는 모듈간 상호 의존 관계의 높은 정도를 말하고, 응집도는 모듈의 완성도를 말하며 모듈 내부에 모든 기능이 하나의 목적에 맞게 충실하게 모여있는지를 나타내는 용어이다. 

 

 

포인터

  • 포인터 정의
  • 포인터 사용법
  • 인스턴스 개념

 

포인터는 메모리주소를 값으로 갖는 타입이다.

float 타입 변수 b가 있을때, b는 메모리에 저장되어있고, 메모리 주소는 속성으로 갖는다. 

그리고 변수 b의 주소가 0x0010번지일 때, 이 주소값은 숫자다. 

이 값은 다른 변수의 값으로 사용될 수 있다.  남의 변수 주소를  값으로 가질 수 있는 변수를 포인터 변수라고 한다. 

 

포인터 변수는 주소값만 값으로 가질 수 있는 변수다.

 

p = &b 

// p 포인터 변수는 변수 b의 주소값을 값으로 갖도록 대입하고 있다. 
// 이런 상태를 포인터 변수 p가 변수 b를 가리킨다고 말할 수 있다. 
// 메모리 주소를 값으로 가지고, 메모리 주소 값을 통해 메모리 공간을 가리키는 타입을 포인터 타입이라고 한다.

 

 

포인터는 여러 포인터 변수가 하나의 메모리 공간을 가리킬 수 있다. 

포인터가 가리키는 (메모리 주소가 가리키는) 공간에 있는 값을 읽거나 수정할 수 있다. 

 

포인터 변수 선언

 

var p *int

// 위와 같이 포인터 변수는 가리키는 데이터 타입 앞에 *을 붙여 선언한다.

var p2 *Student
// Student 구조체를 가리키는 경우 포인터 변수 선언 방법은 위와 같다.

 

 

포인터 변수에 값을 넣기 위해서는?

 

var b int
var p *int
p = &b

// 3번 라인은 b의 메모리 주소를 p 포인터 변수에 대입하는 것이다.

 

포인터 변수 p를 통해 b의 값을 변경하는 방법은?

 

*p = 100

 

 

// 포인터 변수 값을 출력하기 위해서는 ?

fmt.Printf("나는 포인터 변수 P이며, 값은 %p", p)

// 포인터 변수가 가리키는 메모리 공간의 값을 출력하기 위해서는 ?

fmt.Printf("나는 포인터 변수 P이며, 내가 가리키는 메모리 공간의 값은? %d", *p)

 

 

포인터 변수값 비교

 

포인터가 같은 메모리 공간을 가리키는지 확인하기 위해서 == 연산자를 사용할 수 있다. 

 

만약 같으면 true를, 같지 않으면 false를 출력한다. 

 

포인터의 기본값 nil

 

포인터 변수값을 초기화하지 않으면 기본값은 nil이다. 

nil은 따지고보면, 0이다. 그러나 정확한 의미는 어떠한 메모리 공간(주소)도 가리키고 있지 않다는 의미이다. 

 

var p *int
if p !=nil {
	// 포인터가 유효한 메모리 공간을 가리키고 있지 않아요!
}

 

 

포인터를 쓰는 이유

 

변수를 대입하거나, 함수에 인자를 통해 값을 전달할 때는 항상 값을 복사하여 활용한다. 

값을 복사한다는 의미는 메모리가 이중으로 사용된다는 의미이다. 

즉, 메모리가 필요 이상으로 많이 쓰인다는 이야기이다. 또 큰 메모리 공간을 복사할 경우 성능 문제가 발생할 수 있다. 

추가로 기존 값의 변경을 원할경우, 값이 복사되어 활용되므로, 변경 사항을 반영할 수 없다. 

 

다음과 같은 상황은 포인터를 메모리 낭비 없이 적절하게 사용하는 사용하는 경우이다. 

 

package main 

import "fmt"

type Data struct {
	value int
    data [200]int
}

func ChangeData(arg *Data) {
	arg.value = 100
    arg.data[100] = 100
}

func main(){
	var data Data
    
    ChangeData(&data) // 이때, 데이터 변수 값이 아닌 변수의 메모리 주소만 인수로 전달된다. 
    // 메모리 주소는 8바이트 숫자 값이다. 따라서 8바이트만 복사된다. 
    fmt.Printf("value = %d\n", data.value)
    fmt.Printf("data[100] = %d\n", data.data[100])
}

// 출력 결과 
// value = 999
// data[100] = 999

 

 

Data 구조체를 생성해 포인터 변수 초기화해보기 

 

구조체 변수를 따로 생성하여 포인터 변수에 구조체 변수를 가리키게 하는 방법

 

var data Data
var p *Data = &data

 

 

구조체 변수를 따로 생성하지 않고, 바로 포인터 변수에 구조체를 생성해 주소를 초기값으로 대입하는 방법은 다음과 같다. 

 

var p *Data = &Data{} // Data구조체를 생성해 초기화하는 방식
// &Data{} 는 데이터 구조체를 만들어 주소를 반환하는 것이다.

 

이 부분에서 이해가 안되겠지만, 일단 인스턴스로 넘어가길 권장한다. 

 

인스턴스

 

인스턴스는 메모리에 할당된 데이터의 실체이다.

var data Data

 

위는 Data 타입 값을 저장할 수 있는 메모리 공간(data)을 할당한다. 

 

이 상태에서 데이터 타입의 포인터 변수를 성넌하여 data 변수의 주소 값을 대입시키면 다음과 같다. 

var data Data
var p *Data = &data

 

 

이를 통해 p가 생성될 때, 새로운 Data의 인스턴스가 생성됐다고 보지 않고 기존의 data 인스턴스를 가리킨다고 본다. 

즉, 위 코드에서 Data 인스턴스의 총 개수는 한 개이다.

 

 

인스턴스를 따로 생성하지 않고, 바로 인스턴스 생성과 동시에 그 주소를 포인터 변수에 초기 값으로 대입하는 코드는 다음과 같다. 

 

var p *Data = &Data{}

 

이러면 Data인스턴스가 만들어지고, 해당 주소를 포인터 변수 p가 가리키게 된다. 

 

 

만약 다음과 같은 경우, 

 

var p1 *Data = &Data{}
var p2 *Data = p1
var p3 *Data = p1

 

포인터 변수가 아무리 많아도, p2, p3도 p1의 포인터변수 값을 갖는다.

따라서 p1, p2, p3 모두 공통된 인스턴스를 가리키게 된다. 즉, 포인터 변수가 많다 해도 인스턴스가 추가로 생성되지 않는다. 

 

 

인스턴스는 데이터의 실체

 

포인터를 이용해 인스턴스에 접근할 수 있다. 

구조체 포인터를 함수 매개변수로 받는다는 것은 구조체 인스턴스로 입력을 받겠다는 것이다. 

 

new() 내장 함수

 

포인터 값에 대해 따로 변수 선언을 하지 않고, 초기화 하는 또 다른 방법이 있다. 

 

p1 := &Data{} // 기존의 &를 사용한 초기화 
var p2 = new(Data) // new를 통한 초기화

 

 

인스턴스가 사라지는 타이밍

 

메모리는 한정되어 있으므로, 데이터가 할당되면 적절한 타이밍에 사라져야 프로그램이 계속 동작할 수 있다. 

Go에서는 데이터 할당 후 적절한 타이밍에 사라지게 하는 기능이 있다. 이를 가비지 컬렉터(메모리 청소부)라고 한다. 

이 기능이 일정 주기로 메모리에서 필요없는 데이터를 정리한다. 

 

Go에서는 아무도 찾지 않는 데이터를 사용하지 않는 데이터라고 인식하여 처리한다. 

 

예를들면 다음과 같은 상황이 있다. 

 

func TmpFunc(){
	s := &Student{} // s포인터 변수가 선언되고, Student인스턴스도 생성된다. 
    s.Age = 1
    fmt.Println(u)
}

// 이후 s는 사라지고, Student 인스턴스도 사라진다.

 

 

메모리는 엄청 크기 때문에, 가비지 컬렉터가 한번 움직일때는 큰 메모리를 전부 검사해서 쓸모 없어진 데이터를 정리하는데 성능이 많이 필요하다. 따라서, 가비지 컬렉터 사용시 메모리 관리는 잘 될지 몰라도 성능은 떨어질 수 있다. 

 

Go에서의 스택 메모리 힙 메모리 

 

대체로 프로그래밍 언어에서 메모리를 할당 할 때, 스택 메모리 영역이나 힙 메모리 영역을 사용한다. 

효율적인 면에서는 스택 메모리 영역이 힙 메모리 영역보다 우세하다. 

스택 메모리 영역에서 메모리를 할당할 경우, 스택 메모리는 함수 내부에서만 사용가능하다. 

 

C/C++에서는 malloc()함수를 직접 호출해 힙 메모리 공간을 할당하고,

Java에서는 기본 타입은 스택에, 클래스 타입은 힙에 할당한다. 

 

 

Go의 경우, 탈출 검사를 통해 어느 메모리에 할당할지 결정한다. 

함수 외부로 노출되는 인스턴스는 함수가 종료돼도 사라지지 않는다. 

함수 외부로 노출되는지 마는지의 여부는 Go에서 자동으로 검사해서 스택에 할당할지 힙에 할당할지 결정한다. 

 

Go에서 스택 메모리는 계속 증가되는 동적 메모리 풀이다. 

C/C++ 처럼 일정 크기를 갖지지 않아서 메모리 효율이 좋고, 재귀 호출로 인해 스택 메모리가 고갈되거나 하는 문제도 발생하지 않는다. 

 

 

 

 

 

해당 글은 [Tucker의 Go 언어 프로그래밍] 12장~14장을 읽고 공부한 글입니다. 

 

상수

 

상수는 변하지 않는 고정 값이다 .

상수는 자주 쓰는 고정값에 이름을 정해줘서 쉽게 사용할 수 있게 한다. 

상수를 코드값으로 사용할 수 있으며, iota를 이용해 증가하는 상수를 쉽게 선언할 수 있다. 

상수를 타입 없이 선언할 경우, 타입이 다른 여러 변수에서 해당 상수를 사용할 수 있다. 

 

 

if문(조건문)

Go에서 사용하는 조건문은 C에서 사용하는 조건문과 사용방법이 비슷하다. 

 

단 한가지 다른 점이 있다.

C에서 do while 문과 비슷한 조건문을 사용하는 방법이 하나 존재한다. 

 

if 초기문; 조건문 {
	. . .
    // 초기문 먼저 실행 후 조건을 검사함
    
}

 



Switch문

 

Go의 Switch문은 파이썬에서 Switch문과 사용법이 비슷하다. 

기본 형태는 다음과 같다. 

 

switch 비교값 {
case value1:
 // 비교값이 value1과 같을 경우 실행 
 . . .
case value2:
 // 비교값이 value2과 같을 경우 실행 
 . . .
default:
 . . .
 // 만족하는 비교값이 없는 경우 실행하며,default문의 경우 생략 가능 
}

 

 

Go 에서 Switch 문은 비교값을 기입하지 않을 경우, 컴파일러는 항상 true로 인식한다. 

 

if문과 같이 switch문에도 초기문을 선언하여 먼저 실행시킨 후, 비교값을 확인하게 하는 방법이 존재한다. 

 

 

switch 초기문; 비교값 {
case value1
	. . .
    
}

 

 

For 문

 

Go에서는 for문을 C에 비해서 거의 자유자재로 사용할 수 있다. 

C언어와 같이 for문은 조건이 참이면 코드 반복을 수행하며, continue, break를 사용한다. 

 

Go에서 For문은 C에서 for문 선언하듯이 같고, 

 

초기문을 생략하거나

초기문 혹은 후처리를 생략하거나

초기문, 조건문, 후처리문 모두 생략할 수 있다. 이런경우 무한반복하게된다. 

 

 

 

 

해당 글은 [Tucker의 Go 언어 프로그래밍] 8장~11장을 읽고 공부한 글입니다. 

 

Go언어란?

홈페이지 주소: golang.org

온라인 Go 언어 컴파일러 : play.golang.org

  • Go 언어의 역사
  • Go 언어의 특징
  • Go 언어의 쓰임새

 

Go언어는 구글에서 로버트 그리스머, 롭 파이크, 켄 톰슨이 이끌어 만들어  2009년 발표된 오픈소스 프로그래밍 언어이다. 

즉, 무료이며, 언어 발전에 기여 가능하다. 

해당 언어는 심플하며, 성능이 좋다. 

 

해당 언어는 범용 언어로, 다양한 용도로 사용되지만

특히 백엔드 서버와 시스템 개발에 적합하며, 강력한 동시성 프로그래밍을 지원함

그 이유는 성능이 좋기 때문이다. 

 

특징 요약

 

  • 클래스: 없음, 그러나 메서드를 가지는 구조체를 지원함
  • 상속: 없음
  • 메서드: 있음, 구조체는 메서드를 가질 수 있음
  • 인터페이스: 있음, 상속은 없음
  • 익명 함수: 있음, 함수 리터럴이라는 이름으로 제공함
  • 가비지 컬렉터: 있음, 고성능 가비지 컬렉터를 제공함
  • 포인터: 있음
  • 제네릭 프로그래밍: 없음
  • 네임스페이스: 없음 

 

코드 실행 과정

 

  1. 폴더 생성
  2. .go파일 생성 및 작성
  3. Go 모듈 생성
  4. 빌드
  5. 실행 

 

폴더 생성

Go 언어에서 모든 코드는 패키지 단위로 작성됨.

같은 폴더에 위치한 .go 파일은 모두 같은 패키지에 포함되며, 폴더명이 패키지명이 됨.

 

예를들어서, goproject/hi/plus

 

hi 폴더에 든 .go 파일은 hi 패키지에 속하고, plus 폴더에 든 .go파일은 plus 패키지에 속함

 

Go 코드는 .go 확장자의 파일임 

Go 모듈 생성

 

Go 1.16 버전 이후로 Go 모듈이 기본으로 적용된다. 

따라서 모든 Go 코드 빌드 전에 모듈 생성이 필요하다. 

 

이때, 모듈 생성은 아래의 명령으로 실행한다. 

$ go mod init 모듈명

# 예를 들면

$go mod init goproject/hi

 

모듈 생성시, go.mod 파일이 생성되고, 해당 파일에는 모듈명, Go 버전, 필요한 패키지 목록에 대한 정보가 담겨있다. 

 

빌드

 

$ go build

 

위 명령어를 통해 Go 코드를 기계어로 변환하여 실행 파일을 만든다. 

 

GOOS와 GOARCH 환경변수 조정을 통해 다른 운영체제 및 아키텍처에서도 호환 가능한 실행 파일 제작이 가능하다. 

(터미널에서 $ go tool dist list 명령을 통해 호환 가능한 운영체제 및 아키텍처 목록을 확인할 수 있다. )

 

 

기본 코드에 대한 구조

 

package main 
// 패키지 선언: 해당 코드가 어떤 패키지에 속하는지 알려줌 
// 패키지는 코드의 묶음이며, 여러 기능을 제공함
// Go 파일의 코드는 항상 패키지 선언으로 시작해야함
// 1번 라인은 main 패키지에 속한 코드임을 컴파일러에 알려주는 역할을 함

// main 패키지는 프로그램 시작점을 포함하는 중요한 패키지임

import "fmt"
// fmt 패키지를 가져오겠다는 의미를 가짐
// fmt는 패키지 표준 입출력을 다루는 내장 패키지임

func main(){
	fmt.Println("Hi World")
	// fmt.println()는 표준 출력으로 문자열을 출력하는 함수임.
    // 표준 출력이란 터미널 화면을 말함. 
    // Go언어 프로그램은 main()함수 부터 시작되고, main() 함수가 종료되면 프로그램이 종료됨.
}

// Go 언어에서는 외부로 노출되는 함수 앞에는 함수명으로 시작하는 주석을 달아서 함수를 설명하는 것을 
// 코딩 규약으로 권장함

/* 여러줄 

주석*/

 

 

 

 

변수

 

  • 변수 선언
  • 변수 속성
  • 타입 변환
  • 숫자 표현

 

 

변수란 값을 저장하기 위한  메모리 공간을 가리키는 것이다. 

변수는 이름, 값, 타입, 주소 속성을 가지며, 변수 간 값의 전달은 항상 복사로 일어난다. 

 

컴퓨터에서 프로그램의 코드는 메모리의 데이터를 언제 변경할지 나타낸 문서이다. 

따라서, 프로그래밍은 메모리에 있는 데이터 조작이 핵심이다. 

이를 변수를 이용하면 쉽게 할 수 있다. 

 

변수 사용을 위해 아래와 같이 선언할 수 있다. 

코드에서 변수를 선언을 한다는 것은 컴퓨터에서 코드 실행시 변수를 위한 메모리 공간을 할당한다는 의미를 내포한다. 

따라서, 변수는 이름, 값, 타입, 주소를 속성으로 가진다. 

 

package main

import "fmt"

func main(){
	var tmp int = 11

}

 

위 코드와 달리 쉽게 변수 선언을 하기 위해 초깃값을 생략하거나 변수 타입을 생략하는 등 다양한 선언이 가능하다. 

 

변수의 타입은 처음 변환한 타입에서 변환할 수 있다. Go언어는 다른 언어들과 달리 타입에 대한 자동 변환을 지원하지 않는다. 

따라서 연산하거나 대입을 위한 타입 변환을 수동적으로 해줘야한다. 

 

변수 타입 중 숫자 타입은 변수 크기에 따라서 표현할 수 있는 값의 범위가 다르다. 

실수 타입은 유효 자릿수가 정해져 있으므로, 잘 확인하고 변수를 사용해야한다. 

 

텍스트 입출력 패키지 fmt

 

제목과 같이 Go에서는 fmt 패키지를 통해 데이터 표준 입출력을 할 수 있다. 

이때, 표준 입출력이란 터미널에서 데이터를 출력하고 입력하는 것을 말한다. 

 

표준 출력 함수로 Print(), Printf(), Println()을 사용할 수 있다. 

표준 입력 함수로 Scan(), Scanf(), Scanln()을 사용할 수 있다. 

 

변수를 출력할 때, 서식 문자를 활용해 최소 출력 너비 및 소숫점 이하로 몇개의 숫자까지 표현하는지 등 다양한 형식으로 출력할 수 있다. 

 

서식 문자 %v를 사용할 경우, 모든 타입의 기본 서식으로 출력할 수 있다. 

 

입력받을 때, 에러 발생시 표준 입력 스트림을 지우는 것이 좋다. 

 

 

 

연산자

 

C와 같이 산술 연산자로 사칙 연산, 비트 연산, 시프트 연산자가 있다. 

 

만약 정수 타입 숫자에서 경계 값에서 연산할 경우 주의가 필요하다. 

 

실수 타입에 대해 값의 같음을 확인하는 == 연산자 사용시, 정상적으로 동작하지 않을 수 있다. 

 

복합 대입 연산자를 통해 연산을 간편하게 줄여 사용할 수 있다. 

 

 

함수

 

함수는 한편으로는 코드 블럭 단위이다. 

 

함수를 사용하면 코드 재사용을 용이하게 할 수 있다. 

 

반환값이 여러개인 함수를 멀티 반환 함수라 하며, 함수 선언할 때, 반환 타입을 소괄호로 묶어 표시한다. 

 

함수는 다음과 같이 정의할 수 있다. 

 

func Sub(a int, b int) int {
	return a - b
}

 

 

 

 

 

해당 글은 [Tucker의 Go 언어 프로그래밍] 3장~7장을 읽고 공부한 글입니다. 

 

 

프로그램은 무엇인가?

컴퓨터가 수행해야 될 명령과 순서를 쓴 문서이다.

글이 어떤 언어로 되어있냐에 따라 다르다.

어떤 프로그래밍 언어냐 따라 다르다.

 

컴퓨터← 글로 된 문서(프로그램)을 받는다.

 

컴퓨터→ 내가 이걸 읽을 수 있는가?

 

인간의 언어 구조를 컴퓨터로 이해한다는 것은 어렵다.

프로그램을 컴퓨터가 바로 읽을 수는 없다.

 

컴퓨터는 프로그램 언어를 0, 1로 변환해서 읽어야 한다.

 

명령어 = OP(Operater)

 

명령어에 숫자를 넣은 것은 opcode다

 

ADD 3, 4   와 같은 것을 어셈블리어라고 한다. 

 

언어를 변환시켜줄 필요가 있다. 

 

 

고수준언어를 컴퓨터 기계어로 변환하는 변환기는 컴파일러라고 한다.

 

결국 프로그램은 실행파일이라고 한다. == .exe파일

 

코딩해서 컴파일 하는 과정을 빌드(변환)이라고한다. 이렇게 하면 기계어로 바뀜

 

역변환도 가능하다.

 

해커들은 기계어만 가지고도 어떤 프로그램인지 알 수 있고, 고칠 수 있다.

그러나 굉장히 어렵다. 그러나 가능하다.


프로그램 언어란?

 

고급언어/저급언어로 나뉜다. 

 

고급언어에는 c, c++, go와 같은 컴파일 언어와 Java, C#, Python, javascript와 같은 동적 언어가 있다. 

 

c, c++, go

  • 컴파일 언어
  • 코딩→ 빌드→ 기계어
  • 먹기전에 돈까스 다 잘라놓고 먹는사람
  • 실행 속도가 빠름

 

Java, C#, Python, javascript

  • 동적 언어
  • 코딩→ 빌드→
  • 기계어가 나오지 않음
  • 먹을때 하나씩 자르면서 먹는 사람
  • 실행 속도가 좀 느리다.


C, C++, Go언어의 특징

  • 컴파일 언어
  • 프로그래밍 과정
    • 코딩→빌드(컴파일러라는 프로그래머가 코딩한 문서를 기계어로 변환함)→ 실행파일(기계어1, 0)
    • 대부분의 고수준 언어는 역변환 과정이 불가능하다.

C#, Java, Python언어의 특징 

  • 동적 언어
  • 코딩→ 빌드 필요(Java, C#)→중간 언어= 문서임→ 기계어로 변환⇒ 실행 중간에 변환을 하는 것을 동적 언어라고 한다.

이런 구분이 왜 생겨났을까?

 

컴파일 언어는 속도가 빠르다.

동적 언어는 중간 중간 변환 하기 때문에 속도가 느리다.

 

컴파일 언어가 좋은데 왜 동적 언어들이 생겨났을까?

 

컴파일 언어는 각 플랫폼 별로 다른 변환이 필요했다.

⇒ 각 프로그램 별로 실행 파일이 모두 달랐어야 했다.

⇒각 플랫폼 별로 빌드가 필요하다.

 

과거에는 굉장히 많은 CPU회사가 있었다.

 

자연어를 기계어로 변환 할 때의 어셈블리어의 각 명령어 마다 번호를 부여하게 되는데, 문제는 칩셋 제조사 마다 이 번호가 다 달랐다.

결국, 어떤 칩셋을 사용하냐에 따라 다르게 동작해야 했다.

 

인텔 CPU

  • 칩셋의 발전 과정 8086-80286-80386-80486- 펜티엄, , , , I3, I5, I7(요즘 나오는 것)

새로운 CPU가 나올때 OP코드도 추가된다.

같은 인텔 계열 칩셋이라도 어떻게 변환하냐에 따라 프로그램이 달라진다.

 

OS- 윈도우즈, Mac, Linux 등 각 OS마다 동작 방식이 다르고, 실행파일 형식도 다르고 실행하는 방법도 다르다.

 

CPU, OS에 따라 컴파일 변환 과정이 다 달랐다.

 

옛날 게임들- Windows밖에 안돼 등.. 문제가 있었다. 

이런거 해결을 위해 나온것 = 동적 언어

프로그램을 만들어 놓으면 실행주체가 어떤 프로그램인지 파악해서 그때 그때 변환 하는 것임.

 

Java 언어의 특징 

  • 코딩→ 결과 → 빌드 → .JAR
  • 플랫폼에 따라 실행 가능 하도록 알아서 변환된다.
  • 컴파일 언어에 비해서 속도가 떨어진다는 문제점이 있었다.
  • C++이 자바보다 빠르다고 말하는 사람이 많다.
  • 언어적으로 완성도가 높다.
  • 라이브러리 지원이 많다.
  • 생산성이 좋다. (C# 또한)

C, C++ 언어의 특징 

  • 사용법이 까다롭고 라이브러리가 좀 ,, 자바보다 못하다.
  • 속도는 자바와 거의 비슷하다.

 

첫 언어로 선택할만한 언어에 어떤 언어가 있는가.?

 

C언어

  • 기본, 근간, simple, 포인터

C#

  • 첫 언어로 무난하다.
  • Windows에서 잘 돌아간다.

Python

  • 언어구조 단순, 짜임새가 좋다, 언어의 완성도가 높다.
  • OOP적 측면에서 봤을때 완전 좋다.
  • 인터프리터 언어
  • 속도가 떨어진다.

Javascript

  • 웹쪽 언어
  • 자바스크립트를 첫 언어로 쓰기에는 안좋다.
  • 완성도가 떨어진다.
  • 웹쪽 쓸 때는 필수

 

해당 글은 [Tucker의 Go 언어 프로그래밍] 2장을 읽고 공부한 글입니다. 

트랜지스터(삼 석, 다리 세게 달리고 가운데가 긺. )

  • 컴퓨터의 가장 기본적인 요소임
  • 컴퓨터는 수천, 수억 개의 수많은 트랜지스터들로 만들어져 있음
  • CPU는 트랜지스터 덩어리임
  • 컴퓨터에서는 트랜지스터를 전기만 가지고 스위치의 역할을 하는 것으로 쓴다.
  • 전기적 신호로 작동함. (진짜 스위치는 물리적인 힘으로 껏다 켰다 해야함.)
  • 1 비트는 하나의 트랜지스터라고 보면 된다.

발광 다이오드= LED

  • 성질이 다른 두 개의 실리콘을 겹쳐서 N,P다이오드를 만들어서 건전지를 달면 전기가 흘러서 불이 들어옴
  • 발광 다이오드에서 좀더 발전해서 트랜지스터가 된다.

 

트랜지스터로 논리소자를 만들 수 있다.

 

BOOL 대수 - AND, OR, NOT

 

도체 - 전기가 흐르는 물질 - 구리, 철, 금, 물, 몸

부도체 - 그릇, 세라믹, 돌

 

부도체는 저항이 무한대에 가깝다.

도체는 저항이 매우 작다. = 0에 가깝다.

 

반도체는 도체로 됐다가 부도체로 됐다가 하는 애다.


트랜지스터 → 논리소자 → 계산기 → 컴퓨터

 

컴퓨터(CPU) ← 트랜지스터 ← 실리콘 ← 규소 ← 모래

 

컴퓨터를 발명해낸 사람들은 아래와 같다. 

 

튜링

  업적

  • 튜링테스트(AI를 테스트 하는 기법)
  • 튜링 기계 (상상 속의 기계)
    • 한줄씩 명령을 수행하는 기계가 있으면 연산을 수행하기 좋겠다 라는 생각을 했다. 

폰노이만

  업적

  • 우주 최강의 두뇌 소유자
  • 폰 노이만 기계
    • 전자계산기, 전자시계, 네비게이션과 컴퓨터는 차이가 있다.
    • 컴퓨터는 프로그램을 교체하면서 모든걸 다 할 수 있었다.
    • 튜링 기계랑 비슷한 기계를 만듦
    • 초창기 컴퓨터의 구조와 비슷함


컴퓨터란, 명령을 하나씩 순서대로 수행하는 기계다.

명령: Add, Sub, Div, etc,,

CPU의 각 코어는 코어마다 명령을 하나씩 수행함

 

IP(instruction point)

 

프로그램은 어떤 명령을 어떤 순서로 수행할 것인지 적어놓은 것을 말한다.

 

컴퓨터는 1, 0밖에 모르고 add 등 밖에 못한다.

 

인간이 표현하는 그림, 음악, 문자는 숫자로 표현 가능하다.

 

A=65, a=97 , , , (ASCII-255개 문자에 코드)

 

1byte = 8bit 2^8은 256임

0-255 개 만 표현할 수 있다.

 

영문자만 표현할 수는 없어서 확장으로 UNICode 2byte 를 많이 사용하고 있음.

 

이미지는 모니터의 1080*720은 가로에 1080픽셀이 있다.

 

색 → 숫자로 RGB 로 표현할 수 있음

 

점하나에 LED3개가 묶여있다. RGB(LED)

 

R - 255, G - 255, B - 255, A - 투명도

4byte숫자를 쭉 나열하면 화면을 보여줄 수 있다.

 

소리는 주파수에 따른 세기를 통해 나타낼 수 있다.

모든 숫자는 이진수로 표현할 수 있다.

 


 

하드웨어란?

비유

요리할 때 레시피가 필요하다. → 요리하는 방법이 적혀있다. ⇒ 프로그램이다.

요리 재료가 필요하다. → 마트가 필요함→ 마트에 버스타고 간다.

 

이때 마트는 하드디스크임(HDD), 버스는 진짜 버스임

 

마트에서 사와서 냉장고에 재료를 넣는다. (메모리) 마트보다 가까움

 

요리준비 끝.

 

필요한 만큼 마늘과 고기를 꺼내서 도마 위에 놓는다. = CACHE

이렇게 하는 이유는 효율적이기 때문이다.

이때, 도마는 Register 이다. (값을 딱 준비 시켜놓는 자리)

 

CPU는 요리하는 사람.

 

요리하는 도중에 마트에 가는 비효율적이고 시간이 오래 걸린다.

메모리에 가기도 그렇고 그래서 캐시를 쓴다.

 


 

컴퓨터 성능 3GHZ = 주파수

1초당 연산을 3G만큼 한다.

1초에 3 * 1024(GB) *. 1024(MB) *. 1024(KB) 만큼의 연산을 한다.

 

해당 글은 [Tucker의 Go 언어 프로그래밍] 1장을 읽고 공부한 글입니다. 

알아둘 것 

 

  • NVM(Node Version Manager)
    • Node.js의 버전을 관리하는 도구, 다양한 버전의 Node.js를 설치하게 도와줌

 

  • Nodejs의 특정 버전을 설치하고 싶은 경우
    • nvm 설치
    • 현재 사용중인 버전 확인
    • nvm으로 설치된 node.js 목록 확인
    • 특정 버전의 노드를 사용하는 명령어 입력
    • 특정 버전의 노드가 설치되어 있지 않은 경우, 설치 
#현재 사용중인 버전 확인
$nvm current

#nvm으로 설치된 node.js 목록 확인
$nvm ls

#특정 버전의 노드를 사용하는 명령어 입력
nvm use <version>

#특정 버전의 노드가 설치되어 있지 않은 경우, 설치 
npm install <version>

 

 

 

+ Recent posts