본문 바로가기

Software

Secure Cordng C - Declaration

DCL-00 변하지 않는 객체는 const로 보장해둬라



변하지 않는 객체는 const로 보장해야 한다. 객체의 불변성을 const를 사용해 보장하면

애플리케이션의 정확성과 안전성을 보장하는데 도움이 된다.


부적절한 코드

  • 다음의 코드에서 객체의 값이 의도하지 않게 변경되고 있다.
#include <stdio.h>

typedef struct{
int x, y;
}Point;

void print_point(Point *p){
printf("x = %d, y = %d\n", p->x, p->y);
p->x = -1;
}

int main(){
Point p = { 0, };
print_point(&p);

return 0;
}



  • 해결 방법 - const 키워드를 사용하면 이를 해결할 수 있다.
#include <stdio.h>

typedef struct{
int x, y;
}Point;

void print_point(const Point *p){
printf("x = %d, y = %d\n", p->x, p->y);
p->x = -1;
}

int main(){
Point p = { 0, };
print_point(&p);

return 0;
}


DCL-01 내부 스코프에서 변수 이름을 재사용하지 마라



변수의 이름을 재사용할 경우, 코드 상에서 혼란을 가중시키게 된다.

때문에 다음의 경우는 피해야 한다.

  • 전역 변수가 사용될 수 있는 범위 내에서 어떤 변수든 중복해서 전역 변수 이름을 사용하면 안된다.
  • 어떤 블록 안에서 이미 사용되고 있는 변수와 동일한 이름으로 다른 블록에서 선언하면 안된다.

부적절한 코드
  • 지역 변수가 전역 변수의 이름을 가리게 되므로 코드는 정상적으로 수행되지 않는다.
#include <stdio.h>
#include <string.h>

char msg[32];

void set_error(cont char* error){
char msg[32];
//...
strncpy(msg, error, sizeof(msg));
}

int get_fd(){
//...
return -1;
}

int main(){
int fd = get_fd();
if(fd < 0)
set_error("get_fd error");
//...

printf("error message: %s\n", msg);
return 0;
}



  • 해결 방법 - 변수의 이름을 재사용하고 있다는 것은 변수의 이름 자체가 너무 일반적이라는 것을 의미하기 때문에
                  변수의 이름을 좀 더 설명적으로 정의한다.
#include <stdio.h>
#include <string.h>

char system_msg[32];

void set_error(cont char* error){
char msg[32];
//...
strncpy(system_msg, error, sizeof(system_msg));
}

int get_fd(){
//...
return -1;
}

int main(){
int fd = get_fd();
if(fd < 0)
set_error("get_fd error");
//...

printf("error message: %s\n", system_msg);
return 0;

}




DCL-02 상수 수식의 값을 테스트할 때는 정적 어썰션(static assertion)을 사용하라



어썰션은 취약성이 될 수 있는 소프트웨어의 결점을 찾아 제거하는데 사용되는 효과적인 진단 도구이다.

다만 일반적인 어썰션(assert())를 사용하는 것은 몇 가지 제약이 존재하며 다음과 같다.


  • 일반적인 어썰션 도구는 프로그램이 구동 중에 동작하므로 런타임 오버해드(overhead)가 존재한다.
  • 일반적인 어썰션 도구의 마지막 동작은 abort()를 호출하는 것이므로, 서버 프로그램이나 임베디드 시스템에서는 사용하기 어렵다.

부적절한 코드
  • 다음 코드는 구조체의 패딩 비트를 검사하기 위해 assert 함수를 사용하고 있다.
    진단은 런타임에만 일어나고 그것도 assert 함수가 포함된 코드가 실행될 때만 발견된다.
#include <stdio.h>
#include <assert.h>

typedef struct _Packet{
char cmd;
int len;
}Packet;

int main(){
assert(sizeof(Packet) == 3);

Packet data;
//...

return 0;
}



  • 문제 해결 - 정적 어썰션을 구현하여 해결한다.
#include <stdio.h>

#define JOIN_AGAIN(x, y)    x##y
#define JOIN(x, y)    JOIN_AGAIn(x, y)
#define static_assert(e)    \
typedef char JOIN(assertion_failed_at_line, __LINE__)[(e) ? 1 : -1]

typedef struct _Packet{
char cmd;
int len;
}Packet;

int main(){
static_assert(sizeof(Packet) == 3);

Packet data;
//...

return 0;
}



