모두를 위한 컴퓨터 과학 Chapter 3. 배열

Ryoni·2022년 1월 9일
0
post-thumbnail

얻덯케 아셨나요,,? 마구잡이로 쓴 코드가 잘 돌아갔다는 걸,,?
마구잡이 코드를 이용해서 일도 하고 있는걸요...

컴파일링

main 함수 - 실행 버튼을 클릭하는 것과 같음
printf - 출력을 담당하는 함수(stdio.h 라이브러리가 필요)
stdio.h - 헤더 파일, 파일명이 .h로 끝나는 파일(자바가 .java / 컴파일 후 .class 로 변경되는 것과 같음)
- 이 파일에 printf 함수의 프로토 타입이 있어서 Clang 컴파일러가 프로그램을 컴파일 할 때 printf가 뭔지 알려주는 역할을 함

전처리(Precompile)

컴파일의 과정은 총 네 단계
1. 전처리(전처리기에 의해 수행됨)
#으로 시작되는 C 소스 코드는 전처리기에 실질적인 컴파일이 이루어지기 전에 무언가를 실행하라고 알려줌

컴파일(Complie)

전처리기가 전처리한 소스 코드를 생성하고 나면 컴파일 진행
컴파일러라고 불리는 프로그램은 C 코드 어셈블리어(Assembly language)라는 기계어와 1 : 1 대응이 되는
컴퓨터 프로그래밍의 저급 언어로 컴파일 진행

자바 수업을 진행할 때 초반에 지루함을 무릅쓰고 꼭 짚고 넘어가는 컴파일 이야기
이클립스에서 자바 언어를 사용할 때 class 파일을 생성해서 코드를 작성하고
설정 해놓은 workspace에 들어가 확인하면 클래스이름.java 라고 저장된 걸 확인할 수 있다.
하지만 실제로 컴퓨터가 읽는 것은 .java 확장자가 아닌 .class 확장자인데,
사람이 쓰는 언어(high level language)인 .java 확장자에서
0과 1만 아는 컴퓨터(low level language)가 읽을 수 있도록 .class 확장자로 확장자명을 변환하는 작업을 컴파일이라고 한다.
(파일 변환하는 과정은 컴파일러라고 함)

어셈블(Assemble)

소스 코드가 어셈블리 코드로 변환되면, 다음 단계인 어셈블 단계로 어셈블리(Assembly) 코드를 오브젝트(Object) 코드로 변환 진행
어셈블은 컴파일과 다르게 어셈블리어를 기계어로 번역하는 것!

프로그램이 여러개의 파일로 이우러져 있어 하나의 오브젝트 파일로 합쳐져야 한다면 링크라는 컴파일의 마지막 단계가 필요!
링커는 여러개의 다른 오브젝트 코드 파일을 실행가능한 하나의 오브젝트 코드 파일로 합쳐주는 역할

이 네 단계를 거치면 최종적으로 실행 가능한 파일이 완성!✨

💡 생각해보기

Q. 만약 컴파일링 과정을 거치지 않기 위해 바로 머신코드로 우리가 원하는 프로그램을 작성하려고 한다면 어떤 문제가 있을까요?

  • 엄청나게 많은 시간이 소요되고, 가독성, 편의성 등 모든 면에서 효율이 떨어진다

    디버깅

    버그와 디버깅

    버그(bug)는 코드에 들어있는 오류
    버그로 인해 실행이 되지 않거나 동작이 되지 않음
    디버깅(debugging)은 코드에 있는 버그를 식별하고 고치는 과정
    디버거를 사용하여 디버깅 진행

    디버깅의 기본

    디버거는 프로그램을 특정 행에서 멈출 수 있게 해주기 대문에 버그 찾는데 도움이 된다
    프로그램이 멈추는 특정 지점을 중지점이라고 함

💡 생각해보기

Q. 디버깅을 도와주는 프로그램은 어떤 경우에 더 큰 도움이 될까요? 만약 이런 프로그램의 도움 없이 직접 디버깅을 해야 한다면 어떻게 코드를 작성하는 것이 좋을까요?

  • 프로젝트 진행하면서 함수들이 중첩되어 있을 때 오류나 에러 잡을 때 용이
  • 직접 디버깅을 해야 한다면 구분을 지어 에러 없이 진행되고 있는지 확인하는 코드가 중간 중간 있어야 할 것 같다

코드의 디자인

러버덕🐥

코드에 포함된 오류를 해결할 때 여러가지 프로그램을 사용해봐도 디버깅에 큰 도움이 되지 않을 수도 있음
이럴 때는 '러버 덕' 과 같은 무언가 대상이 되는 물체를 앞에 두고,
내가 작성한 코드를 설명하면서 미처 놓치고 있던 논리적 오류를 찾아낼 수도 있음!

💡 생각해보기

Q. 만약 여러 사람들이 함께 참여하는 프로젝트에서, 각자가 작성하는 코드 스타일이 서로 다르다면 어떤 비효율적인 일이 발생할까요?

  • 코드를 이해하는 데에 많은 시간이 소요되고 커뮤니케이션도 잘 안 될 것 같다!

배열

메모리

C에는 아래와 같은 여러 자료형이 있고, 각각의 자료형은 서로 다른 크기의 메모리를 차지합니다.

  • bool: 불리언, 1바이트
  • char: 문자, 1바이트
  • int: 정수, 4바이트
  • float: 실수, 4바이트
  • long: (더 큰) 정수, 8바이트
  • double: (더 큰) 실수, 8바이트
  • string: 문자열, ?바이트


