본문 바로가기

Software

Secure Cordng C - Coding Style

2-1 코딩 스타일의 이해


1. 코딩(Coding)


일반적으로 프로그래밍 언어를 사용하여 원시 코드 또는 소스 코드를 작성하는 행위를 코딩이라고 한다.


2. 프로그래밍과 코딩(Programming and Coding)


코딩과 프로그래밍은 유사한 개념으로 사용되지만 엄밀히 이야기하면 다음과 같다.

  • 프로그래밍 - 프로그램의 논리를 설계하는것
  • 코딩 - 프로그램의 논리에 따라 특정 언어를 사용하여 구현

3. 코딩 스타일(Cording Style)

원시 코드 또는 소스 코드를 작성하는 방식을 보통 코딩 스타일이라고 하며, 유사한 개념으로 '코딩 습관(Coding convention)'이 있다.

4. 코딩 스타일의 목적

코딩 스타일의 목적은 여러 가지가 존재하며 대표적으로 다음과 같은 것들이 있다.
  • 개발 기간의 지연 방지
  • 유지 보수에 대한 편리성
  • 견고한 프로그램의 구현

5. C 코딩 스타일의 종류

대표적으로 다음의 3가지가 존재한다.
  • K&R
  • GNU
  • BSD
자세한 내용은 다음의 웹사이트에서 참고하기 바란다.(https://namu.wiki/w/%EC%BD%94%EB%94%A9%20%EC%8A%A4%ED%83%80%EC%9D%BC)


개인적으로 나는 K&R Style을 선호한다. 흔히 C계열 창시자들이 사용하던 방식이며,
코드 수를 절약하며 한눈에 많은 코드를 볼 수 있고, 수평으로 많은 코드를 작성할 수 있다.
(Java계열 eclipse/구글 C++/JavaScript의 기본 포멧팅이다)



2-2 가급적 한 줄에 한 문장만 사용하라

1. 좋지 않은 코드

관련된 코드를 한 줄에 작성하게 되면 가독성이 떨어지게 되고 유지 보수 하기가 어려워진다.
그리고 이는 버그의 위험이 될 수 있다.

#include <stdio.h>

int main()
{
int num1; int num2; int sum;
printf("Input number : ");    scanf("%d", &num1);
printf("Input number : ");    scanf("%d", &num2);
sum = num1 + num2;        printf("%d + %d = %d\n", num1, num2, sum);
return 0;
}


해결 방법 - 한 줄에 한 문장만 기술한다.


#include <stdio.h>


int main()
{
int num1, num2;

printf("Input number : ");    
scanf("%d", &num1);

printf("Input number : ");
scanf("%d", &num2);

int sum = num1 + num2;
printf("%d + %d = %d\n", num1, num2, sum);

return 0;
}



2-3 서로 의미가 있는 코드는 단락으로 묶어라


1. 좋지 않은 코드


다음의 코드는 가독성을 떨어트리고 유지 보수하기도 어렵다.

#include <stdio.h>

#define TRUE    (1)

#define FALSE   (2)

#define STACK_SIZE    (10)

typedef struct _Stack{

int arr[STACK_SIZE];

int top;

}Stack;

void init_stakc(Stack* stack){ stack->top = 0; }

int is_full(const Stack* stack) { return stack->top = STACK_SIZE; }

int is_empty(const Stack *stack) { return stack->top = 0; }

int push(Stack* stack, int data){

if(is_full(stack))

return -1;

stack->arr[(stack->top)++] = data;

return 0;

}

int pop(Stack* stack, int* data){

if(is_empty(stack) || data == NULL)

return -1;

*data = stack->arr[--(stack->top)];

return 0;

}


int main(){

Stack stack;

init_stack(&stack);

int i;

for(i=0; i < 5; i++){

push(&stack, i + 1);

}


for(i=0; i < 5; i++){

int data;

pop(&stack, &data);

printf("%d\n", data);

}

return 0;

}



해결 방법 - 서로 의미가 있는 코드는 다음과 같이 단락으로 묶는다.


#include <stdio.h>


#define TRUE    (1)

#define FALSE   (2)


typedef struct _Stack{

#define STACK_SIZE    (10)

int arr[STACK_SIZE];

int top;

}Stack;


void init_stakc(Stack* stack) {

stack->top = 0; 

}


int is_full(const Stack* stack) { 

return stack->top = STACK_SIZE; 

}


int is_empty(const Stack *stack) { 

return stack->top = 0; 

}


int push(Stack* stack, int data){

if(is_full(stack))

return -1;


stack->arr[(stack->top)++] = data;

return 0;

}


int pop(Stack* stack, int* data){

if(is_empty(stack) || data == NULL)

return -1;


*data = stack->arr[--(stack->top)];

return 0;

}


int main(){

Stack stack;

init_stack(&stack);

int i;


for(i=0; i < 5; i++){

push(&stack, i + 1);

}


for(i=0; i < 5; i++){

int data;

pop(&stack, &data);

printf("%d\n", data);

}


return 0;

}




2-4 연산자의 앞과 뒤에 공백을 두라


1. 좋지 않은 코드 - 코드의 가독성이 떨어진다


#include <stdio.h>

#include <stdlib.h>


typedef struct _Node{

int data;

struct _Node* next;

} Node;


void insert_front(Node **head,int data){

Node *node =(Node*)malloc(sizeof(Node));

node->data=data;

node->next=*head;

*head=node;

}


void display_list(const Node **head){

system("cls");

printf("[head]");

for(Node *node=*head; node!=NULL; node=node->next)

printf("->[%2d]", node->data);

getchar();

}


int main(){

Node *head=NULL;

display_list(&head);

int i;

for(i=0; i<5; i++){

insert_front(&head, i+1);

display_list(&head);

}


return 0;

}



해결방법 - 연산자의 앞과 뒤에 공백을 추가한다.


#include <stdio.h>

#include <stdlib.h>


typedef struct _Node{

int data;

struct _Node* next;

} Node;


void insert_front(Node **head, int data){

Node *node = (Node*)malloc(sizeof(Node));

node->data = data;

node->next = *head;

*head = node;

}


void display_list(const Node **head){

system("cls");

printf("[head]");

for(Node *node = *head; node != NULL; node = node->next)

printf("->[%2d]", node->data);

getchar();

}


int main(){

Node *head = NULL;

display_list(&head);

int i;

for(i = 0; i < 5; i++){

insert_front(&head, i + 1);

display_list(&head);

}


return 0;

}




2-5 중괄호의 위치를 통일 시켜라


1. 좋지 않은 코드


중괄호의 위치가 제각각이어서 가독성이 떨어진다.


#include <stdio.h>

#include <stdlib.h>


typedef struct _Node{

int data;

struct _Node* next;

} Node;


typedef struct _List { Node* head; } List;


void init_list(List *list) { list->head = NULL; }


void insert_front(Node **head, int data)

{

Node *node = (Node*)malloc(sizeof(Node));

node->data = data;

node->next = *head;

*head = node;

}


void display_list(const Node **head){

system("cls");

printf("[head]");

for(Node *node = *head; node != NULL; node = node->next)

printf("->[%2d]", node->data);

getchar();

}


int main()

{

Node *head = NULL;

display_list(&head);

int i;


for(i = 0; i < 5; i++)

{

insert_front(&head, i + 1);

display_list(&head);

}


return 0;

}



해결 방법 - 중괄호의 위치를 일관되게 사용한다.


#include <stdio.h>

#include <stdlib.h>


typedef struct _Node {

int data;

struct _Node* next;

} Node;


typedef struct _List { 

Node* head; 

} List;


void init_list(List *list) { 

list->head = NULL; 

}


void insert_front(Node **head, int data) {

Node *node = (Node*)malloc(sizeof(Node));

node->data = data;

node->next = *head;

*head = node;

}


void display_list(const Node **head) {

system("cls");

printf("[head]");

for(Node *node = *head; node != NULL; node = node->next)

printf("->[%2d]", node->data);

getchar();

}


int main() {

Node *head = NULL;

display_list(&head);

int i;


for(i = 0; i < 5; i++) {

insert_front(&head, i + 1);

display_list(&head);

}


return 0;

}



2-6 변수의 이름에 적절한 접두사를 사용하라


다음은 변수에 관행적으로 사용되는 접두사들이다.


접두사 

 의미

 arr

배열(Array) 

 b

불리언(boolean) 

 c 

문자(character) 

 i

정수(integer) 

 f 

단정도 실수(float) 

 d 

배정도 실수(double) 

 fd 

파일 기술자(file descriptor) 

 fp 

파일 포인터(file pointer) 

 h 

핸들(handle) 

 p 

일중 포인터(single pointer) 

 pp 

이중 포인터(double pointer) 

 s 

정적 변수(static variable) 

 g 

전역 변수(global variable) 

 str 

문자열(string) 



1. 좋지 않은 코드


실수 연산의 결과를 정수 타입의 변수에 저장하고 있다.


#include <stdio.h>


int main(){

double num1 = 0.1;

double num2 = 0.2;


//...


int sum = num1 + num2;

printf("%d\n", sum);


return 0;

}



해결 방법 - 접두사를 사용한다.


#include <stdio.h>


int main(){

double dNum1 = 0.1;

double dNum2 = 0.2;


//...


double dSum = dNum1 + dNum2;

printf("%d\n", dSum);


return 0;

}



2. 좋지 않은 코드


다음 코드는 잘못된 메모리 참조를 수행하고 있다.


#include <stdio.h>

#include <stdlib.h>

#include <string.h>


int get_name(char **name){

char buf[32];


printf("Input name: ");

scanf("%s", buf);


char* p = malloc(strlen(buf) + 1);

if(p == NULL){

perror("malloc");

return -1;

}


strcpy(p, buf);

name = p;


return 0;

}


int main(){

char *name;


get_name(&name);

printf("-> %s\n", name);


free(name);

return 0;

}



해결 방법 - 포인터 접두사를 사용한다.


#include <stdio.h>

#include <stdlib.h>

#include <string.h>


int get_name(char **ppName){

char buf[32];


printf("Input name: ");

scanf("%s", buf);


char* p = malloc(strlen(buf) + 1);

if(p == NULL){

perror("malloc");

return -1;

}


strcpy(p, buf);

ppName = p;


return 0;

}


int main(){

char *pName;


get_name(&pName);

printf("-> %s\n", pName);


free(pName);


return 0;

}



3. 좋지 않은 코드


insert_front 함수 내부에서 전역 변수와 동일한 심볼을 사용하고 있다.


#include <stdio.h>

#include <stdlib.h>


typedef struct _Node {

int data;

struct _Node *next;

} Node;


Node *head = NULL;


void insert_front(int data){

Node *node = malloc(sizeof(Node));

node->data = data;


//...

Node *head;


node->next = head;

head = node;

}


Node *remove_front(){

Node* node = head;

head = node->next;


return node;

}


int main(){

int i;

for(i = 0; i < 5; i++)

insert_front(i + 1);


Node *node = remove_front();

free(node);


return 0;

}



해결 방법 - 전역 변수를 식별하기 위한 접두사를 사용한다.


#include <stdio.h>

#include <stdlib.h>


typedef struct _Node {

int data;

struct _Node *next;

} Node;


Node *pHead = NULL;


void insert_front(int data){

Node *node = malloc(sizeof(Node));

node->data = data;


//...

Node *head;


node->next = pHead;

pHead = node;

}


Node *remove_front(){

Node* node = pHead;

pHead = node->next;


return node;

}


int main(){

int i;

for(i = 0; i < 5; i++)

insert_front(i + 1);


Node *node = remove_front();

free(node);


return 0;

}




2-7 함수의 기능을 식별할 수 있도록 접두사를 사용하라


다음은 함수에 관행적으로 사용되는 접두사이다.


접두사

의미

get 

어떤 값을 얻어온다

get_age 

set 

떤 값을 저장한다 

set_age 

is 

어떤 것에 대해 묻는다 

is_full 

check 

어떤 것에 대해 조사한다 

check_error 



1. 좋지 않은 코드


다음의 코드에서 함수의 기능을 유추하기 어렵다.


#include <stdio.h>

#include <stdlib.h>

#include <string.h>


int name(char **ppName){

if(ppName == NULL)

return -1;


char buf[32];


printf("Input name : ");

scanf("%s", buf);


char *pName = (char*)malloc(strlen(buf) + 1);

if(pName == NULL){

error("malloc");

return -1;

}


strcpy(pName, buf);

**ppName = pName;


return 0;

}


int main(){

char *pName = NULL;


if(name(&pName) < 0)

return -1;

printf("-> %s\n", pName);


free(pName);

return 0;

}



해결 방법 - 함수의 기능을 식별할 수 있는 접두사를 사용한다.


#include <stdio.h>

#include <stdlib.h>

#include <string.h>


int get_name(char **ppName){

if(ppName == NULL)

return -1;


char buf[32];


printf("Input name : ");

scanf("%s", buf);


char *pName = (char*)malloc(strlen(buf) + 1);

if(pName == NULL){

error("malloc");

return -1;

}


strcpy(pName, buf);

**ppName = pName;


return 0;

}


int main(){

char *pName = NULL;


if(get_name(&pName) < 0)

return -1;

printf("-> %s\n", pName);


free(pName);

return 0;

}




2-8 이름은 의미 있고 간결하게 작성하라


1. 좋지 않은 코드


선언된 변수가 어떠한 의미를 갖는지 유추하기 어렵다.


#include <stdio.h>

#include <stdlib.h>


int ** create_matrix(int a, int b){

int **c = (int**)malloc(sizeof(int*) * a);

int iterator;


for(iterator = 0; iiterator < a; iterator++)

c[iterator] = (int*)calloc(b, sizeof(int));


return c;

}


int main(){

int **z = create_matrix(2, 3);

int a, b;


for(a = 0; a < 2; a++){

for(b = 0; b < 3; b++)

printf("%2d ", z[a][b]);

printf("\n");

}


free(z);

return 0;

}



해결 방법 - 의미 있는 이름으로 변경한다.


#include <stdio.h>

#include <stdlib.h>


int ** create_matrix(int row, int col){

int **matrix = (int**)malloc(sizeof(int*) * a);

int i;


for(i = 0; i < row; i++)

c[i] = (int*)calloc(col, sizeof(int));


return matrix;

}


int main(){

int **matrix = create_matrix(2, 3);

int a, b;


for(i = 0; i < 2; i++){

for(j = 0; j < 3; j++)

printf("%2d ", matrix[i][j]);

printf("\n");

}


free(matrix);

return 0;

}




2-9 포인터 변수의 선언에서 포인터 기호는 변수 이름에 붙여라


1. 좋지 않은 코드


변수 pTel은 문자에 대한 포인터가 아니라 문자를 저장하는 변수이다.


#include <stdio.h>


int main(){

char* pName, pTel;


pName = 0;

pTel = 0;


//...


return 0;

}



해결 방법 - 포인터 기호를 변수에 붙여 선언한다.


#include <stdio.h>


int main(){

char *pName, pTel;


pName = 0;

pTel = 0;


//...


return 0;

}



2-10 하나의 표현식에는 가급적 하나의 연산만 수행하라


1. 좋지 않은 코드


우선 순위에 대한 잘못된 이해로 프로그램은 제대로 동작하지 않는다.


#include <stdio.h>


int main(){

FILE *fp = fopen("main.c", "rb");

if(fp == NULL){

perror("fopen");

return -1;

}


int ch;

while(ch = fgetc(fp) != EOF)

fputc(ch, stdout);

printf("\n");


fclose(fp);

return 0;

}



해결 방법 - 가급적 하나의 표현식에는 하나의 연산만 수행한다.


#include <stdio.h>


int main(){

FILE *fp = fopen("main.c", "rb");

if(fp == NULL){

perror("fopen");

return -1;

}


int ch = fgetc(fp);

while(ch != EOF){

putchar(ch);

ch = fgetc(fp);

printf("\n");


fclose(fp);

return 0;

}

반응형