DCL-03 프로그램 로직 상의 고정적인 값(Hard cording value)을 나타낼 때는 의미 있는 심볼릭 상수를 사용하라


코드 상에서 리터럴(literal)을 사용할 경우, 가독성이 떨어질 수 있다. 때문에 가급적 리터럴을 직접 사용하기 보다는 심볼릭(symbolic)

상수를 통해 적절한 이름을 붙여 코드의 의도를 명확히 하는 것이 좋다. C 언어에서 심볼릭 상수를 만두는 방법은 다음과 같다.

  • const로 지정된 객체
  • 열거형 상수
  • 객체형 매크로

const로 지정된 객체

const로 지정된 객체는 특정 스코프 내에서 사용 가능하며 컴파일러가 타입 체크를 해주며, 디버깅 도구 사용 시 객체의 이름을 타나낼 수 있다. 대신 각 객체는 메모리를 사용하며 런타임에 약간의 오버헤드가 발생한다. 또한 아래의 경우처럼 컴파일 타임에서 정수형 상수가 필요한 곳에서는 사용할 수 없다.
  • 구조체 내부의 비트 단위로 정의된 멤버의 크기
  • 배열의 크기(가변 배열은 예외)
  • 열거형 상수의 값
  • case  상수의 값

열거형 상수

열거형 상수는 정수로 나타낼 수 있는 정수형 상수 표현식을 나타낼 때 사용한다.
const 키워드를 사용한 객체와 달리 메모리를 소모하지 않는다.
다만 열거형 상수는 값의 타입을 정의할 수 없으며 항상 정수(int)이다.


객체형 매크로

전처리 단계에서 사용되는 객체형 매크로(object-like macro)의 정의는 다음과 같다.



#define identifier replacement-list


객체형 매크로는 전처리기에 의해 치환되는 구조이므로 컴파일 과정에서는 매크로의 심볼을 볼 수 없다.

그래서 대부분의 컴파일러들은 매크로 이름들을 따로 저장하고 디버거에 전달하기도 한다.


매크로는 다음 이름으로 적용될 수 있는 스코프 규칙들을 고려하지 않았기 때문에 의도하지 않는 방식으로 치환되어 기대하지 않은 결과를

만들기도 한다. 그리고 객체형 매크로는 메모리를 소비하지 않으며 따라서 포인터로 가리킬 수 도 없다.

C 프로그래머들은 심볼릭 상수로 객체형 매크로를 사용하는 편이다.


정리하면 다음과 같다.


방식 

평가 시점 

메모리 소비 

디버깅 시의 심볼 

타입 체크 

컴파일 타임 상수 표현식 

열거형

컴파일 타임 

없음 

있음 

있음 

있음 

const 지정 

런타임 

있음 

있음 

있음 

없음 

매크로 

전처리 

없음 

없음 

없음 

있음 



1. 부적절한 코드

  • 정수 리터럴에 대한 의미가 분명하지 않다.

void draw_color(int color){

switch(color){

case 0: /* ... */ break;

case 1: /* ... */ break;

case 2: /* ... */ break;

}

}


int main(){

//...

draw_color(0);


return 0;

}


  • 해결 방법 - 심볼릭 상수로 변경하여 의미를 분명하게 한다.

#define RED        0

#define BLUE      0

#define GREEN    0


void draw_color(int color){

switch(color){

case RED:       /* ... */ break;

case BLUE:     /* ... */ break;

case GREEN:   /* ... */ break;

}

}


int main(){

//...

draw_color(0);


return 0;

}



2. 부적절한 코드

  • 버퍼의 크기가 일치하지 않다, 버퍼 오버플로가 발생할 수 있다.

#include <stdio.h>


int main(){

char buff[16];


gets(buff, 32, stdin);

printf("%s\n", buff);


return 0;

}



  • 해결 방법 1 : 열거형을 사용하여 해결한다.
#include <stdio.h>

enum { BUFF_SIZE = 16 };


int main(){

char buff[BUFF_SIZE];


gets(buff, BUFF_SIZE, stdin);

printf("%s\n", buff);


return 0;

}


  • 해결 방법 2: sizeof 연산자를 사용한다.

#include <stdio.h>


int main(){

char buff[16];


gets(buff, sizeof(buff), stdin);

printf("%s\n", buff);


return 0;

}


DCL-04 함수 선언 시 적절한 타입 정보를 포함시켜라


