system programming coding rules
Programming/C / 2009/09/15 09:32
Preprocessor
- 소스 코드의 헤더 파일은 항상 inclusion guard를 포함해야 한다.
| Compliant Solution |
#ifndef _HEADER_H_ #define _HEADER_H_ /* ... contents of the header */ #endif /* _HEADER_H_ */ |
- 소스 코드 및 주석에서 반복되는 ? 를 사용하지 말아야 한다.
| Noncompliant Code Example | Compliant Solution |
size_t i = /* some initial value */;
if (i > 9000) {
if (puts("Over 9000!??!") == EOF) {
/* Handle Error */
}
}
|
size_t i = /* some initial value */;
/* assignment of i */
if (i > 9000) {
if (puts("Over 9000!?""?!") == EOF) {
/* Handle Error */
}
}
|
Declarations and Initialization
- 함수에 의하여 변경되지 않는 parameter를 위한 pointer는 const로 선언한다.
| Noncompliant Code Example | Compliant Solution |
void foo(int * x) {
if (x != NULL) {
printf("Value is %d\n", *x);
}
/* ... */
}
|
void foo(const int * x) {
if (x != NULL) {
printf("Value is %d\n", *x);
}
/* ... */
}
|
- 내부에서만 사용되는 함수는 항상 static으로 선언한다.
| Noncompliant Code Example | Compliant Solution |
enum { MAX = 100 };
int helper(int i) {
/* perform some computation based on i */
}
int main(void) {
size_t i;
int out[MAX];
for (i = 0; i < MAX; i++) {
out[i] = helper(i);
}
/* ... */
}
|
enum {MAX = 100};
static int helper(int i) {
/* perform some computation based on i */
}
int main(void) {
size_t i;
int out[MAX];
for (i = 0; i < MAX; i++) {
out[i] = helper(i);
}
/* ... */
}
|
- source buff 와 destination buff는 같은 메모리 공간이 되지 않도록 설계한다.
| Noncompliant Code Example |
char str[]= "test string"; char *ptr1=str; char *ptr2; ptr2 = ptr1 + 3; memcpy(ptr2, ptr1, 6); |
Expression
- 두 structure를 비교할때는 memcmp() 함수를 사용하지 않고 직접 비교한다.
| Noncompliant Code Example | Compliant Solution |
struct my_buf {
char buff_type;
size_t size;
char buffer[50];
};
unsigned int buf_compare(
const struct my_buf *s1,
const struct my_buf *s2)
{
if (!memcmp(s1, s2, sizeof(struct my_buf))) {
return 1;
}
return 0;
}
|
struct my_buf {
char buff_type;
size_t size;
char buffer[50];
};
unsigned int buf_compare(
const struct my_buf *s1,
const struct my_buf *s2)
{
if (s1->buff_type != s2->buff_type) return 0;
if (s1->size != s2->size) return 0;
if (strcmp(s1->buffer, s2->buffer) != 0) return 0;
return 1;
}
|
Integer, Float, Array, String
- 배열의 길이를 나타내는 parameter는 int 형대신 size_t, ssize_t 형을 사용한다.
- 32bit 머신과 64bit 머신간의 호환성을 위함
| Noncompliant Code Example | Compliant Solution |
char str = "abc"; int len; len = strlen(str); ... } |
char str = "abc"; size_t len; len = strlen(str); ... |
- atoi() 함수류 대신에 strtol() 함수류를 사용한다.
- atoi() 함수류는 error를 detect할수 없다.
atoi: (int)strtol(nptr, (char **)NULL, 10) atol: strtol(nptr, (char **)NULL, 10) atoll: strtoll(nptr, (char **)NULL, 10)
| Noncompliant Code Example | Compliant Solution |
int si;
if (argc > 1) {
si = atoi(argv[1]);
}
|
long sl;
int si;
char *end_ptr;
if (argc > 1) {
errno = 0;
sl = strtol(argv[1], &end_ptr, 10);
if ((sl == LONG_MIN || sl == LONG_MAX) && errno != 0) {
perror("strtol error");
}
else if (end_ptr == argv[1]) {
if (puts("error encountered during conversion") == EOF) {
/* Handle error */
}
}
else if (sl > INT_MAX) {
printf("%ld too large!\n", sl);
}
else if (sl < INT_MIN) {
printf("%ld too small!\n", sl);
}
else if ('\0' != *end_ptr) {
if (puts("extra characters on input line\n") == EOF) {
/* Handle error */
}
}
else {
si = (int)sl;
}
}
|
- array의 길이 또는 위치를 입력 받는 함수는 항상 입력받은 값이 valid한지 check 해야 한다.
| Noncompliant Code Example | Compliant Solution |
void func(size_t s) {
int vla[s];
/* ... */
}
/* ... */
func(size);
/* ... */
|
enum { MAX_ARRAY = 1024 };
void func(size_t s) {
if (s < MAX_ARRAY && s != 0) {
int vla[s];
/* ... */
} else {
/* Handle error */
}
}
/* ... */
func(size);
/* ... */
|
| Noncompliant Code Example | Compliant Solution |
enum { WORKSPACE_SIZE = 256 };
void func(const int src[], size_t len) {
int dest[WORKSPACE_SIZE];
memcpy(dest, src, len * sizeof(int));
/* ... */
}
|
enum { WORKSPACE_SIZE = 256 };
void func(const int src[], size_t len) {
int dest[WORKSPACE_SIZE];
if (len > WORKSPACE_SIZE/sizeof(int)) {
/* Handle error */
}
memcpy(dest, src, sizeof(int)*len);
/* ... */
}
|
- strcpy(), strcat(), sprintf(), .. 등 destination buff의 크기를 검사하지 않는 문자열 함수는 사용하지 않는다.
| Noncompliant Code Example | Compliant Solution |
#define FILE_PATH_MAX 100
char config_path[FILE_PATH_MAX];
char *env_home;
env_home = getenv("HOME");
if (env_home == NULL) {
return -1;
}
sprintf(config_path, "%s/etc/config.ut", env_home);
|
#define FILE_PATH_MAX 100
char config_path[FILE_PATH_MAX];
char *env_home;
env_home = getenv("HOME");
if (env_home == NULL) {
return -1;
}
snprintf(config_path, sizeof(config_path), "%s/etc/config.ut", env_home);
|
- strncat(), strncpy() 함수들은 dest buff가 부족할경우 null-terminated 되지 않은 불완전한 문자열을 만들수 있으므로 strlcat(), strlcpy() 함수를 사용한다
- strlcat(), strlcpy() 함수는 Solaris 8 이상에서만 지원되며, 다른 운영체제에서는 MPF에서 지원한다.
| Noncompliant Code Example | Compliant Solution |
#define STR_MAX 5 char str[STR_MAX]; strncat(str, "0123456789", sizeof(str)); |
#define STR_MAX 5 char str[STR_MAX]; strlcat(str, "0123456789", sizeof(str)); |
- isalpha(), islower(), isupper(), .. 등 isxxx() 류의 함수사용시 입력문자 c는 항상 unsigned char 형으로 cast하여 사용한다.
int isalnum(int c); int isalpha(int c); int isascii(int c); int isblank(int c); int iscntrl(int c); int isdigit(int c); int isgraph(int c); int islower(int c); int isprint(int c); int ispunct(int c); int isspace(int c); int isupper(int c); int isxdigit(int c);
| Noncompliant Code Example | Compliant Solution |
size_t count_preceding_whitespace(const char *s) {
const char *t = s;
size_t length = strlen(s) + 1;
/* possibly *t < 0 */
while (isspace(*t) && (t - s < length)) {
++t;
}
return t - s;
}
|
size_t count_preceding_whitespace(const char *s) {
const char *t = s;
size_t length = strlen(s) + 1;
while (isspace((unsigned char)*t) && (t - s < length)) {
++t;
}
return t - s;
}
|
- strtok() 함수는 token을 분리하기 위하여 입력 buff의 손상을 가져오기 때문에 항상 복사본으로 작업한다.
| Noncompliant Code Example | Compliant Solution |
char *token;
char *path = getenv("PATH");
token = strtok(path, ":");
puts(token);
while (token = strtok(0, ":")) {
puts(token);
}
printf("PATH: %s\n", path);
/* PATH is now just "/usr/bin" */
|
char *token;
const char *path = getenv("PATH");
/* PATH is something like "/usr/bin:/bin:/usr/sbin:/sbin" */
char *copy = (char *)malloc(strlen(path) + 1);
if (copy == NULL) {
/* handle error */
}
strcpy(copy, path);
token = strtok(copy, ":");
puts(token);
while (token = strtok(0, ":")) {
puts(token);
}
free(copy);
copy = NULL;
printf("PATH: %s\n", path);
/* PATH is still "/usr/bin:/bin:/usr/sbin:/sbin" */
|
- strlen() 함수는 결과 값에 null-terminating 문자를 포함하지 않기때문에 주의해서 사용해야 한다.
| Noncompliant Code Example | Compliant Solution |
int main(int argc, char *argv[]) {
/* ... */
char prog_name[128];
strcpy(prog_name, argv[0]);
/* ... */
}
|
int main(int argc, char *argv[]) {
/* ... */
char * prog_name;
size_t prog_size;
prog_size = strlen(argv[0])+1;
prog_name = (char *)malloc(prog_size);
if (prog_name != NULL) {
if (strlcpy(prog_name, argv[0]), prog_size) {
/* Handle strcpy_s() error */
}
}
else {
/* Couldn't get the memory - recover */
}
/* ... */
}
|
Memory Management
- 최대한 동적할당된 메모리는 같은 모듈에서 해제시켜준다.
| Noncompliant Code Example | Compliant Solution |
enum { MIN_SIZE_ALLOWED = 32 };
int verify_size(char *list, size_t size) {
if (size < MIN_SIZE_ALLOWED) {
/* Handle Error Condition */
free(list);
return -1;
}
return 0;
}
void process_list(size_t number) {
char *list = (char *)malloc(number);
if (list == NULL) {
/* Handle Allocation Error */
}
if (verify_size(list, number) == -1) {
free(list);
return;
}
/* Continue Processing list */
free(list);
}
|
enum { MIN_SIZE_ALLOWED = 32 };
int verify_size(const char *list, size_t size) {
if (size < MIN_SIZE_ALLOWED) {
/* Handle Error Condition */
return -1;
}
return 0;
}
void process_list(size_t number) {
char *list = (char *)malloc(number);
if (list == NULL) {
/* Handle Allocation Error */
}
if (verify_size(list, number) == -1) {
free(list);
return;
}
/* Continue Processing list */
free(list);
}
|
- 자원을 사용한 후의 변수는 즉시 초기화 시킨다. (free(), close(), fclose() ...)
| Noncompliant Code Example | Compliant Solution |
char *message;
int message_type;
/* Initialize message and message_type */
if (message_type == value_1) {
/* Process message type 1 */
free(message);
}
/* ...*/
if (message_type == value_2) {
/* Process message type 2 */
free(message);
}
|
char *message;
int message_type;
/* initialize message and message_type */
if (message_type == value_1) {
/* Process message type 1 */
free(message);
message = NULL;
}
/* ...*/
if (message_type == value_2) {
/* Process message type 2 */
free(message);
message = NULL;
}
|
- malloc(), calloc(), realloc() 함수 사용시 입력 size가 0이 되지 않도록 보장해야 한다.
- size가 0 일경우 함수의 행동은 정해져 있지 않으며, 구현에 따라 다르다.
| Noncompliant Code Example | Compliant Solution |
size_t size;
/* initialize size, possibly by user-controlled input */
int *list = (int *)malloc(size);
if (list == NULL) {
/* Handle allocation error */
}
else {
/* Continue processing list */
}
|
size_t size;
/* initialize size, possibly by user-controlled input */
if (size == 0) {
/* Handle error */
}
int *list = (int *)malloc(size);
if (list == NULL) {
/* Handle allocation error */
}
/* Continue processing list */
|
- rewind() 함수는 에러를 리턴하지 않으므로, fseek() 함수를 사용한다.
| Noncompliant Code Example | Compliant Solution |
char *file_name;
FILE *fp;
/* initialize file_name */
fp = fopen(file_name, "r");
if (fp == NULL) {
/* Handle open error */
}
/* read data */
rewind(fp);
/* continue */
|
char *file_name;
FILE *fp;
/* initialize file_name */
fp = fopen(file_name, "r");
if (fp == NULL) {
/* Handle open error */
}
/* read data */
if (fseek(fp, 0L, SEEK_SET) != 0) {
/* Handle repositioning error */
}
/* continue */
|
Signal
- signal 함수를 등록하기 위해 signal() 함수 대신 sigaction() 함수를 사용한다.
- signal() 함수는 persistant하게 signal handler를 운용하기 위해서는 signal handler안에서 signal을 재등록해야한다.
- sigaction() 함수는 struct sigaction 구조체 값을 이용하여 다양한 signal 처리가 가능하다.
- MPF에서는 mpsignal() 함수가 sigaction() 함수를 이용해 구현되었으므로, 이 함수를 사용한다.
- signal handler안에서는 asynchronous-safe 함수만을 사용한다.
Error
- 리턴 값을 가지는 모든 함수는 error 처리를 해야 한다.
Log Format
- 32bit/64bit 호환성을 위하여 pid_t, pthread_t, thread_t, size_t, ssize_t, socklen_t 형을 로그에 쓸경우 long type으로 변환한다.
- pid_t, pthread_t, size_t, socklen_t: (unsigned long int)
- ssize_t: (long int)
- 예) LOG_SVC("%s:: ccm_forward_thr exit, TID=%lu\n", __func__, (unsigned long int)pthread_self());