컴퓨터 안에는 위와 같은 RAM이라고 하는 물리적 칩이 존재, 여러개의 노란 사각형이 메모리를 의미, 작은 사각형 하나가 1byte!

배열

배열은 같은 자료형의 데이터를 메모리상에 연이어서 저장하고 이를 하나의 변수로 관리하기 위해 사용
배열의 인덱스는 0부터 시작하며, 인덱스를 변수명 뒤 대괄호[] 사이에 입력하여 원하는 위치에 값을 저장하고 불러올 수 있음

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

int main(void)
{
    // Scores
    int scores[3];
    scores[0] = 72;
    scores[1] = 73;
    scores[2] = 33;

    // Print average
    printf("Average: %i\n", (scores[0] + scores[1] + scores[2]) / 3);
}

💡 생각해보기

Q. 실생활의 어떤 데이터를 배열로 표현할 수 있을까요?

  • 날짜, 날씨, 아파트의 우체통 등

전역변수

전역변수의 이름은 대문자로 표기

배열의 동적 선언 및 저장

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

float average(int length, int array[]);

int main(void)
{
    // 사용자로부터 점수의 갯수 입력
    int n = get_int("Scores:  ");

    // 점수 배열 선언 및 사용자로부터 값 입력
    int scores[n];
    for (int i = 0; i < n; i++)
    {
        scores[i] = get_int("Score %i: ", i + 1);
    }

    // 평균 출력
    printf("Average: %.1f\n", average(n, scores));
}

//평균을 계산하는 함수
float average(int length, int array[])
{
    int sum = 0;
    for (int i = 0; i < length; i++)
    {
        sum += array[i];
    }
    return (float) sum / (float) length;
}

배열의 킉를 사용자에게 직접 입력 받고, 배열의 크기만큼 반복하면서 각 인덱스에 해당하는 값을 역시 사용자로부터 입력 받아 저장 진행

average 함수 선언하여 평균 구하기
average 함수는 length와 array[] 즉, 배열의 길이와 배열 입력 받은 후 배열의 길이만큼 반복하고, 합 구하고, 최종적으로 평균 값 반환 진행

→ 이와 같은 방법을 통해 임의의 점수 개수와 점수 배열에 대해서 동적으로 평균값을 구하는 프로그램 작성 가능

💡 생각해보기

Q. 점수의 평균을 구하는 예제에서, 동적으로 작성한 코드는 그렇지 않은 코드에 비해 어떤 장단점이 있을까요?

  • 여러 상황에 맞춰 사용자의 입력을 통해 변수가 설정되기 때문에 활용성과 효율성이 좋다! 단점으로는 코드가 길어지게 된다

문자열과 배열

문자열(String) 자료형의 데이터는 사실 문자(Char) 자료형의 데이터들의 배열 형태
String s = "Hi!"; 과 같이 문자열 s가 정의되어 있다고 할 때
s는 문자의 배열이기 때문에 메모리상에 아래 그림과 같이 저장되고, 인덱스로 각 문자에 접근할 수 있음


여기서 가장 끝의 '\0'은 문자열의 끝을 나타내는 널 종단 문자
단순히 모든 비트가 0인 1바이트를 의미

💡생각해보기

Q. 널 종단 문자는 왜 필요할까요?

  • 다른 문자열과 구분 지어주기 위해서 필요!

문자열의 활용

문자열의 길이 및 탐색

문자열의 끝을 알 수 있는 방법 중 하나는 인덱스의 문자가 널 종단 문자, 즉 '\0'와 일치하는지 검사하는 것!
String.h 라이브러리 안에 포함된 문자열의 길이를 알려주는 함수인 strlen 사용

문자열 탐색 및 수정

문자의 대소비교가 가능한 이유는 ASCII(아스키코드)값, 즉 그 문자가 정의되는 ASCII 코드 상에서 숫자값으로 비교할 수 있기 때문
또한 알페벳의 ASCII 값을 잘 살펴보면 각 알파벳의 소문자와 대문자는 32씩 차이가 나는 것을 알 수 있음
문자가 소문자인 경우 그 값에서 32를 뺀 후에 '문자' 형태로 출력하면 대문자가 출력됨

💡 생각해보기

Q. string.h와 ctype.h의 라이브러리에 다른 어떤 함수가 있는지 확인해 보고, 어떤 함수를 어떻게 활용해 볼 수 있을지 생각해봅시다.

  • string.h : 문자열 복사, 비교, 더하는 함수
  • ctype.h : 문자열의 조건 검사 및 변환(대소문자, 알파벳 or 숫자, 아스키코드 등)

명령행 인자

프로그램을 실행할 때 컴파일하고자 하는 코드 외에도 컴파일 후에 저장하고자 하는 파일명과 같이 추가적인 정보를 함께 줄 수 있는데 이런 정보들을 명령행 인자라고 부름

💡 생각해보기

Q. 명령행 인자는 프로그램의 확장성에 어떤 도움이 될까요? 구체적인 예시를 떠올려보세요.

  • 암호화는 물론 문법검사, 맞춤법 검사 등 문자열을 가지고 할 수 있는 모든 것이 가능

profile
비전공자 개발자는 술로 코딩한다

2개의 댓글

comment-user-thumbnail
2022년 1월 9일

빨리 빨리 올리셔야죠. 근데 우측에 목차 보이는거 어떻게하는건가요....??

1개의 답글