함수 호출 전 반드시 함수의 선언 정보가 반드시 존재해야 한다. 이는 함수 선언 정보가 없을 경우, 컴파일러는 타입 정보를 정확하게

체크할 수 없기 때문이다. 표준 라이브러리의 함수를 사용할 때 함수 선언 정보를 삽입하는 방법은 적절한 헤더 파일을 삽입하는 것이다.

함수의 선언 정보가 없는 상태로 함수를 호출할 경우, 컴파일러는 경고를 내지만 에러는 발생하지 않는다.


1. 부적절한 코드

  • 함수의 선언 정보 없이 함수를 호출하면 잘못된 값이 인자로 전달될 수 있다.
#include <stdio.h>

int main(){
func(1, 2);
return 0;
}

int func(int a, int b, int c){
printf("func(%d, %d, %d)", a, b, c);
return 0;
}


  • 해결 방법 - 함수의 선언 정보를 정확하게 기술한다.
#include <stdio.h>

int func(int a, int b, int c);

int main(){
func(1, 2, 3);
return 0;
}

int func(int a, int b, int c){
printf("func(%d, %d, %d)", a, b, c);
return 0;
}



2. 부적절한 코드

  • 잘못된 함수 포인터를 선언해서 사용하면 의도하지 않는 결과가 나타날 수 있다.
#include <stdio.h>

int add(int a, int b){
return a + b;
}

int main(){
int(*fp)(int);

fp = add;
printf("%d\n", fp(1));

return 0;
}



  • 해결 방법 - 함수 포인터를 정확하게 정의한다.
#include <stdio.h>

int add(int a, int b){
return a + b;
}

int main(){
int(*fp)(int, int);

fp = add;
printf("%d\n", fp(1));

return 0;
}


DCL-05 error 에러 코드를 반환하는 함수의 타입을 error_t로 정의하라


에러 코드를 반환하는 함수들은 보통 리턴 타입을 int로 사용한다. 이 때, 리턴 타입 정보만으로는 에러 코드를 반환하는지에 대한 구분이 명확하지 않다. 이 때, typedef 키워드를 사용하면 가독성을 높일 수 있다.


1. 부적절한 코드

  • 함수가 에러 코드를 반환하는지 알 수 없다.
#include <stdio.h>

int Divide(int dividend, int divisor, int* result){
if(result == NULL || divisor == 0)
return -1;

*result = dividend / divisor;
return 0;
}

int main(){
int data;

Divide(4, 0, &data);
printf("%d\n", data);

return 0;
}



  • 해결 방법 - 반환 타입을 error_t 타입으로 변경하여 함수가 에러 코드를 반환한다는 것을 주지시킨다.
#include <stdio.h>

typedef int error_t;
error_t Divide(int dividend, int divisor, int* result){
if(result == NULL || divisor == 0)
return -1;

*result = dividend / divisor;
return 0;
}

int main(){
int data;

if(Divide(4, 0, &data) < 0)
printf("%d\n", data);
else
printf("%d\n", data);

return 0;
}


DCL-06 가변 인자를 가진 함수에서는 함수 작성자와 함수 사용자 간의 약속이 지켜져야 한다.


가변 인자 함수는 여러 개의 인자를 취하는데 문제의 소지가 있다. 때문에 가변 인자 함수의 정확한 사용 방법을

인지하여 의도하지 않는 결과가 발생되지 않도록 해야 한다.


1. 부적절한 코드

  • 아래의 함수에서 마지막 인자로 VA_END 값을 넘겨주지 않을 경우, 코드는 정상적으로 동작하지 않을 수 있다.

#include <stdio.h>

#include <stdarg.h>


enum { VA_END = -1 };

int average(int first, ...){

int sum =0;

int cnt = 0;


va_list args;

va_start(args, first);


int i = first;

while(i != VA_END){

sum += i;

++cnt;

i = va_arg(args, int);

}

va_end(args);


return cnt ? sum / cnt : 0?

}


int main(){

int avg = average(100, 100, 100, 100);

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


return 0;

}


  • 해결 방법 1 - 마지막 인자로 VA_END를 넣어 해결한다.

#include <stdio.h>

#include <stdarg.h>


enum { VA_END = -1 };

int average(int first, ...){

int sum =0;

int cnt = 0;


va_list args;

va_start(args, first);


int i = first;

while(i != VA_END){

sum += i;

++cnt;

i = va_arg(args, int);

}

va_end(args);


return cnt ? sum / cnt : 0?

}


