[고급 C++] 가변인자 / CERT C (feat. 안전 코딩하기)
- 컴퓨터공학과/Programming
- 2020. 10. 26.
가변 인자 |
가변인자
특성
- 함수 인자의 수가 정해져있지 않은 함수
- C는 객체지향과 달리 함수명으로만 함수를 구분
- 인자리스트만 다를 경우, 가변인자 함수를 작성하면 하나의 함수로 가능
- 가변인자를 위한 매크로有
- 고정할당 뒤에 차례대로 저장됨
형태
int 함수명(인자, …)
* …; 인자 생략 가능, 여러개 전달 가능
가변인자를 위한 매크로
특성
- ANSI C에서는 이식성을 높일 목적으로 가변길이 인수를 사용하기 위한 매크로를 제공
- 헤더파일 : #include <stdarg.h>
- va_start / va_arg / va_end
va_start(list,fix)
- 리스트를 초기화
- 리스트 포인터 고정 다음인자의 시작주소를 리스트에 저장함
- va_list : 헤더파일에 어떤 주소도 대입받을 수 있는 void형 포인터
va_arg(list, type)
- 리스트에서 타입만큼 읽어들임
- 가변인자 리스트를 하나씩 접근하기 위해 사용
va_end(list)
- 리스트를 NULL 포인터로 저장
- 가변인자의 사용이 끝날 때 사용
예시
1 #include <stdio.h> 2 #include <stdarg.h> // 가변 파라미터 헤더 3 int main() 4 { 5 int result; 6 result = add(3, 10, 20, 30); 7 printf("result : %d \n", result); 8 9 result = add(5, 10, 20, 30, 40, 50); 10 printf("result : %d \n", result); 11 12 13 return 0; 14 } 15 16 int add(int count, ...) // 가변인자를 갖는 함수 17 { 18 int i, sum = 0; 19 va_list list; // var_list 선언 void형포인터 20 21 va_start(list, count); 22 23 for(i=0;i<count;i++) 24 sum += va_arg(list, int); 25 26 va_end(list); 27 28 return sum; 29 } |
result : 60 |
CERT C |
소프트웨어 시스템의 안전성, 신뢰성 및 보안 향상을 위한 C 프로그래밍 언어에 대한 소프트웨어 코딩 표준
안전한 전처리 작업
헤더파일에서 다른 파일을 #include 하는 것
- -I 로 비표준 헤더파일이 들어있는 디렉토리를 지정해줄 것
- define 문은 “;”로 끝나면 안됨
- define문은 헤더파일에 담을 것
- 소스코드는 각자 컴파일이 일어나므로 각 헤더파일에 선언해주어야함
- 관리의 편리성을 위해 공통적으로 사용하는 부분은 헤더에 넣음
버그 없는 C포인터 사용
1. 배열의 크기를 얻을 때 포인터로 sizeof() 사용하지말 것
- sizeof() 연산자는 피 연산자의 크기를 반환하며 sizeof가 포인터로 배열의 크기를 결정하기 위해 사용될 때 원치 않는 결과를 가져올 수 있음
- void funcA(int arr[5])는 4Byte * 5를 할당받은 것이 아니라 주소 하나를 받은 것으로 4Byte만큼만 할당됨
* void funcA(int arr[5])= void funcA(int arr[]) = void funcA(int *arr)
2. 표현식에서 배열타입이 호환 가능함을 보장할 것
- 표현식에서 호환되지 않은 두 개 이상의 배열을 사용하면 정의되지 않은 동작을 초래함
- 배열 주소를 받을 수는 있으나 의도하지 않은 값이 저장됨
- 두 배열의 타입은 호환 가능해야 하며, 같은 원소타입을 갖고있어야 함
3. 반복 제어문에서 연산식이나 함수를 호출하지말 것
반복할 때마다 연산을 수행하거나 함수를 호출하면 성능이 저하됨
4. 문자열 상수를 가리키는 포인터는 const로 선언할 것
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
// "" : 문자열 상수
char c[] = "hello"; // \0까지 6byte 할당
// 문자열이 수정될 경우 할당 대신 초기화를 사용한다.
c[0] = 'H'; // 항상 변경 가능
printf("c : %s \n", c);
// 문자열 상수를 가리키는 포인터는 const로 선언
// char *p = "hello"; // char c[]와 다름. char *p는 주소를 보관
// 변경하려면 런타임 오류
// p[0] = 'H'; // 컴파일러마다 다름(리눅스 오류, VS 버전마다 다름),
// 따라서 const로 상수화 해라
const char *p="hello"; // 수정 불가 // 컴파일오류
p[0] = 'H'; // 컴파일타임에 에러나기 위해
printf("p : %s \n", p);
return 0;
}
5. 배열의 경계를 넘어가는 문자열을 고정된 길이의 배열에 데이터를 저장하지말 것
- 배열의 크기를 넘는 문자열 입력은 버퍼 오버플로우 발생시킴
- 크기를 모르는 문자열을 고정된 크기의 배열에 바로 입력 받는 것은 위험
#define MAX_SZ 100
char tmp[MAX_SZ]; // 크기가 넉넉한 임시배열 생성
// Buf의 사이즈가 입력받은 문자열보다 작으면 다시 입력받기
do {
printf(“input buf”);
gets(tmp); // 임시배열에 받은 후
} while(strlen(tmp) >= sizeof(buf));
// 임시배열을 버퍼에 복사
strcpy(buf, tmp);
6. strtok()에서 파싱되는 문자열은 보존되지 않음(알고 코딩해야 안전하다!)
- strtok()은 문자열을 구분자가 처음 나타나는 부분까지 파싱하고 구분자를 ‘\0’로 변경한 후 토큰의 처음 문자주소를 전달될 변수에 넣음
- 처음 나타난 구분자 위치에는 ‘\0’이 저장됨
- 문제점 : strtok()후 path가 변경됨
2 #include <stdio.h> 3 #include <string.h> 4 #include <stdlib.h> 5 6 int main() 7 { 8 // strtok() 에서 파싱되는 문자열은 보존되지 않는다. 9 10 char *token; 11 char path[30]="/usr/bin:/usr/sbin:/etc"; 12 13 token=strtok(path, ":"); // 처음 호출 시 ":"찾아 '\0'로 14 puts(token); 15 16 while(token=strtok(0,":")) 17 puts(token); 18 19 printf("path: %s \n", path); // /usr/bin 출력 20 21 22 return 0; 23 } |
:!a.out /usr/bin /usr/sbin /etc path: /usr/bin |
- 수정 : 원래의 문자열을 보존하려면 문자열을 버퍼에 복사한 후 복사된 버퍼를 strtok() 수행
2 #include <stdio.h> 3 #include <string.h> 4 #include <stdlib.h> 5 6 int main() 7 { 8 // strtok() 에서 파싱되는 문자열은 보존되지 않는다. 9 10 char *token; 11 char path[30]="/usr/bin:/usr/sbin:/etc"; 12 char PATH[30]; 13 14 strcpy(PATH, path); // 복사해놓고시작 15 16 token=strtok(path, ":"); // 처음 호출 시 ":"찾아 '\0'로 17 puts(token); 18 19 while(token=strtok(0,":")) 20 puts(token); 21 22 printf("path: %s \n", path); // /usr/bin 출력 23 printf("path: %s \n", PATH); // /usr/bin 출력 24 25 return 0; 26 } |
:!a.out /usr/bin /usr/sbin /etc path: /usr/bin path: /usr/bin:/usr/sbin:/etc |
더 많은 시리즈 보기
[고급 C++]C컴파일러와 C라이브러리(공유/정적/동적라이브러리)
[고급 C++]포인터 1편(프로세스/포인터변수/포인터연산/NULL포인터)
궁금한 사항은 댓글로 남겨주세요💃💨💫
좋아요와 구독(로그인X)은 힘이 됩니다 🙈🙉
'컴퓨터공학과 > Programming' 카테고리의 다른 글
[고급 C++] 공용체와 구조체 비트필드 + 바이트 오더링 / 활용방안 (1) | 2020.11.02 |
---|---|
[고급 C++] 구조체와 구조체포인터 (0) | 2020.10.27 |
[고급 C++]포인터 4편(다중 포인터/함수 포인터/void형 포인터) (1) | 2020.10.23 |
[고급 C++]포인터 3편(배열 포인터/포인터 배열/문자열 상수 포인터) (2) | 2020.10.22 |
[고급 C++]포인터 2편(배열과 포인터) (0) | 2020.10.21 |