C 언어 코드 snippet

C 언어 코드 snippet

2019, Oct 20    


목차


  • 매크로 모음

  • 변수명 출력

  • 숫자 → 문자열

  • 문자열 → 숫자

  • 정수 → 바이너리 문자열

  • 텍스트 입/출력

  • 파일 존재하는 지 확인

  • 기본 문자열 관련 함수

  • 특정 문자를 기준으로 문자열 split

  • 부분 문자열 (substr) 저장

  • 부분 문자열 (substr) 출력

  • 현재 시간 출력

  • 정렬 (quick sort)


머신러닝 / 딥러닝 관련 모음


  • softmax 함수

  • entropy 함수


매크로 모음


  • 타입에 상관없이 사용할 수 있는 함수 형태의 매크로 또는 자주 사용할 수 있는 상수들을 모았습니다.


#define PI 3.141592
#define TRUE 1
#define FALSE 0
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
#define ABS(x) ((x)<0 ? -(x) : (x))
#define INF 999999999


변수명 출력


#include <stdio.h>

 #define GET_VARIABLE_NAME(variable, holder) sprintf(holder, "%s", #variable)

int main() {
    char variable_name[100]; // 출력할 변수명을 저장할 문자열
    GET_VARIABLE_NAME(print_this_variable, variable_name);
    puts(variable_name); // print_this_variable이 출력됩니다.
    return 0;
}


숫자 → 문자열


  • 숫자를 문자로 변경할 때에는 sprintf 함수를 사용합니다.


#include <stdio.h>
#include <stdlib.h>

int main(){

    int a=123;
    int b=0;
    char buf[10];
    
    sprintf(buf, "%d", a);
    printf("%s\n",buf); 


문자열 → 숫자


  • 문자열을 숫자로 바꿀 때에는 아래 함수를 사용하여 바꿀 수 있습니다.

int StringToInteger(char *str) {
    int ret = 0;
    while (*str) {
        ret = ret * 10 + (int)(*str - '0');
        str++;
    }
    return ret;
}


정수 → 바이너리 문자열


  • 아래 코드는 정수를 입력 받아서 바이너리 형태로 출력하는 코드 입니다. 그 정수의 이진수 값을 알고 싶을 때 사용하시면 됩니다.


void PrintIntToBinary(int n){

    char str[100];
    int i = 0;

    printf("%d : ", n);
    do{
        if (n % 2 == 0){
            str[i] = '0';
        }
        else{
            str[i] = '1';
        }
        n /= 2;
        i++;
    }while(n > 0);

    while(i > 0){
        printf("%c", str[--i]);
    }
    printf("\n");
}


텍스트 입/출력


  • FILE 포인터와 fprintf를 이용하여 텍스트 파일에 출력하는 방법입니다.
  • 아래 코드와 같이 사용 시 fprintf 함수의 파일 포인터에 해당하는 파일에는 fprintf를 통해 출력한 값만 적혀집니다.
  • 물론 printf를 통하여 출력한 출력은 콘솔창에 기존 그대로 찍히게 됩니다.


#include <stdio.h>

int main(){
    // "a" 적용 시 파일 끝에서 부터 계속 이어서 쓴다.
    // "w" 적용 시 파을을 덮어쓰기 한다.
    FILE* fp = fopen("output.txt", "w"); 
    fprintf(fp, "%d %lf\n", 1, 2.0);
    
    return 0;
}


  • 이번에는 반대로 FILE 포인터와 fscanf를 이용하여 텍스트 파일을 읽어서 입력하는 방법입니다.
  • 기본적인 사용법은 scanf와 유사하며 파라미터로 File Pointer를 넘겨주는 것에 차이가 있습니다.


#include <stdio.h>

int main(){
    // "a" 적용 시 파일 끝에서 부터 계속 이어서 쓴다.
    // "w" 적용 시 파을을 덮어쓰기 한다.
    int a;
    double b;
    FILE* fp = fopen("input.txt", "r"); 
    fscanf(fp, "%d %lf\n", &a, &b);
    
    return 0;
}


  • fscanf 함수가 더 이상 읽을 것이 없으면 -1을 반환합니다.
  • 아래 코드와 같이 fscanf가 -1을 반환할 때 까지 파일을 읽으면 됩니다.


#include <stdio.h>
#include <stdlib.h>

int main() {

	FILE* fp = fopen("input.txt", "r");
	int a;
	int ret = 1;
	while (1) {
		ret = fscanf(fp, "%d", &a);		
		if (ret < 0) {
			break;
		}
		printf("%d\n", a);
	}
}


파일 존재하는 지 확인


  • 다음은 파일 경로를 입력하였을 때, 그 파일이 존재하는 지 확인하는 코드 입니다.


// 입력 받은 경로에 파일이 존재하는 지 확인한다.
int FileExists(const char *file_name){
    FILE *file;
    int ret;
    if ((file = fopen(file_name, "r"))){
        fclose(file);
        ret = 1;
    }
    else{
        ret = 0;
    }

    return ret;
}


기본 문자열 관련 함수


  • strlen : 문자열 길이 반환
    • 예) strlen(str)
  • strcmp : 문자열 비교
    • 예) strcmp(str1, str2)
  • strncmp : 문자열 n개 비교
    • 예) strcmp(str1, str2, 8)
  • strcpy : 문자열을 복사하는 함수 인자로 주소값을 받습니다.
    • 예) strcpy(dest, src). 여기서 dest는 복사를 할 곳의 시작 주소이고 src는 복사할 문자열의 시작 주소입니다.
    • 따라서 문자열의 시작 주소를 적절하게 응용하여 사용하면 좋습니다.
  • strncpy: 문자열 n개 복사
    • 예) strncpy(dest, src, 8)
  • strcat : 새로운 문자열을 기존 문자열 끝에 붙입니다. 따라서 기존 문자열의 NULL을 찾고 그 위치에서 부터 새로운 문자열을 붙입니다.
    • 예) strcat(dest, src)
  • strncat : 새로운 문자열을 기존 문자열 끝에 n개 붙입니다.
    • 예) strncat(dest, src, 8)