int main(){

int avg = average(100, 100, 100, 100, VA_END);

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


return 0;

}


  • 해결 방법 2 - average 함수를 다시 설계한다.

#include <stdio.h>

#include <stdarg.h>


int average(int cnt, ...){

int sum =0;

int cnt = 0;


va_list args;

va_start(args, first);


int sum = first;

int i;


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

sum += va_avg(args, int);

va_end(args);


return cnt ? sum / cnt : 0?

}


int main(){

int avg = average(4, 100, 100, 100, 100);

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


return 0;

}


DCL-07 불투명한 타입을 사용해 추상 데이터 타입을 구현한다.


추상 데이터 타입(ADT, Abstract Data Type)은 프로그램의 대상이 되는 사물이나 현상을 추상화하여 정의한 것이다.

추상 데이터 타입을 사용하면 그 타입의 세부적인 구현을 감출 수 있기 때문에 정보 은닉(information hiding)을 할 수 있다.


1. 부적절한 코드

  • 아래의 코드에서 스택 자료구조의 내부 구조가 노출되어 외부로부터 내부 데이터를 보호 할 수 없다.
#include <stdio.h>

// Stack.h---------------------------
struct __Stack{
void** arr;
int top;
int size;
};
typedef struct __Stack Stack;

extern Stack* create_stack(int size);
// ----------------------------------


int main(){
Stack* stack = create_stack(10);
//....

stack->top = -1;

return 0;
}

// Stack.c---------------------------
#include <stdlib.h>

// #include "Stack.h"

Stack* create_stack(int size){
Stack* stack = calloc(1, sizeof(Stack));
stack->arr = calloc(size, sizeof(void*));
stack->size = size;

return stack;
}
// ----------------------------------


  • 해결 방법 - 불완전한 타입으로 선언한 후, 포인터를 사용한다.
#include <stdio.h>

// Stack.h---------------------------
struct __Stack;
typedef struct __Stack Stack;

extern Stack* create_stack(int size);
// ----------------------------------


int main(){
Stack* stack = create_stack(10);
//....

stack->top = -1;

return 0;
}

// Stack.c---------------------------
#include <stdlib.h>

// #include "Stack.h"

struct __Stack{
void** arr;
int top;
int size;
};

Stack* create_stack(int size){
Stack* stack = calloc(1, sizeof(Stack));
stack->arr = calloc(size, sizeof(void*));
stack->size = size;

return stack;
}
// ----------------------------------


DCL-08 함수의 의해 바뀌지 않을 값에 대한 포인터를 함수의 매개 변수로 사용할 때는 const로 정의하라.


포인터를 매개 변수로 하는 함수는 내부적으로 대상체의 값을 변경하는 위험이 존재한다.

때문에 의도하지 않는 변경을 막으려면 const 키워드를 사용해야 한다.


1. 부적절한 코드

  • 다음의 코드는 인자로 전달된 배열의 값을 임으로 변경하고 있다.
#include <stdio.h>

int sum_arr(int *arr, int cnt){
int sum = 0;
int i;

for(i = 0; i < cnt; i++){
sum += arr[i];
}
*arr = 0;

return sum;
}

int main(){
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%d\n", sum(arr, 10);

return 0;
}



  • 해결 방법 - 상수 객체를 가리키는 포인터로 변경한다.
#include <stdio.h>

int sum_arr(const int *arr, int cnt){
int sum = 0;
int i;

for(i = 0; i < cnt; i++){
sum += arr[i];
}
*arr = 0;

return sum;
}

int main(){
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%d\n", sum(arr, 10);

return 0;
}


DCL-09 현재 범위를 넘어서까지 사용되지 않을 객체는 static으로 선언해라


객체나 함수를 현재 스코프의 외부에 노출시키지 않을 것이라면 static으로 선언해야 한다.

C99 표준에서는 static에 대하여 다음과 같이 언급하고 있다.

  • 한 파일 스코프 내에서 객체나 함수에 대한 식별자의 선언이 static을 포함하고 있으면 식별자는 내부에서만 결합(사용)된다.
  • 한 파일 스코프에서 객체나 함수에 대한 식별자의 선언에 static과 같은 저장 클래스 지정자가 없으면 식별자는 외부에서
    결합(사용) 된다.

