티스토리 뷰

배열

  • 동일한 자료형의 값들을 여러 개 담는 기술. 메모리에 데이터를 연속적으로 저장한다.
  • index를 사용해서 data에 접근할 수 있다.
    • index는 0부터 시작한다.
    • data들은 모두 같은 자료형이다.
    • 모두 같은 자료형이므로, 자료형 크기 * index로 특정 데이터에 한 번에 접근할 수 있다(O(1))
  • 10개 데이터를 담는 배열은 40byte 메모리를 차지할 것이다.(4byte x 10)
  • 선언
    자료형 배열명[배열 크기];  // 메모리 영역만 확보하고 쓰레기값으로 초기화되어 있는 상태
    자료형 배열명[배열 크기] = { a, b, c };    // index 0부터 a, b, c 차례로 초기화. 값이 없는 index에는 쓰레기 값
    자료형 배열명[배열 크기] = { a, };    // 배열 크기에 동일한 값으로 초기화
  • 데이터 접근
    int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int b = a[5];
    printf("%d", b);    // 6

<limits.h> : INT_MAX 등 최대, 최소값 등을 정의해 둔 라이브러리


문자열과 배열

  • 원시적인 C언어에서는 문자열이라는 자료형이 없음. 문자(char)의 모음(배열)로 표현
    • char a[20] = "HELLO WORLD";
    • C++은 이런 불편함을 개선하기 위해 문자열(string) 자료형 제공
  • C는 문자열이 배열로 구성되므로, index를 사용해서 특정 문자 1개에 접근하거나 수정하기 편하다
    • char a[20] = "HELLO WORLD"; a[4] // O a[6] = 'T'; // -> HELLO TORLD

포인터

  • 지금까지 변수는 그 자체로 자료형에 맞는 값을 저장
  • 포인터 변수는 메모리 주소를 저장(어떤 자료형의 주소값인지 저장)
    • 값이 저장된 위치(주소, 0xaaaa)값을 저장하는 것.
  • 간접참조연산자(*) : 현재 포인터가 가리키고 있는 주소에 들어 있는 값
    • 선언할 때는 포인터
    • 사용할 때는 간접 참조 연산자(메모리 접근)
  • & : 주소 연산자(변수의 메모리 시작 주소)
// a라는 변수의 주소 0xAFB03954
// a 변수에 저장된 값 5
int a = 5;    

// b라는 포인터 변수의 주소 0xCA29839F
// b 변수에 저장된 값 0xAFB03954
int *b = &a;

printf("%d", &a);  // 5
printf("%d", *b);  // 5. 포인터 변수 b가 가리키는 주소가 가지고 있는 값

포인터의 강력한 기능

  • 시스템의 특정 메모리에 바로 접근할 수 있으므로, 기존의 다른 중요한 메모리 영역에 접근하지 않도록 주의해야함

  • 다음은 아주 위험한 코드.

    // 이 주소가 어떤 역할을 하는 값인지 알지 못하는데 그 주소의 값을 0으로 만들어버림
    int *a = 0x33484753; 
    *a = 0;
  • 다중 포인터

    • 다중 포인터는 여러번 중첩도 가능(포인터의 포인터의 포인터의 포인터의 포인터의....)
    • 난독화 기법으로도 사용할 수 있다.
    int a = 5;     // 값 5를 저장하고 있는 변수 a
    int *b = &a;   // 변수 a의 주소값을 저장하고 있는 포인터 변수 b
    int **c = &b;  // 포인터 변수 b의 주소값을 저장하고 있는 포인터 변수 c
    
    // 5를 출력하기 위해서는
    printf("%d", &a);  // a 변수의 주소에 저장되어 있는 값
    printf("%d", *b);  // b 변수의 주소에 저장되어 있는 주소(a)에 저장된 값
    printf("%d", **c);  // c 변수의 주소에 저장되어 있는 주소(b)에 저장되어 있는 주소(a)에 저장된 값

배열과 포인터의 관계

  • 배열과 포인터는 내부적으로 동일하게 동작함
  • 배열을 선언한 뒤에는 그 이름 자체가 포인터 변수와 동일하다.
int a = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int *b = a;  // 주소 연산자가 없어도 배열이름 a가 주소값 자체를 가지고 있으므로 주소값이 전달됨.

b[2] // -> 3. 포인터 변수로도 index를 사용해서 접근할 수 있다.

문자열


문자

  • 문자와 버퍼의 이해와 활용

아스키코드

  • C 프로그램의 문자는 ASCII code를 따른다.
  • 0 ~ 127 중 1 byte에 대해 매칭되는 문자를 출력한다.
    • e.g. '0' : 48, 'A' : 65, 'a' : 97
  • char 자료형에 숫자를 넣으면 ASCII code에 대응되는 문자로 출력된다.
    char a = 65;
    printf("%c", a); // A

