List 란?

|

List (리스트)

  • 원소간 순서 개념이 있다.
  • 기본적인 연산 : 삽입, 삭제, 검색 등등

  • 리스트를 구현하는 대표적인 두 가지 방법
    • 배열
    • 연결리스트

배열

  • 장점

    • 랜덤 액세스가 가능하다 -> 검색 속도가 빠르다. ex) 4번째 칸 : 시작 주소 + 3 * (1칸의 크기)
  • 단점

    • 크기가 고정 - reallocation이 필요하다.

    • 리스트의 중간에 원소를 삽입하거나 삭제할 경우 다수의 데이터를 옮겨야 한다.

연결리스트

  • 특징
    • 각각의 노드는 데이터 필드와 하나 이상의 링크 필드로 구성
    • 링크 필드는 다음 노드의 주소를 저장한다.
    • 첫 번쨰 노드의 주소는 따로 저장해야 한다.
    • 마지막 노드의 next 필드에는 NULL을 저장하여 연결리스트의 끝임을 표시한다.
  • 장점
    • 다른 데이터의 이동없이 중간에 삽입이나 삭제가 가능하다.
    • 길이의 제한이 없다.
  • 단점
    • 랜덤 엑세스가 불가능하다 -> 검색속도 느리다.

strtok() 사용법

|
  • strtok() 의 역활
    • 원본 문자열을 변화시킨다. (‘\0’을 삽입한다.) 따라서 원본 문자열을 보존시키려면 복사한 후 strtok()을 호출 해야한다.
    • strtok()은 새로운 배열을 생성하지 않는다.

코드를 통해 실제 예제를 살펴보자.


//
// Created by jayyhkwon on 25/09/2019.
//

#include "string.h"
#include "stdio.h"

int main(){
    char str[] = "now # is the time # to start preparing ## for the exam##"; // char array
//    char *str[] = "now # is the time # to start preparing ## for the exam##"; string literal 이므로 strtok이 tokenizing 할 수 없음.
    char delims[] = "#";
    char *token;
    char *tokens[100];
    int i = 0;

    printf("Before tokenizing str : %s\n",str);
    // strtok 함수는 문자열을 tokenizing 하는 함수이다.
    token = strtok(str,delims);
    while(token != NULL){
        printf("next token is: %s length:%d\n",token,strlen(token));
        // tokenizing 할 문자열에 NULL을 넘겨주면, strtok 함수가 직전에 tokenizing한 문자열의 분리지점의 주					소를 기억하고 그 곳으로 돌아간다.
        // 말이 어려우니 위 예제로 설명 해보겠다.
        // strtok 함수가 동작하는 과정을 하나씩 쪼개보면
        // 1. strtok(str,delims); 을 처음 호출한다.
        // 2. 'now ', ' is the time '으로 str을 tokenizing 한다.
        // 3. ' is the time ' 의 주소를 기억한다.
        // 4. 'now ' 의 주소를 리턴한다.

        // 5. strtok(NULL,delims);를 호출한다.
        // 6. ' is the time ' 의 주소를 기억하고 찾아간다.
        // 7. ' is the time ', ' to start preparing'으로 str을 tokenizing 한다.
        // 8. ' to start preparing '의 주소를 기억한다.
        // 9 .' is the time '의 주소를 리턴한다.
        token = strtok(NULL,delims);
        tokens[i++] = token;
    }

    printf("After tokenizing str : %s\n",str);

    return 0;
}

char[]와 char * 차이점

|

char *와 char []의 차이

강의를 들으며 char *와 char []을 혼용해서 쓰는 경우가 많아

어떤 차이점이 있는 지 궁금하여 찾아보았다.

char *는 주소를 담고, char[]는 값을 담는다.

겉보기에는 같아보이나 내부적으로 담는 방법이 다르다.

그러므로 사용하는 목적도 다르다.

선언과 동시에 초기화를 하는 조건에서 비교를 해보자.

char *str1 = “I Love You”; char str2[] = “I Love You”;

str1은 포인터 변수이므로 주소를 담고 있으므로 값을 변경 할 수 없다. 즉, 상수로 문자열을 사용하고 싶다면 char * 타입으로 선언하면 된다.

