C 메모리 및 문자열 함수

메모리 함수 (string.h)

memcpy - 메모리 복사

#include <string.h>
void *memcpy(void *dest, const void *src, size_t n);
  • src에서 destn 바이트를 복사
  • 메모리 영역이 겹치면 정의되지 않은 동작 (겹치면 memmove 사용)
  • 반환값: dest 포인터
#include <stdio.h>
#include <string.h>

int main() {
    char src[] = "Hello, World!";
    char dest[20];

    memcpy(dest, src, strlen(src) + 1);  // +1은 NULL 문자 포함
    printf("복사된 문자열: %s\n", dest);  // "Hello, World!"

    // 배열 복사
    int arr1[] = {1, 2, 3, 4, 5};
    int arr2[5];
    memcpy(arr2, arr1, sizeof(arr1));

    for (int i = 0; i < 5; i++) {
        printf("%d ", arr2[i]);  // 1 2 3 4 5
    }
    return 0;
}

memmove - 겹치는 메모리 복사

#include <string.h>
void *memmove(void *dest, const void *src, size_t n);
  • memcpy와 동일하지만 메모리 영역이 겹쳐도 안전
  • 반환값: dest 포인터
#include <stdio.h>
#include <string.h>

int main() {
    char str[] = "Hello, World!";

    // 메모리가 겹치는 경우 (str[7]부터 str[0]으로 복사)
    memmove(str, str + 7, 6);  // "World!"를 앞으로 이동
    str[6] = '\0';
    printf("%s\n", str);  // "World!"

    // 배열 요소 이동
    int arr[] = {1, 2, 3, 4, 5};
    memmove(arr + 1, arr, 4 * sizeof(int));  // 한 칸씩 오른쪽으로
    arr[0] = 0;
    // 결과: {0, 1, 2, 3, 4}

    return 0;
}

memset - 메모리 초기화

#include <string.h>
void *memset(void *s, int c, size_t n);
  • 메모리 영역을 특정 값으로 초기화
  • cunsigned char로 변환되어 사용
  • 반환값: s 포인터
#include <stdio.h>
#include <string.h>

int main() {
    char buffer[100];

    // 0으로 초기화
    memset(buffer, 0, sizeof(buffer));

    // 특정 문자로 채우기
    memset(buffer, 'A', 10);
    buffer[10] = '\0';
    printf("%s\n", buffer);  // "AAAAAAAAAA"

    // 배열 초기화
    int arr[10];
    memset(arr, 0, sizeof(arr));  // 모든 요소를 0으로

    // -1로 초기화 (주의: int는 4바이트이므로 0xFFFFFFFF가 됨)
    memset(arr, -1, sizeof(arr));

    return 0;
}

memcmp - 메모리 비교

#include <string.h>
int memcmp(const void *s1, const void *s2, size_t n);
  • 두 메모리 영역을 바이트 단위로 비교
  • 반환값:
    • s1 < s2: 음수
    • s1 == s2: 0
    • s1 > s2: 양수
#include <stdio.h>
#include <string.h>

int main() {
    char str1[] = "Hello";
    char str2[] = "Hello";
    char str3[] = "World";

    // 문자열 비교
    int result1 = memcmp(str1, str2, 5);
    printf("%d\n", result1);  // 0 (같음)

    int result2 = memcmp(str1, str3, 5);
    printf("%d\n", result2);  // 음수 (str1 < str3)

    // 배열 비교
    int arr1[] = {1, 2, 3};
    int arr2[] = {1, 2, 3};
    int arr3[] = {1, 2, 4};

    int cmp1 = memcmp(arr1, arr2, sizeof(arr1));
    printf("%d\n", cmp1);  // 0 (같음)

    int cmp2 = memcmp(arr1, arr3, sizeof(arr1));
    printf("%d\n", cmp2);  // 음수 (arr1 < arr3)

    return 0;
}

memchr - 메모리에서 문자 검색

#include <string.h>
void *memchr(const void *s, int c, size_t n);
  • 메모리 영역에서 특정 문자를 검색
  • 반환값: 찾으면 해당 포인터, 못 찾으면 NULL
#include <stdio.h>
#include <string.h>

int main() {
    char str[] = "Hello, World!";

    // 'W' 문자 찾기
    char *found = (char *)memchr(str, 'W', strlen(str));
    if (found) {
        printf("찾음: %s\n", found);  // "World!"
    }

    // 배열에서 특정 값 찾기
    int arr[] = {10, 20, 30, 40, 50};
    int *ptr = (int *)memchr(arr, 30, sizeof(arr));
    if (ptr) {
        printf("값 30을 찾았습니다: 인덱스 %ld\n", ptr - arr);
    }

    return 0;
}

문자열 함수 (string.h)

strlen - 문자열 길이

