파일로부터 한줄 씩 읽기

파이썬

가장 일반적으로 파일을 한 줄씩 읽어들이는 방법은 다음과 같다.

with open('filename.txt', 'r') as f:
    for line in f:
        pass # line은 읽어들인 각 줄

하지만 이 방법은 파일을 한 번에 메모리로 읽어들인 다음, 한 줄씩 스캔한 결과를 돌려주기 때문에 수 기가짜리 로그 파일을 이런 식으로 처리하면 문제가 생긴다.

파일로부터 한줄 씩 읽기 더보기

재귀호출과 피보나치 수열 탐구

재귀호출은 함수가 그 내부에서 자신을 다시 호출하는 것이다. 이는 언뜻 이상하게 보일 수 있고, 경우에 따라서는 의도치 않은 동작을 하게 할 수 있어서 일반적으로는 지양되는 방법이기는 하나, 대신에 코드가 짧아질 수 있고 실행 로직 자체가 어느 정도 제한된 경우라면 충분히 사용할 수 있다. 특히 하스켈과 같은 함수형 언어에서는 반복문을 돌리는 로직이 없기 때문에 재귀호출을 하는 함수를 자주 사용하게 된다. 재귀호출과 피보나치 수열 탐구 더보기

한 편으로 이해해보는 포인터(C)

C를 공부하는 많은 사람들이 입버릇처럼 ‘포인터는 어렵다’고 말한다. 기술적으로 포인터는 사실 단순히 메모리상의 주소를 가리키는 정수값일 뿐이다. 그런데 왜 이것이 어려울까? 사실 포인터 자체는 단순하지만, 포인터를 이해하기 까지에는 제법 많은 배경지식이 추가로 필요하기 때문일 것이다. 오늘 이 글에서는 각잡고 포인터를 이해하는데 필요한 내용들을 정리하여 전달하고자 한다.

한 편으로 이해해보는 포인터(C) 더보기

[C] 문자열 상수와 문자열 변수의 차이

문자열 상수와 문자열 변수

문자열을 초기화하는 다음 두 가지 방법은 거의 비슷해 보이지만 근본적으로 완전히 다른 동작을 한다.

char *s1 = "abcdefg";
char s2[8] = "abcdefg";

첫번째 s1은 프로그램이 로딩될 때 정적영역에 "abcdefg"를 저장한 다음 이 시작 주소를 s1에 대입한다. 문자열 포인터는 s1에는 나중에 다른 주소의 값을 대입할 수 있다. 즉, s1자체는 변경이 가능한 포인터 변수이지만, 지금 s1이 가리키고 있는 문자열은 문자열 상수이므로 이 문자열을 변경할 수 없다. 반대로 s2는 힙 영역에 메모리를 할당한 후 "abcdefg"를 이곳에 저장했다. 따라서 s2에 담겨 있는 문자열은 변경이 가능하다. (각각의 바이트는 모두 개별 변수처럼 취급되므로)

간단한 예시를 보자. 다음 함수는 문자열을 모두 대문자로 만들어 주는 함수이다. (윈도우용 C컴파일러에 있는 함수임)

#include <ctype.h>
void strupr(char *origin) {
    char *temp = origin;
    while(*temp != '\0') {
        *temp = toupper(*temp);
    }
}

이 함수는 문자열 포인터(=문자열의 시작번지)를 인수로 받아, 각 문자를 대문자로 바꿔준다. toupper 함수는 표준 C 라이브러리에 포함되어 있는 함수이다. 만약 이 함수에 s1을 넘기면 프로그램이 다운된다. 문자열 상수를 변경하려는 시도를 했기 때문이다. 반대로 s2를 넘긴 경우에는 정상적으로 동작하는 것을 볼 수 있다. 다음은 전체 코드이다.

#include <stdio.h>
#include <ctype.h>

void strupr(char *origin);

int main(void) {
    char *s1 = "abcdefg";
    char s2[8] = "abcdefg";
    //printf("%s\n", strupr(s1)); // ==> 프로그램이 다운된다. 
    printf("%s\n", strupr(s2)); // ==> 정상적으로 동작한다. 
    return 0;
}

void strupr(char *origin) {
    char *temp = origin;
    while(*temp != '\0') {
        *temp = toupper(*temp);
    }
}

만약 문자열 상수로 만들어진 문자열을 사용하고자 한다면 다음과 같이 고친다.
char *s1 = "abcdefg";
char s2[8];
strcpy(s2,s1);

언뜻 생각하기에는 s2 = s1; 이라고 하면 안되나? 하는 생각이 들 수 있는데, 안된다. 비록 문자열 배열 변수의 변수명이 배열의 시작번지를 가리킨다고 해서 이것이 포인터라는 것은 아니다. 그것은 일종의 convension으로 배열의 변수명을 배열의 시작번지와 같이 다루는 것일 뿐이다. 즉, s2는 포인터 상수이고, 방금 예를 든 저 구문은 s2의 자리에 s1의 값 (정적영역내의 "abcdefg"가 자리잡은 메모리 번지)을 대입하고자 하는 시도이고, 이는 서로 타입이 맞지 않는 대입 구문이기 때문에 에러를 유발한다.

정리

  • 문자열 포인터로 문자열을 초기화하면 문자열 상수가 된다.
  • 배열로 선언한 문자열에 문자열을 집어넣을 때는 직접 대입이 아니라 strcpy를 쓰거나 한자 한자 루프를 돌면서 넣어야 한다.