반면 str2는 값을 변경할 수 있고, 인덱스로도 접근가능 하다. (컴파일러가 선언시 string 길이를 계산하여 배열에 할당해주기 떄문에) 변경이 잦은 문자열의 경우 char[] 타입으로 선언하여 사용하는 것이 편리하다.

C언어 전화번호부 만들기

|

//
// Created by jayyhkwon on 23/09/2019.
//

#include "calculator.h"
#include <stdio.h>
#include <string.h>

int n = 0;
int BUFFER_SIZE = 100;
char *names[5];
char *numbers[5];

void add(){
    char name[BUFFER_SIZE];
    char number[BUFFER_SIZE];
    scanf("%s",name);
    scanf("%s",number);

    names[n] = strdup(name);
    numbers[n] = strdup(number);
    n++;

    printf("%s is added.\n",name);
}

void find(){
    char name[BUFFER_SIZE];
    scanf("%s",name);

    for(int i=0; i<n; i++)
    {
        if(strcmp(names[i],name) == 0){
            printf("%s : %s\n",names[i],numbers[i]);
            return;
        }
    }
    printf("%s 을 찾을 수 없습니다.\n",name);
    return;

}
void remove(){
    char buf[BUFFER_SIZE];
    scanf("%s",buf);

    int i;
    for(i=0;i<n;i++){
        if(strcmp(buf,names[i])==0){
            names[i] = names[n-1];
            numbers[i] = numbers[n-1];
            n--;
            printf("'%s' was deleted successfully.\n",buf);
            return;
        }
        printf("%s를 찾을 수 없습니다.",buf);

    }
}
void status(){
    int i;
    for(i=0; i<n; i++)
        printf("%s %s\n",names[i],numbers[i]);
    printf("Total %d persons.\n", n);
}

int main(){
    char command[BUFFER_SIZE];

    while(1){
        printf("$ ");
        scanf("%s",command);

        if(strcmp(command,"add") == 0)
            add();
        else if(strcmp(command,"find") == 0)
            find();
        else if(strcmp(command,"remove") == 0)
            remove();
        else if(strcmp(command,"status") == 0)
            status();
        else if (strcmp(command,"exit") == 0)
            break;
    }
    return 0;
}

C언어에서 포인터, 배열, 포인터 연산, 동적메모리 할당

|
  • 포인터(= 포인터 변수)

    포인터는 메모리 주소를 값으로 가지는 변수.

    다음과 같이 선언한다.

      type-name * variable-name;
      ex) int *aInt;
    
  • 연산자 & (주소연산자)

    연산자 &는 변수로부터 그 변수의 주소를 추출하는 연산자이다. ```c

    int c = 12; int * p; p = &c;




- 연산자 * (참조연산자)

    연산자 *는 포인터가 가르키는 주소에 저장된 값을 반환한다.
```c

    int x =1, y=2;
    int * ip; // ip는 int형 포인터 변수임을 선언.
    ip = &x; // ip에 x의 주소값을 할당

    y = *ip; // y에 ip의 주소값이 가진 실제 값을 할당 ( y = x = 1 )
    *ip = 0; // ip의 주소값이 가진 실제 값에 0을 할당 ( x = 0 )

  • 포인터와 배열
    • 포인터와 배열은 매우 긴밀히 연관되어 있다.
    • 예를 들어 다음과 같이 선언된 배열 a가 있다고 하자.

    int a[10]; // 배열의 이름은 배열의 시작 주소를 저장하는 포인트 변수다.(단 그 값을 변경할 수 없다)

  • 포인터 arithmetic

    int a[10];

*a와 a[0]은 동일한 의미이다.
또한 a[1]은 *(a+1)과 동일하고, a[i]는 *(a+i)와 동일하다.
만약 a[0]의 주소값 = 1000 이라면
a[1]의 주소값 = 1004.
  • 동적 메모리 할당

    변수를 선언하는 대신 프로그램의 요청으로 메모리를 할당할 수 있다. 이 것을 동적 메모리 할당이라고 부른다. malloc 함수를 호출하여 동적 메모리 할당을 요청하면 요구하는 크기의 메모리를 할당하고 그 시작 주소를 반환한다.

      int *p = (int *)malloc(40); 
      p[0] = 10; // 포인터변수를 배열처럼 사용가능하다.
    

    그렇다면 int array[4] 와 int *array 차이점은 무엇일까?

    int array[4] : 처음부터 배열로 선언 시 재할당이 불가능하다.