특정 문자를 기준으로 문자열 split


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

// str배열을 delim의 문자들로 split한 후 tokenize 배열에 시작 인덱스들을 저장하고 저장된 인덱스의 갯수를 반환한다.
// 사용 방법 : StringTokenizer(str, delim, tokenize, sizeof(tokenize)/sizeof(int));
int StringTokenizer(const char* str, const char* delim, int* tokenize, int max_tokenize_index){
    
    int token_count = 0;
    int str_len = strlen(str);    
    
    // initialize string and tokenize
    char *s;    
    s = (char*)malloc(sizeof(char)*(str_len + 1));
    strcpy(s, str);
    memset(tokenize, -1, (int)sizeof(tokenize));

    char *start_point = s;
    char *ptr = strtok(s, delim);
    
    while (ptr != NULL){

        if(token_count >= max_tokenize_index){
            printf("The number of token exceeds maximum index of tokenize array.\n");
            exit(1);
        }
        tokenize[token_count++] = (int)(ptr - start_point); 		
		ptr = strtok(NULL, delim);
    }

    tokenize[token_count] = str_len;

    free(s);
    return token_count;
}


부분 문자열 (substr) 저장


  • 단순히 부분문자열을 구하려면 strncpy 함수를 사용 하면 됩니다.
    • strncpy(dest, str + 시작인덱스, 길이)
  • 범위를 이용하여 부분 문자열을 구하려면 다음 함수를 사용하면 됩니다. SubstringToDest 함수는 파라미터로 받은 dest 배열에 부분 문자열을 저장하는 형태이고 GetSubstring은 함수 내에서 동적 할당을 하여 반환값으로 새로운 부분 문자열을 반환하는 방법입니다. 범위는 [begin, end) 입니다.


// str 문자열에서 [begin, end) 범위의 부분 문자열을 반환한다.
// malloc을 사용하였으므로 free로 해제해주어야 한다.
char* GetSubstring(const char *str, int begin, int end){	
    char *ret;
    int len = end - begin;
    ret = (char*)malloc(sizeof(char) * (len + 1));	
    strncpy(ret, (str + begin), len);	
	return ret;
}

// str 문자열에서 [begin, end) 범위의 부분 문자열을 dest에 저장한다.
void SubstringToDest(const char* str, char* dest, int begin, int end){
    strncpy(dest, str + begin, end - begin);
}


부분 문자열 (substr) 출력


  • 부분 문자열을 출력하는 함수 입니다.


void PrintSubstring(const char* str, int begin, int end){
    for(int i = begin; i < end; ++i){
        printf("%c", str[i]);
    }
    printf("\n");
}


현재 시간 출력


  • C에서 현재 시간을 출력할 때, time.h 헤더의 time_tlocaltime 함수를 사용할 수 있습니다.
  • 아래 코드를 사용하면 year, month, day, hour, minute, second 를 각각 출력할 수 있습니다.


time_t t = time(NULL);
struct tm tm = *localtime(&t);
printf("%d-%d-%d %d:%d:%d\n", tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);


  • tm 구조체에 대하여 알아보면 다음과 같습니다. 타입은 모두 정수형 입니다.