문자 입출력과 버퍼

  • getchar() : 단 하나의 문자를 입력받는다.

  • 버퍼(Buffer) : 임시로 특정 데이터를 저장하기 위해 사용

    • 사용자가 많은 양의 문자를 입력하면, 컴퓨터가 처리할 수 있는 양은 한정적. 버퍼에 담은 다음에 하나씩 실행된다.
    • 자동으로 버퍼를 사용해서 입출력을 처리하고 있다.
    • 입력 버퍼로 인한 오류위 프로그램에서 정수 5를 받은 뒤, 현재 버퍼에 남아있는 문자를 지워줘야 문자를 다시 입력받아 c에 저장할 수 있다.
  • 버퍼로 인한 문제 : 5를 입력하고 엔터(\n)를 입력하면 5는 a 변수에 저장되고 \n 문자는 c 변수에 저장되어 모든 코드가 실행되고 프로그램이 바로 종료된다.

    int a;
    char c;
    scanf("%d", &a);
    printf("%d", a);
    scanf("%c", &c);
    printf("%c", c);
  • 버퍼를 지우고 입력을 받음

    int a;
    char c;
    scanf("%d", &a);
    printf("%d", a);
    
    // getchar로 문자 1개씩 받아서 EOF(파일의 끝)이거나 개행 문자(\n)라면 입력을 멈추고 버퍼를 지운다.
    // 입력을 멈추먼 버퍼를 비운다
    int temp;  // 문자는 아스키코드에 의해 내부적으로 숫자로 관리되므로 int로 받을 수 있음
    while (temp = getchar() != EOF && temp != '\n') { }
    
    // 버퍼가 비었으므로 다시 입력을 받는다.
    scanf("%c", &c);
    printf("%c", c);

문자열

  • 문자열 : 문자들의 배열
  • 배열은 포인터 ⇒ 문자열은 배열 및 포인터처럶 사용할 수 있다.
  • 문자 배열 맨 마지막엔 NULL(존재하지 않는다는 의미)값을 포함한다.(\0)
    • NULL은 문자열의 끝을 알리는 목적으로 사용
    • 실제 메모리에 저장된 값 : H, E, L, L, O, whitespace, W, O, R, L, D, \0
    • printf() 함수를 실행하면 내부적으로 NULL을 만날 때 까지 문자를 출력, NULL을 만나면 출력 종료

문자열과 포인터

  • 배열이 아닌 포인터 변수에 문자열 할당 → 포인터에 문자열을 상수(literal)로(읽기 전용으로) 특정 문자열의 주소값을 넣는다.
    • 문자열의 주소는 컴파일러가 알아서 결정한다.
    • char *a = "HELLO WORLD"; printf("%s", a); // 문자열은 내부적으로 배열로 치환되고, 배열은 주소를 가리키므로 & 사용하지 않아도 된다. // 포인터는 배열로 치환되므로 index로 접근할 수도 있다. printf("%c\n", a[1]); // E
  • 문자열 입출력
    • scanf()는 공백을 만날 때 까지 입력을 받지만, gets()는 공백을 포함해서 입력을 받는다.
    • char a[100]; gets(a); printf("%s", a); // HELLO WORLD
    • gets는 배열의 전체 범위를 고려하지 않아서 보안 취약점이 있다고 알려져 있다.
    • gets_s() : 특정 범위 만큼만 입력을 받도록 함. → C11 표준부터 추가. 범위를 넘으면 런타임 오류
      gets_s(a, sizeof(a));

문자열 관련 함수

  • <string.h> 라이브러리에 포함된 함수를 사용할 수 있다.
    • 최근 vs는 <string.h>를 포함하고 있는 경우도 있어서 include 선언하지 않아도 사용할 수 있는 경우도 있다.
  • strlen() : 문자열 길이 반환
  • strcmp() : 문자열 1이 문자열 2보다 사전적으로 앞에 있으면 -1, 뒤에 있으면 1 반환
  • strcpy() : 문자열 복사
    • C에서는 a=b 같은 방법으로 문자열을 복사할 수 없다.
  • strcat() : 문자열 1에 문자열 2를 더한다(뒤로 이어붙인다).
    • 문자열 1의 배열 크기가 문자열 2가 더해진 뒤에 길이를 포함할 수 있을 정도로 충분해야 한다.
  • strstr() : 긴 문자열에서 짧은 문자열을 찾아 그 위치를 반환.
    • 짧은 단어를 찾은 주소값 자체를 반환하므로, 단순 출력에서는 찾은 단어의 뒤에 있는 모든 문자를 반환한다.
    • 문자열 1에 문자열 2가 어떻게 포함되어 있는지 반환
char a[20] = "HELLO WORLD";
printf("%d\n", strlen(a));  // 11

char b[20] = "HELLO C";
printf("%d\n", strcmp(a, b));  // 1. WORLD가 C보다 뒤에 있으므로

strcpy(a, b);
printf("%s\n", a);  // HELLO C

char c[40] = "My name is ";
char d[20] = "CSKIM";
printf("%s\n", c);  // My name is CSKIM

char e[20] = "I like you";
char f[20] = "like";
printf("%s\n", strstr(e, f));  // like you

문자열, 배열, 포인터의 상관관계

  • 배열은 동일한 자료형을 갖는 데이터를 메모리에 순서대로 저장한다.
  • 포인터 변수는 어떤 변수의 주소값을 저장한다.
  • 배열은 주소값과 자료형 크기를 사용해서 값에 접근한다. → 배열은 주소값을 가지고 있다. → 배열은 포인터와 동일하게 처리된다.
  • 문자열은 char + NULL 문자의 모음이다. → 문자들이 메모리에 순서대로 저장되어 있다 → 문자열은 문자의 배열이다.
  • 정리 : 문자열은 문자의 배열이다 → 배열은 포인터다 → 문자열은 포인터다
  • char a[20] = "HELLO WORLD"; char *a = "HELLO WORLD";
댓글
댓글쓰기 폼