void 키워드 사용 규칙:
1. 함수에 반환 값이 없으면 void 유형으로 선언해야 합니다.
2. 함수에 매개변수가 없으면 매개변수는 다음과 같습니다. void로 선언해야 합니다.
3. 함수의 매개 변수가 모든 유형의 포인터일 수 있는 경우 해당 매개 변수는 void *로 선언해야 합니다.
4. Void는 실수를 나타낼 수 없습니다. 변수; >void는 추상화를 구현합니다.
################### ### ########################################
1. 개요
많은 초보자들이 C/C++ 언어의 void 및 void 포인터 유형을 이해하지 못해 사용 시 실수를 저지릅니다. 이 글에서는 void 키워드의 심오한 의미를 설명하고
void 및 void 포인터 유형의 사용 방법과 기술을 자세히 설명합니다.
2. void의 의미
void의 문자 그대로의 의미는 "유형이 지정되지 않음"이고 void *는 "유형이 지정되지 않은 포인터"이며 void *는 모든 유형의 데이터를 가리킬 수 있습니다.
void는 거의 "주석 달기" 및 프로그램 제한 기능만 갖고 있습니다. 아무도 void 변수를 정의한 적이 없으므로 정의해 보겠습니다.
void a;
이 명령문 줄은 컴파일할 때 "'void' 유형의 불법 사용"이라는 메시지가 표시되는 오류를 발생시킵니다. 그러나 void가 오류 없이 컴파일되더라도 실질적인 의미는 없습니다.
void의 실제 역할은 다음과 같습니다.
(1) 함수 반환에 대한 제한
(2) 함수 매개변수에 대한 제한.
위의 두 가지 사항에 대해서는 세 번째 부분에서 자세히 설명하겠습니다.
우리 모두 알고 있듯이 p1과 p2의 유형이 동일하면 p1과 p2가 서로 다른 데이터 유형을 가리키는 경우 p1과 p2 사이에 값을 직접 할당할 수 있습니다. 필수 유형을 사용해야 합니다
변환 연산자는 대입 연산자 오른쪽에 있는 포인터 유형을 왼쪽에 있는 포인터 유형으로 변환합니다.
예:
float *p1;
int *p2;
p1 = p2;
p1 = p2 문은 "' =': 'int *'에서 'float *'"로 변환할 수 없습니다. 다음으로 변경해야 합니다.
p1 = (float *)p2;
void *가 다르므로 모든 유형의 포인터를 직접 할당할 수 있습니다. 캐스트 변환을 수행하지 않고 수행:
void *p1;
int *p2;
p1 = p2
그러나 이것이 캐스트 없이도 void *를 할당할 수 있다는 의미는 아닙니다. 포인터의. "typeless"에는 "typed"가 포함될 수 있지만 "typed"에는
"untyped"가 포함될 수 없기 때문입니다. 그 이유는 아주 간단합니다. “남자와 여자는 인간이다”라고 말할 수는 있지만 “사람은 남자다”, “사람은 여자이다”라고 말할 수는 없습니다. 다음 문은 오류를 컴파일합니다.
void *p1;
int *p2;
p2 = p1;
"'=' 프롬프트: 'void *'에서 'int *로 변환할 수 없습니다. '".
3. void 사용
void 키워드 사용 규칙은 다음과 같습니다.
규칙 1 함수에 반환 값이 없으면 void 유형으로 선언해야 합니다.
C 언어에서 반환 값 유형을 지정하지 않는 모든 함수는 컴파일러에서 정수 값을 반환하는 것으로 처리됩니다. 그러나 많은 프로그래머들은 이것이 void 타입이라고 잘못 생각하고 있습니다. 예:
add (int a, int b)
{
return a + b;
}
int main(int argc, char* argv[])
{
printf ( "2 + 3 = %d", add ( 2, 3) );
}
프로그램 실행 결과는 다음과 같습니다.
2 + 3 = 5
이는 반환 값 설명이 없는 함수가 실제로 int 함수임을 보여줍니다.
Lin Rui 박사는 "고품질 C/C++ 프로그래밍"에서 다음과 같이 언급했습니다. "C++ 언어는 매우 엄격한 유형 안전성 검사를 수행하며 위의 상황(유형 선언이 없는 함수 참조)이 발생하는 것을 허용하지 않습니다. " 그러나 컴파일러는 반드시 그렇게 생각하지는 않습니다. 예를 들어 Visual C++6.0에서는 위의 add 함수가 오류나 경고 없이 컴파일되고 올바르게 실행되므로 엄격한 유형 검사를 수행하는 컴파일러에 의존할 수 없습니다.
그러므로 혼란을 피하기 위해 C/C++ 프로그램을 작성할 때 모든 함수의 유형을 한 비트도 놓치지 않고 지정해야 합니다. 함수가 값을 반환하지 않는 경우에는 void class
형식으로 선언해야 합니다. 이는 프로그램의 가독성을 위한 요구 사항일 뿐만 아니라 프로그래밍 표준화를 위한 요구 사항이기도 합니다. 또한 void 유형 선언을 추가하면 코드의 "자체 주석" 역할도 할 수 있습니다. 코드의 "자체 주석
설명"은 코드가 스스로 주석을 달 수 있다는 의미입니다.
규칙 2 함수에 매개변수가 없으면 해당 매개변수를 void로 선언해야 합니다
C++ 언어에서 이러한 함수 선언:
int function(void)
{
return 1;
}
다음 호출을 하는 것은 불법입니다.
function(2);
C++에서는 함수 매개변수가 void이므로 이는 다음을 의미합니다. 이 함수는 어떤 매개변수도 허용하지 않습니다.
Turbo C 2.0에서 컴파일합니다:
#include "stdio.h"
fun()
{
return 1;
}
main()
{
printf("%d",fun(2));
getchar();
}
올바르게 컴파일되어 1을 출력합니다. 이는 C 언어에서 매개변수 없이 함수를 제공할 수 있음을 보여줍니다. 모든 유형의 매개변수를 전달하지만 C++ 컴파일러에서 동일한 코드를 컴파일하면 오류가 발생합니다. C++
에서는 매개변수가 없는 함수에 매개변수를 전달할 수 없으며 "'fun': 함수는 1개의 매개변수를 사용하지 않습니다."라는 오류 메시지가 표시됩니다.
따라서 C이든 C++이든 함수가 매개변수를 허용하지 않으면 매개변수를 void로 지정해야 합니다.
규칙 3: void 포인터 유형을 주의해서 사용하세요
ANSI(American National Standards Institute) 표준에 따르면 void 포인터에서는 산술 연산을 수행할 수 없습니다. 즉, 다음 연산은 불법입니다.
void * pvoid;
pvoid++; //ANSI: 오류
pvoid += 1; //ANSI: 오류
//ANSI 표준이 이를 식별하는 이유는 알고리즘 연산은 반드시 그것이 가리키는 데이터 유형의 크기를 알고 있어야 합니다.
//예:
int *pint;
pint++; //ANSI: 정확함
pint++의 결과는 sizeof(int)를 늘리는 것입니다. (VC6.0에서 테스트한 결과, sizeof(int)의 배수입니다.)
하지만 유명한 GNU(GNU's Not Unix의 약자)는 그렇게 생각하지 않습니다. void *의 알고리즘 연산이 일관적이라고 지정합니다. 문자 *로.
따라서 GNU 컴파일러에서는 다음 명령문이 정확합니다.
pvoid++; //GNU: 올바른
pvoid += 1 //GNU: 올바른
실행 결과 of pvoid++ 1만큼 증가합니다. (VC6.0에서 테스트, sizeof(int)의 배수입니다.)
실제 프로그래밍에서는 ANSI 표준을 준수하고 프로그램의 이식성을 향상시키기 위해 다음을 달성하는 코드를 작성할 수 있습니다. 다음과 같은 함수:
void * pvoid;
(char *)pvoid++; //ANSI: 올바른; GNU: 올바른
(char *)pvoid += 1; //ANSI: 잘못된; 맞습니다
GNU와 ANSI에는 몇 가지 차이점이 있습니다. 일반적으로 GNU는 ANSI보다 "개방적"이며 더 많은 구문을 지원합니다. 하지만 실제로 디자인할 때는
ANSI 표준을 최대한 준수해야 합니다.
규칙 4 함수의 매개변수가 모든 유형의 포인터일 수 있는 경우 해당 매개변수는 void로 선언되어야 합니다. *
메모리 작업 함수 memcpy 및 memset과 같은 일반적인 함수 프로토타입은 다음과 같습니다.
void * memcpy(void *dest, const void *src, size_t len);
void * memset(void * buffer, int c, size_t num);
이런 식으로 모든 유형은 포인터는 memcpy 및 memset에 전달될 수 있으며, 이는 메모리 작동 함수의 의미를 실제로 반영합니다. 왜냐하면 포인터가 작동하는 객체는 메모리 유형에 관계없이 단지 메모리 조각이기 때문입니다. memcpy와 memset의 매개변수 유형이 void*가 아니고 char*라면 정말 이상할 것 같습니다! 그러한 memcpy와 memset은 분명히
"저수준의 재미가 없는 순수" 기능이 아닙니다!
다음 코드는 올바르게 실행됩니다.
//예: memset은 모든 유형의 포인터를 허용합니다.
int intarray[100];
memset( intarray, 0, 100*sizeof(int) ) ; //intarray를 0으로 지웁니다
//예: memcpy는 모든 유형의 포인터를 허용합니다
int intarray1[100], intarray2[100];
memcpy( intarray1, intarray2, 100*sizeof( int) ); //intarray2를 intarray1로 복사
흥미로운 점은 memcpy 및 memset 함수도 void * 유형을 반환한다는 것입니다. 표준 라이브러리 함수 작성자는 얼마나 박식한지요!
규칙 5 void는 실제 변수를 나타낼 수 없습니다
다음 코드는 모두 void가 실제 변수를 나타내도록 시도하므로 모두 잘못된 코드입니다.
void a; 🎜>function(void a); //오류
void는 추상화를 구현합니다. 이 세상의 변수는 모두 "유형화"되어 있습니다. 예를 들어 사람은 남자이거나 여자입니다. .
void의 출현은 단지 추상적인 요구를 위한 것입니다. 객체지향의 "추상 기본 클래스" 개념을 올바르게 이해하면 void 데이터 유형을 이해하기 쉽습니다. 추상
기본 클래스의 인스턴스를 정의할 수 없는 것처럼 void(비유적으로 void를 "추상 데이터 유형"이라고 부르겠습니다) 변수를 정의할 수 없습니다.
4. 요약
Small void에는 풍부한 디자인 철학이 담겨 있으며, 프로그래머로서 더 깊은 수준에서 문제를 생각하면 많은 이점을 얻을 수 있습니다. 어떤 유형의 포인터(void*, char*, int*, float*...)에 관계없이 기본 초기 값은 0xCCCCCCCC//이것은 각 컴파일러마다 달라야 하며 이는 vc6용입니다
#include< ; iostream.h>
#include
//#include
void main()
{
void *p1;
int a = 10;
int *p2 = &a;
cout cout p1 = p2;
cout cout << (int)*p2 << endl;
}
/* 출력:
0xCCCCCCCC
10
10
* /
은 선언 시 NULL이 할당되고 삭제 후 즉시 NULL로 설정됩니다.
디버그 버전의 포인터 기본 초기값은 0xCCCCCCCC이고, 릴리스 버전의 초기값은 0x0000000A(내 컴퓨터의 VC6.0)입니다. 포인터에 적합한 초기화 값이 없으면 NULL(0)으로 설정해야 합니다.
좋은 프로그래밍 연습을 위해 포인터를 선언하고 NULL로 초기화하세요. 클래스 멤버인 경우 생성자에서 초기화하세요. 포인터에서 삭제를 사용할 때는 NULL로 설정하세요.
0xCCCCCCCC는 이 포인터가 초기화되지 않았으며 릴리스 상태의 이 값과 동일하지 않음을 나타내기 위해 디버그 상태에서 VC에 의해 생성된 정의되지 않은 포인터 값입니다(우연이 아닌 한). 포인터에 적합한 초기화 값이 없으면 NULL(0)으로 설정해야 합니다.