#include <string.h>
size_t strlen(const char *s);
  • NULL 문자(\0)를 제외한 문자열 길이 반환
#include <stdio.h>
#include <string.h>

int main() {
    char str[] = "Hello";
    printf("%zu\n", strlen(str));  // 5

    char *ptr = "World";
    printf("%zu\n", strlen(ptr));  // 5

    return 0;
}

strcpy - 문자열 복사

#include <string.h>
char *strcpy(char *dest, const char *src);
  • srcdest로 복사 (NULL 문자 포함)
  • dest는 충분한 크기여야 함
  • 반환값: dest 포인터
#include <stdio.h>
#include <string.h>

int main() {
    char src[] = "Hello, World!";
    char dest[50];

    strcpy(dest, src);
    printf("%s\n", dest);  // "Hello, World!"

    return 0;
}

strncpy - 제한된 문자열 복사

#include <string.h>
char *strncpy(char *dest, const char *src, size_t n);
  • 최대 n 문자까지 복사
  • nsrc 길이보다 크면 나머지는 NULL 문자로 채움
  • 반환값: dest 포인터
#include <stdio.h>
#include <string.h>

int main() {
    char src[] = "Hello, World!";
    char dest[20];

    strncpy(dest, src, 5);
    dest[5] = '\0';  // NULL 문자 추가 필요
    printf("%s\n", dest);  // "Hello"

    return 0;
}

strcat - 문자열 연결

#include <string.h>
char *strcat(char *dest, const char *src);
  • srcdest 끝에 연결
  • dest는 충분한 크기여야 함
  • 반환값: dest 포인터
#include <stdio.h>
#include <string.h>

int main() {
    char dest[50] = "Hello";
    char src[] = ", World!";

    strcat(dest, src);
    printf("%s\n", dest);  // "Hello, World!"

    return 0;
}

strncat - 제한된 문자열 연결

#include <string.h>
char *strncat(char *dest, const char *src, size_t n);
  • src에서 최대 n 문자를 dest 끝에 연결
  • 자동으로 NULL 문자 추가
  • 반환값: dest 포인터
#include <stdio.h>
#include <string.h>

int main() {
    char dest[50] = "Hello";
    char src[] = ", World!";

    strncat(dest, src, 3);  // ", W"만 추가
    printf("%s\n", dest);  // "Hello, W"

    return 0;
}

strcmp - 문자열 비교

#include <string.h>
int strcmp(const char *s1, const char *s2);
  • 두 문자열을 사전식으로 비교
  • 반환값:
    • s1 < s2: 음수
    • s1 == s2: 0
    • s1 > s2: 양수
#include <stdio.h>
#include <string.h>

int main() {
    char str1[] = "apple";
    char str2[] = "banana";
    char str3[] = "apple";

    printf("%d\n", strcmp(str1, str2));  // 음수 ("apple" < "banana")
    printf("%d\n", strcmp(str1, str3));  // 0 (같음)
    printf("%d\n", strcmp(str2, str1));  // 양수 ("banana" > "apple")

    return 0;
}

strncmp - 제한된 문자열 비교

#include <string.h>
int strncmp(const char *s1, const char *s2, size_t n);
  • 최대 n 문자까지만 비교
#include <stdio.h>
#include <string.h>

int main() {
    char str1[] = "apple";
    char str2[] = "application";

    // 처음 3글자만 비교
    printf("%d\n", strncmp(str1, str2, 3));  // 0 (같음: "app")
    printf("%d\n", strncmp(str1, str2, 5));  // 음수 ("apple" < "appli")

    return 0;
}

strchr - 문자 검색

#include <string.h>
char *strchr(const char *s, int c);
  • 문자열에서 첫 번째로 나타나는 문자 c 검색
  • 반환값: 찾으면 해당 포인터, 못 찾으면 NULL
#include <stdio.h>
#include <string.h>

int main() {
    char str[] = "Hello, World!";

    char *found = strchr(str, 'o');
    if (found) {
        printf("찾음: %s\n", found);  // "o, World!"
        printf("인덱스: %ld\n", found - str);  // 4
    }

    // 모든 'o' 찾기
    char *ptr = str;
    while ((ptr = strchr(ptr, 'o')) != NULL) {
        printf("위치: %ld\n", ptr - str);
        ptr++;  // 다음 위치부터 검색
    }

    return 0;
}

strrchr - 마지막 문자 검색

#include <string.h>
char *strrchr(const char *s, int c);
  • 문자열에서 마지막으로 나타나는 문자 c 검색
#include <stdio.h>
#include <string.h>

int main() {
    char str[] = "Hello, World!";

    char *found = strrchr(str, 'o');
    if (found) {
        printf("마지막 'o' 위치: %ld\n", found - str);  // 8
        printf("%s\n", found);  // "orld!"
    }

    return 0;
}