1. 부적절한 코드
  • square 함수가 내부적(internal)으로 사용된다고 가정했을 때, 다음의 코드는 위험하다.

// Calc.h------------------------------------

double circle_area(int radius);

// ------------------------------------------


// Calc.c-------------------------------------

int square(int x){

    return x * x;

}


double circle_are(int radius){

    return square(radius) * 3.14;

}

// ------------------------------------------

#include <stdio.h>


// in Calc.h

double circle_area(int radius);


int main(){

    printf("%lf\n", circle_area(2));

    printf("%lf\n", square(2));


    return 0;



  • 해결 방법 - square 함수를 내부 연결성(internal linkage)를 갖도록 static으로 선언한다.

// Calc.h------------------------------------

double circle_area(int radius);

// ------------------------------------------


// Calc.c-------------------------------------

static int square(int x){

    return x * x;

}


double circle_are(int radius){

    return square(radius) * 3.14;

}

// ------------------------------------------

#include <stdio.h>


// in Calc.h

double circle_area(int radius);


int main(){

    printf("%lf\n", circle_area(2));

    printf("%lf\n", square(2));


    return 0;



DCL-10 객체를 선언할 때 적절한 지속공간을 지정하라


객체가 자신의 수명을 다한 후에도 참조된다면 정의되지 않은 행동을 유발할 수 있다.

예를 들어 수명을 다한 객체를 참조하는 포인터는 정의되지 않은 값을 갖게 된다.

수명을 다한 객체에 접근하는 것은 매우 위험하고 취약성을 만드는 결과를 초래하기도 한다.


1. 부적절한 코드

  • 수명을 다한 지역 객체를 전역 변수가 참조함으로 임의의 코드를 수행할 수 있는 위험성이 존재한다.
#include <stdio.h>
const char* path;

void open_path(){
const char str[] = "c:\\a.txt";
path = str;
}

void hack(){
const char str[] = "d:\\a.exe";
printf("path = %s\n", path);
}

int main(){
open_path();
hack();

printf("path = %s\n", path);
return 0;
}

  • 해결 방법 - 전역 포인터가 유효한 객체를 가리키도록 한다.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

const char* path;

void open_path(){
const char str[] = "c:\\a.txt";
path = calloc(1, strlen(str) +1);
strcpy(path, str, strlen(str) +1);
}

void hack(){
const char str[] = "d:\\a.exe";
printf("path = %s\n", path);
}

int main(){
open_path();
hack();

printf("path = %s\n", path);
return 0;
}


2. 부적절한 코드
  • 다음 코드는 지역 객체의 주소를 반환하고 있다.
#include <stdio.h>

char* init_array(){
char arr[10] = { 0, };
return arr;
}

int main(){
char* pArr = init_array();
int i;

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

return 0;
}

  • 해결 방법 - 초기화할 배열을 함수의 인자로 받아 처리한다.
#include <stdio.h>

void init_array(char arr[]){
int i;

for(i=0; i < 10; i++){
arr[i] = 0;
}
}

int main(){
char arr[10];
init_array(pArr);
int i;

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

return 0;
}


DCL-11 함수 인자에서 restrict로 지정된 소스 포인터와 목적 포인터가 동일한 객체를 참조하지 않게 하라


restrict 키워드는 오직 포인터에만 적용되는 키워드로 그 포인터가 데이터 객체에 접근할 수 있는 유일하고도 최초가 되는 수단임을

나타낸다. 즉 포인터가 restrict로 한정되면 그 포인터가 가리키는 데이터 블록은 그 포인터만이 접근이 가능하므로 컴파일러가 더 

효율적으로 코드를 최적화 할 수 있다.


그러나 restrict 지정자를 사용할 때는 포인터들이 서로 동일한 객체를 참조하지 말아야 한다.

함수의 두 포인터가 동일한 객체를 참조하는 경우 그 결과는 미정의 동작이다.



1. 부적절한 코드

  • memcpy는 restrict 지정자를 사용한 함수로 동일한 객체의 주소를 인자로 전달하면 그 결과는 알 수 없다.
#include <stdio.h>
#include <string.h>

void print_arr(int* arr, int len){
int i;
for(i = 0; i < len; i++){
printf("%d ", arr[i]);
}
getchar();
}

int main(){
int cnt = 10;
int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
print_arr(arr, cnt);

--cnt;
memcpy(arr, arr + 1, sizeof(int)*cnt);
print_arr(arr, cnt);

return 0;
}


  • 해결 방법 1 - 메모리의 순서를 고려하지 않는다면 마지막 요소를 앞쪽에 복사한다.
#include <stdio.h>
#include <string.h>

void print_arr(int* arr, int len){
int i;
for(i = 0; i < len; i++){
printf("%d ", arr[i]);
}
getchar();
}

int main(){
int cnt = 10;
int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
print_arr(arr, cnt);

arr[0] = arr[--cnt];
print_arr(arr, cnt);

return 0;
}



  • 해결 방법 2 - memmove 함수를 사용하여 처리한다. memmove 함수는 내부적으로 버퍼를 가지고 있어 안전하게 복사가 가능하다.
#include <stdio.h>
#include <string.h>

void print_arr(int* arr, int len){
int i;
for(i = 0; i < len; i++){
printf("%d ", arr[i]);
}
getchar();
}

int main(){
int cnt = 10;
int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
print_arr(arr, cnt);

--cnt;
memmove(arr, arr + 1, sizeof(int)*cnt);
print_arr(arr, cnt);

return 0;
}




DCL-12 캐시되어서는 안되는 데이터에는 volatile을 사용하라


일반적으로 CPU는 성능 상의 이유로 데이터를 메모리에서 직접 읽어오지 않고 캐시를 사용하여 읽어온다.

프로그램이 아닌 하드웨어에 의해 변경되는 데이터는 캐시에 반영하지 않으므로, 메모리에서 직접 읽어와야 한다.

volatile는 데이터가 프로그램이 아닌 외부적인 요인에 의해 그 값이 변경될 수 있다고 컴파일러에 알려 캐싱을 제한할 때 사용한다.


1. 부적절한 코드

  • 최적화에 의해 flag가 캐시되었다면 SIGINT를  받아도 종료되지 않는다.
#include <stdio.h>
#include <signal.h>

int flag;

void handler(int signum){
printf("\thandler\n");
flag = 0;
}

int main(){
flag = 1;
signal(SIGINT, handler);

while(flag){
printf("do something\n");
sleep(1);
}

return 0;
}


  • 해결방법 - 변수를 volatile로 선언한다.
#include <stdio.h>
#include <signal.h>

volatile int flag;

void handler(int signum){
printf("\thandler\n");
flag = 0;
}

int main(){
flag = 1;
signal(SIGINT, handler);

while(flag){
printf("do something\n");
sleep(1);
}

return 0;
}


DCL-13 함수 정의와 맞지 않는 타입으로 함수를 변환하지 마라


원래의 타입과 다른 타입의 함수를 참조하도록 함수 포인터를 사용하면 정의되지 않는 결과를 초래한다.

때문에 함수 포인터를 사용할 경우, 정확한 타입을 선언해서 사용해야 한다. C99에서는 다음과 같이 언급하고 있다.


  • 특정 타임에 대한 함수의 포인터는 다른 타입의 함수 포인터로 변환될(컴파일러의 정책에 의해 변경가능한 상태) 수 있고, 결과는 원래의 포인터와 같아진다. 호환되지 않는 타입의 함수를 호출하는데 함수 포인터가 사용되면 알 수 없는 행동을 초래한다.

1. 부적절한 코드
  • 함수 포인터와 함수의 타입이 일치하지 않아 정의되지 않는 결과를 초래한다.
#include <stdio.h>
#include <signal.h>

int square(int a) { return a * a ; }
int cube(int a) { return  a * a * a; }
int add(int a, int b) { return a + b ; }

int main(){
int (*fp)(int);
//...

fp = add;
printf("%d\n", fp(1, 1));

return 0;
}



  • 해결 방법 - 정확한 타입의 포인터 변수를 선언한다.
#include <stdio.h>
#include <signal.h>

int square(int a) { return a * a ; }
int cube(int a) { return  a * a * a; }
int add(int a, int b) { return a + b ; }

int main(){
int (*fp)(int, int);
//...

fp = add;
printf("%d\n", fp(1, 1));

return 0;
}

반응형

'Software' 카테고리의 다른 글

Error message 모음  (0) 2019.02.08
Secure Cordng C - Expression  (0) 2018.08.30
Secure Cordng C - Preprocessor  (0) 2018.07.23
Secure Cordng C - Coding Style  (0) 2018.07.20
Secure Cordng C - 포인터의 개념과 이해 #3  (0) 2018.07.18