struct tm {
   int tm_sec;         /* second,  range 0 to 59            */
   int tm_min;         /* minute, range 0 to 59             */
   int tm_hour;        /* hour, range 0 to 23            */
   int tm_mday;        /* day, range 1 to 31             */
   int tm_mon;         /* month, range 0 to 11             */
   int tm_year;        /* year, from 1900년                */
   int tm_wday;        /* day of the week, range sunday(0) to saturday(6) */
   int tm_yday;        /* elapsed day of year, range 0 to 365  */
   int tm_isdst;       /* summer time                        */
};


정렬 (quick sort)



int comparator(int left, int right){
    return left < right; // ascending
    // return left > right; // descending
}

void swap(int* arr, int i, int j){

    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
    
}

void quick_sort(int* arr, int lo, int hi){
    if (hi - lo <= 0){
        return;
    }

    int pivot = arr[lo + (hi - lo + 1) / 2];
    int i = lo, j = hi;

    while(i <= j){
        while(comparator(arr[i], pivot)) i++;
        while(comparator(pivot, arr[j])) j--;

        if (i > j){
            break;
        }

        swap(arr, i, j);        

        i++;
        j--;
    }

    quick_sort(arr, lo, j);
    quick_sort(arr, i, hi);
}

int main(){
    int arr[10];
    for(int i=0; i < 10; ++i){
     arr[i] = rand() % 100;
    }
    quick_sort(arr, 0, 10);
}


  • 위 코드는 퀵 정렬을 나타낸 것이며 정렬 기준은 comparator를 사용하면 됩니다.
  • swap 함수를 통하여 값을 교환하면 됩니다.
  • 위 코드와 같이 comparatorswap을 분리한 이유는 구조체나 특수한 정렬 기준을 준 경우 편리하게 적용하기 위함입니다.
  • 구조체를 사용하는 경우 pivot의 자료형도 구조체에 맞게 사용하면 됩니다.


  • 다음과 같이 응용할 수 있습니다.



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

int comparator(int left, int right){
    return left < right; // ascending
    // return left > right; // descending
}

void swap(int arr[], char strs[][40], int i, int j){

    char temp_str[40];
    strcpy(temp_str, strs[i]);
    strcpy(strs[i], strs[j]);
    strcpy(strs[j], temp_str);


    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

void quick_sort(int arr[], char strs[][40], int lo, int hi){
    if (hi - lo <= 0){
        return;
    }

    int pivot = arr[lo + (hi - lo + 1) / 2];
    int i = lo, j = hi;

    while(i <= j){
        while(comparator(arr[i], pivot)) ++i;
        while(comparator(pivot, arr[j])) --j;

        if (i > j){
            break;
        }

        swap(arr, strs, i, j);        

        i++;
        j--;
    }

    quick_sort(arr, strs, lo, j);
    quick_sort(arr, strs, i, hi);
}

int main(void) {

    int arr[13];
    char strs[13][40];

    for(int i=0; i < 10; ++i){
        arr[i] = rand() % 100;
    }

    strcpy(strs[0], "01");
    strcpy(strs[1], "02");
    strcpy(strs[2], "03");
    strcpy(strs[3], "04");
    strcpy(strs[4], "05");
    strcpy(strs[5], "06");
    strcpy(strs[6], "07");
    strcpy(strs[7], "08");
    strcpy(strs[8], "09");
    strcpy(strs[9], "10");

    for(int i = 0; i < 10; ++i){
        printf("%d %s\n", arr[i], strs[i]);
    }

    printf("\n\n");

    quick_sort(arr, strs, 0, 9);

   for(int i = 0; i < 10; ++i){
        printf("%d %s\n", arr[i], strs[i]);
    }
    
}


머신러닝 / 딥러닝 관련 모음


softmax 함수


  • 아래 코드는 (h, w, c) 크기의 이미지가 있을 때, 채널 c 방향으로 softmax를 적용하는 함수 입니다.



#define MAX(a,b) (((a)>(b))?(a):(b))

void set_softmax(float probablities[], int num_classes) {
	float max_value = -999;
	float denominator = 0;
	for (int i = 0; i < num_classes; ++i) {
		max_value = MAX(max_value, probablities[i]);
	}
	for (int i = 0; i < num_classes; ++i) {
		probablities[i] -= max_value;
		probablities[i] = expf(probablities[i]);
		denominator += probablities[i];
	}
	for (int i = 0; i < num_classes; ++i) {
		probablities[i] /= denominator;
	}
}