본문 바로가기
Development/CS

[CS50] 제 3-2강 배열

by _KHK 2021. 12. 20.

이전 3-1에서는 배열이 어떤 역할을 하게 맡게 될지 알게 되었다면 3-2강에서는 직접 배열을 배워본다.

 

문자열과 배열

 

자료형들은 각각 할당된 메모리를 갖고 있다.

하드웨어적으로 메모리를  쉽게 얘기하면 여러 바이트들의 묶음이라고 설명한다.

char타입의 메모리는 메모리 안의 저장공간인 수많은 작은 칸 중 하나를 요청한다. 각 칸은 어떤 방식으로든 0과 1을 표현하고 있을 것이다.

 

이제 실제로 코드를 작성해본다.

"HI!"를 출력하고 싶은데 C는 기본적으로 문자열(string)이라는 자료형이 없다. 

그래서 표현 할 수 있는 코드는 이렇게 작성될 수 있다.

char c1 = 'H';
char c2 = 'I';
char c3 = '!';

 

이런 코드는 디자인적으로도, 메모리 저장 방식에도 문제가 있다. 또 같은 의미의 연속된 변수를 붙여 넣기 하는 방법과 같다고 설명한다.

하나 이상의 값들이 있고, 서로 연관되어 있다고 할 때 연결 지을만한 변수명은 무엇일까?

그때 사용하는 방법이 배열이다. 

char s[] = "hi!";

C언어에서 문자열(string)은 메모리에 연속적으로 저장된 문자(character) 자료형 데이터들의 배열인 것이다.

 

메모리에 저장되는 문자열의 형태

 

위 그림처럼 메모리안에 연속적으로 저장되고 각 문자열은 사실 어떠한 형태로든 0과 1로 이루어져 저장되어있는 것이다.

s[3]이라고 저장 마지막에 있는 \0은 'null' 문자 혹은 널 종단 문자라고 불린다. 널 문자는 메모리에서 연속적으로 저장되어 있는 문자열들 사이에서 하나의 문자열이 끝났다는 것을 컴퓨터에게 알려주는 역할을 한다. 

사실 이런 것이 현실적으로 코딩을 하는데 엄청나게 중요한 것은 아니지만 이런 식의 조작을 통해 실행된다는 것을 알고 있는 것이 중요하다.

 

 

문자열의 활용

 

이렇게 문자열이 저장되는 것을 알고 있다면 어떤 방식으로 활용 될 수 있는지, 또 평범하게 사용했던 함수들이 어떤 원리를 갖고 만들어진 것인지 알 수 있게 된다.

 

#include <stdio.h>
#include <cs50.h>
#include <string.h>

int main(void)
{
    string s = get_string("Input : ");
    printf("Output : ");
    for (int i = 0; s[i] != '\0'; i++)
    {
        printf("%c", s[i]);
    }
    printf("\n");
}

널 문자를 사용해 입력받은 문자열이 끝남을 활용해 입력받은 문자열을 그대로 다시 출력해주는 코드이다.

printf("%s) 가 아닌 %c를 사용해 하나의 문자열이 하나의 문자가 배열로 저장되어있는 것도 다시 한번 확인할 수 있다.

 

하지만 우리는 이런 기계어에 가깝게 코드를 짜는게 아니라 잘 추상화된 함수들을 이용해 코드를 짠다.

단지 이런 추상화된 함수들이 이런 원리로 작성되어있다는것을 알 필요가 있다는 것뿐이다.

 

for (int i = 0; i < strlen(s); i++)

우리는 문자열의 끝을 알고 싶었던 것이고 strlen은 문자열의 길이를 알려준다. 다시 말해 문자열의 길이를 알려주는 코드는 사실 널문자를 추상화해 만들어낸 함수인 것이다.

 

여기서 추가해 강의 중 내가 충격을 받은 부분이 있다. 바로 위 for문에서 strlen함수가 조건문에 들어가는 것은 디자인적으로 이쁘지 않다고 말한다. 내가 흔하게 사용한 for문이 사실은 알고리즘적 측면에서 비효율 적으로 작성되어 있었던 것이다. 

 

아래 코드를 보면

for (int i = 0; i < strlen(s); i++) // X
for (int i = 0, n = strlen(s); i < n; i++) // O

둘 다 코드가 동작하는데 이상은 없다.

하지만 첫 번째 코드는 for문이 돌아가면서 매번 strlen함수에게 문자열의 길이를 묻는다. 하지만 두 번째 줄 코드를 보면 int i를 선언하고 n을 초기화 선언하는 부분에서 단 한 번만 문자열의 길이를 묻는다. 즉 for문이 끝나기까지 다시 묻는 일이 없다는 것이다. 

 

어떠한 연산을 1회 줄인다는 것은 200만 번 돌아야 하는 for문에서 200만 회의 연산을 줄일 수 있다. 강의 자체는 어렵지 않지만 항상 신선한 충격을 받고 새로움을 배워가고 있다. 강의를 듣다 보면 이렇게 사고의 신선함을 주는 순간들이 있어서 너무 새롭고 재미있게 강의를 집중하게 된다.

 

 

 

명령행 인자

사실 이 파트를 작성해야 할지 말아야 할지 고민했는데 고민한 순간 그냥 작성하기로 했다.

처음 코딩을 배울 때 도대체 무슨 뜻인가 이해하지 못했었고, 그냥 띄엄띄엄 느낌적으로 알아두고 넘어갔었던 게 기억이 났기 때문이다. 별 거 아닐 수 있지만 조금이라도 정리를 해두는 게 좋을 것 같다는 느낌이 들었다.

 

int main(int argc, string argv[])
{
	//...
}

위와 같은 코드를 본 적이 있을 것이다.

단어부터 설명하면 argc는 argument count, argv는 argument vector이다.

사용명이 명령행 인자라는 것이 이들의 특징이다. 명령행 인자는 명령어를 사용하면서 입력받는 인자를 말한다.

 

여태껏 터미널에서 make, clang, 등등 명령어 다음에 단어 입력을 받는다.

프로그램을 실행하면서 입력을 받는 것인데 명령행 인자가 바로 이 입력받는 인자를 저장하고 코드에서 필요에 의해 사용하게 된다.

예를 들어 make stirng 명령어를 실행했다고 하면./string으로 프로그램을 실행하는데 사실은 ./string 인자

이런 식으로 인자를 받을 수 있다.

받은 인자는 어떻게 활용할 수 있을까?

비밀번호로 활용할 수도 있고, 입력값이 필요한 프로그램에 먼저 입력받을 수 있고, 프로그램 실행 시 옵션 값을 줄 수도 있다.

 

'Development > CS' 카테고리의 다른 글

[CS50] 5강 메모리  (0) 2021.12.23
[CS50] 제 4강 알고리즘  (0) 2021.12.20
[CS50] 제 3-1강 배열  (0) 2021.12.19
[CS50] 제 2강 - C언어  (0) 2021.12.18
[CS50] 제 1강 - 컴퓨팅 사고  (0) 2021.12.16

댓글