strstr - 부분 문자열 검색

#include <string.h>
char *strstr(const char *haystack, const char *needle);
  • haystack에서 needle 부분 문자열 검색
  • 반환값: 찾으면 해당 포인터, 못 찾으면 NULL
#include <stdio.h>
#include <string.h>

int main() {
    char str[] = "Hello, World!";
    char search[] = "World";

    char *found = strstr(str, search);
    if (found) {
        printf("찾음: %s\n", found);  // "World!"
        printf("인덱스: %ld\n", found - str);  // 7
    }

    return 0;
}

strtok - 문자열 토큰화

#include <string.h>
char *strtok(char *str, const char *delim);
  • 문자열을 구분자로 분리
  • 첫 번째 호출: str 전달
  • 이후 호출: NULL 전달 (이전 문자열 계속 사용)
  • 반환값: 토큰 포인터, 더 이상 없으면 NULL
#include <stdio.h>
#include <string.h>

int main() {
    char str[] = "apple,banana,cherry";
    char *token;

    // 첫 번째 토큰
    token = strtok(str, ",");
    while (token != NULL) {
        printf("%s\n", token);
        token = strtok(NULL, ",");  // 다음 토큰
    }
    // 출력:
    // apple
    // banana
    // cherry

    // 여러 구분자 사용
    char str2[] = "apple;banana,cherry:grape";
    token = strtok(str2, ";,:");  // 세미콜론, 쉼표, 콜론 모두 구분자
    while (token != NULL) {
        printf("%s\n", token);
        token = strtok(NULL, ";,:");
    }

    return 0;
}

strspn - 일치하는 문자 개수

#include <string.h>
size_t strspn(const char *s, const char *accept);
  • s의 시작 부분에서 accept에 포함된 문자만 연속으로 나타나는 개수 반환
#include <stdio.h>
#include <string.h>

int main() {
    char str[] = "12345abc";
    char digits[] = "0123456789";

    size_t len = strspn(str, digits);
    printf("%zu\n", len);  // 5 (처음 5개가 숫자)

    return 0;
}

strcspn - 일치하지 않는 문자 개수

#include <string.h>
size_t strcspn(const char *s, const char *reject);
  • s의 시작 부분에서 reject에 포함되지 않은 문자만 연속으로 나타나는 개수 반환
#include <stdio.h>
#include <string.h>

int main() {
    char str[] = "hello123";
    char digits[] = "0123456789";

    size_t len = strcspn(str, digits);
    printf("%zu\n", len);  // 5 (처음 5개가 숫자가 아님)

    return 0;
}

안전한 함수 (C11)

strcpy_s, strcat_s 등

C11 표준에서는 더 안전한 함수들이 추가되었습니다 (일부 컴파일러에서만 지원).

// 예시 (Microsoft Visual C++ 등)
errno_t strcpy_s(char *dest, size_t destsz, const char *src);
errno_t strcat_s(char *dest, size_t destsz, const char *src);

주의사항

  1. 버퍼 오버플로우: strcpy, strcat 등은 버퍼 크기를 확인하지 않음
  2. NULL 포인터: 모든 함수는 유효한 포인터를 받아야 함
  3. 메모리 겹침: memcpy는 메모리가 겹치면 안 됨 (겹치면 memmove 사용)
  4. NULL 문자: 문자열 함수는 NULL 문자(\0)를 기준으로 동작
  5. strtok의 재진입 불가: strtok는 내부 정적 변수를 사용하므로 스레드 안전하지 않음

실전 예제

문자열 뒤집기

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

void reverse_string(char *str) {
    int len = strlen(str);
    for (int i = 0; i < len / 2; i++) {
        char temp = str[i];
        str[i] = str[len - 1 - i];
        str[len - 1 - i] = temp;
    }
}

int main() {
    char str[] = "Hello";
    reverse_string(str);
    printf("%s\n", str);  // "olleH"
    return 0;
}

문자열에서 숫자 추출

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

void extract_numbers(const char *str) {
    while (*str) {
        if (isdigit(*str)) {
            printf("%c", *str);
        }
        str++;
    }
    printf("\n");
}

int main() {
    extract_numbers("abc123def456");  // "123456"
    return 0;
}

메모리 안전한 문자열 복사

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

void safe_strcpy(char *dest, size_t dest_size, const char *src) {
    if (dest == NULL || src == NULL || dest_size == 0) {
        return;
    }

    size_t src_len = strlen(src);
    size_t copy_len = (src_len < dest_size - 1) ? src_len : dest_size - 1;

    memcpy(dest, src, copy_len);
    dest[copy_len] = '\0';
}

int main() {
    char dest[10];
    safe_strcpy(dest, sizeof(dest), "Hello, World!");
    printf("%s\n", dest);  // "Hello, Wo" (안전하게 잘림)
    return 0;
}