Nice programing

func () 대 c99의 func (void)

nicepro 2020. 12. 8. 19:59
반응형

func () 대 c99의 func (void)


void func() 실제로 빈 매개 변수는 모든 인수가 허용됨을 의미합니다.

void func(void) 인수를 허용하지 않습니다.

그러나 Standard C99에서는 다음과 같은 줄을 찾습니다.

6.7.5.3 함수 선언자 (프로토 타입 포함)
14 식별자 목록은 함수 매개 변수의 식별자 만 선언합니다. 함수 정의의 일부인 함수 선언자의 빈 목록은 함수에 매개 변수가 없음을 지정합니다. 해당 함수 정의의 일부가 아닌 함수 선언자의 빈 목록은 매개 변수의 수 또는 유형에 대한 정보가 제공되지 않음을 지정합니다.

표준에 따라, func()그리고 func(void)같은입니까?


TL; DR

선언에서

void func1();     // obsolescent
void func2(void);

행동은 상당히 다릅니다. 첫 번째는 프로토 타입없이 함수를 선언합니다. 인수는 얼마든지받을 수 있습니다! 후자는 매개 변수가없고 인수도 허용하지 않는 프로토 타입으로 함수를 선언하는 반면.

에서 정의

void func1() { }     // obsolescent

void func2(void) { }
  • 전자는 func1매개 변수와 프로토 타입 이없는 함수 선언하고 정의합니다.

  • 후자는 매개 변수가없는 func2 프로토 타입 으로 함수 선언하고 정의합니다 .

이 두 가지는 C 컴파일러 잘못된 수의 인수를 사용하여 프로토 타입 함수를 호출 할 때 진단 메시지를 인쇄 해야하는 반면 프로토 타입 없이 함수를 호출 할 때는 그렇게 할 필요가 없다는 점에서 분명하게 작동합니다.

즉, 위의 정의가 주어지면

func1(1, 2, 3); // need not produce a diagnostic message
func2(1, 2, 3); // must always produce a diagnostic message 
                // as it is a constraint violation

그러나 호출 모두 6.5.2.2p6에 따라 명시 적으로 정의되지 않은 동작이므로 엄격하게 준수하는 프로그램에서는 불법입니다  .

또한 빈 괄호는 더 이상 사용되지 않는 기능으로 간주됩니다.

빈 괄호 (프로토 타입 형식 매개 변수 유형 선언자가 아님)가있는 함수 선언자를 사용하는 것은 더 이상 사용되지 않는 기능입니다.

별도의 매개 변수 식별자 및 선언 목록 (프로토 타입 형식 매개 변수 유형 및 식별자 선언자가 아님)과 함께 함수 정의를 사용하는 것은 구식 기능입니다.

상세히

관련이 있지만 별개의 두 가지 개념이 있습니다. 매개 변수와 인수입니다.

  • 인수는 함수에 전달 된 값입니다.

  • 매개 변수는 함수가 입력되었을 때 인수 값으로 설정되는 함수 내의 이름 / 변수입니다.

다음 발췌 부분에서 :

int foo(int n, char c) {
    ...
}

...

    foo(42, ch);

nc매개 변수입니다. 42그리고 ch인수입니다.

인용 된 발췌 부분은 함수의 매개 변수에만 관련이 있지만 프로토 타입이나 함수의 인수에 대해서는 언급하지 않습니다.


선언 void func1() 기능이 있다는 의미 func1로 호출 할 수있는 임의의 개수의 인수 지정 인수의 수에 관한 정보, 즉하지 선언 반면, (별도의 선언 된 바와 같이, C99없이 매개 변수 사양 "기능으로이 지정) void func2(void)기능이 수단 인수 를 전혀 func2받아들이지 않습니다 .

하는 내 질문 수단의 인용 함수 정의 , void func1()그리고 void func2(void)모두 신호들을 더 없는지 매개 변수 , 즉 인수의 값으로 설정되는 변수 이름 함수가 입력 될 때. 전자 void func() {}대조 void func();func실제로 매개 변수를 취하지 않는다고 선언하는 반면, 후자는 매개 변수 유형이 지정 되지 않은 함수에 func대한 선언입니다 (프로토 타입없는 선언).

그러나 그들은 정의 측면에서

  • 정의 void func1() {}는 프로토 타입을 선언하지 않는 반면는 매개 변수 유형 목록 이 아니기 void func2(void) {}때문에 선언하는 ()반면 (void)매개 변수 유형 목록 ( 6.7.5.3.10 )입니다.

    목록의 유일한 항목 인 이름이 지정되지 않은 매개 변수 void 유형의 특별한 경우는 함수에 매개 변수가 없음을 지정합니다.

    및 추가 6.9.1.7

    선언자가 매개 변수 유형 목록을 포함하는 경우 목록은 또한 모든 매개 변수의 유형을 지정합니다. 이러한 선언자는 나중에 동일한 번역 단위에서 동일한 함수에 대한 호출을위한 함수 프로토 타입 역할도합니다. 선언자가 식별자 목록을 포함하는 경우 매개 변수의 유형은 다음 선언 목록에 선언되어야합니다. 두 경우 모두 매개 변수 유형 목록에 대해 6.7.5.3에 설명 된대로 각 매개 변수의 유형이 조정됩니다. 결과 유형은 객체 유형이어야합니다.

    에 대한 함수 정의 선언자에 매개 변수 유형 list가 포함되어 func1있지 않으므로 함수에 프로토 타입이 없습니다.

  • void func1() { ... }인수의 개수에 관계없이 호출 할 수 있지만 void func2(void) { ... }인수 (6.5.2.2) 로 호출하는 것은 컴파일 타임 오류입니다 .

    호출 된 함수를 나타내는 표현식 에 prototype을 포함하는 유형이있는 경우 인수의 수는 매개 변수의 수와 일치해야합니다. 각 인수는 해당 매개 변수 유형의 규정되지 않은 버전을 가진 객체에 값이 할당 될 수있는 유형을 가져야합니다.

    (강조 내)

    이것은 표준에 따라 준수 구현 이이 문제에 대한 적어도 하나의 진단 메시지를 표시 해야 한다고 말하는 제약 조건 입니다. 그러나 프로토 타입이 없기 때문에 진단을 생성하는 데 적합한 구현이 필요하지 않습니다.func1


그러나 인수 개수가 매개 변수 개수와 같지 않으면 동작이 정의되지 않습니다. 6.5.2.2p6 :

호출 된 함수를 나타내는 표현식 에 프로토 타입이 포함 되지 않은 유형이있는 경우 [...] 인수 수가 매개 변수 수와 같지 않으면 동작이 정의되지 않습니다.

따라서 이론적으로 준수하는 C99 컴파일러는이 경우 오류를 발생 시키거나 경고를 진단 할 수도 있습니다. StoryTellerclang이이를 진단 할 수 있다는 증거를 제공 했습니다 . 그러나 내 GCC가 그렇게하지 않는 것 같습니다 (그리고 이것은 일부 오래된 모호한 코드와도 호환되기 위해 필요할 수도 있습니다).

void test() { }

void test2(void) { }

int main(void) {
    test(1, 2);
    test2(1, 2);
}

위의 프로그램을로 컴파일 gcc -std=c99 test.c -Wall -Werror하면 출력은 다음과 같습니다.

test.c: In function ‘main’:
test.c:7:5: error: too many arguments to function ‘test2’
     test2(1, 2);
     ^~~~~
test.c:3:6: note: declared here
 void test2(void) { }
      ^~~~~

That is, the arguments are not checked at all against the parameters of a function whose declaration in definition is not prototyped (test) whereas GCC considers it as a compile-time error to specify any arguments to a prototyped function (test2); any conforming implementation must diagnose this as it is a constraint violation.


The significant part of the quote is highlighted in bold below:

6.7.5.3 Function declarators (including prototypes) 14 An identifier list declares only the identifiers of the parameters of the function. An empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters. The empty list in a function declarator that is not part of a definition of that function specifies that no information about the number or types of the parameters is supplied.

So, when the parameter list is empty for a function with its body, they are the same. But of it is just a declaration of a function.

void function1(); // No information about arguments
void function2(void); // Function with zero arguments

void function3() {
    // Zero arguments
}

void function4(void) {
    // Zero arguments
}

according to the standard, func() and func(void) is the same?

No. func(void) says the function takes no arguments at all; whereas func() says the function takes an unspecified number of arguments. Both are valid but the func() style are obsolete and shouldn't be used.

This is an artifact from pre-standard C. C99 marked this as obsolete.

6.11.6 Function declarators:

The use of function declarators with empty parentheses (not prototype-format parameter type declarators) is an obsolescent feature.

As of C11, it still remains as obsolescent and hasn't been removed from the standard.


The empty parameter list inside a function definition means that it does not include a prototype nor has any parameters.

C11 §6.9.1/7 Function definitions (emphasis in ongoing quotes is mine)

The declarator in a function definition specifies the name of the function being defined and the identifiers of its parameters. If the declarator includes a parameter type list, the list also specifies the types of all the parameters; such a declarator also serves as a function prototype for later calls to the same function in the same translation unit.

The question asks:

according to the standard, func() and func(void) is the same?

No. The essential difference between void func() and void func(void) lies in their calls.

C11 §6.5.2.2/2 Function calls (within constraints section):

If the expression that denotes the called function has a type that includes a prototype, the number of arguments shall agree with the number of parameters. Each argument shall have a type such that its value may be assigned to an object with the unqualified version of the type of its corresponding parameter.

Notice that parameters ≠ arguments. The function may contain no parameters, but it may have multiple arguments.

As function defined with empty parameters does not introduce a prototype, it's not checked against its calls, so in theory it may be supplied with whatever number of arguments.

However, it is technically an undefined behavior to call such function with at least one argument (see Antti Haapala's comments).

C11 §6.5.2.2/6 Function calls (within semantics section):

If the number of arguments does not equal the number of parameters, the behavior is undefined.

Hence, the difference is subtle:

  • When a function is defined with void, it won't compile when number of arguments don't match with parameters (along with their types), because of constaint violation (§6.5.2.2/2). Such situation requires diagnostic message from conforming compiler.
  • If it is defined with empty parameters, it may or may not compile (there is no requirement for diagnostic message from conforming compiler), however it's UB to call such function.

Example:

#include <stdio.h>

void func1(void) { puts("foo"); }
void func2()     { puts("foo"); }

int main(void)
{
    func1(1, 2); // constraint violation, it shouldn't compile
    func2(3, 4); // may or may not compile, UB when called
    return 0;
}

Note that optimizing compiler may cut off the arguments in such case. For instance, this is how Clang compiles the above code (excluding func1's call) with -01 on x86-64 according to the SysV ABI calling conventions:

main:                                   # @main
        push    rax          ; align stack to the 16-byte boundary
        call    func2        ; call func2 (no arguments given)
        xor     eax, eax     ; set zero as return value
        pop     rcx          ; restore previous stack position (RSP)
        ret

참고URL : https://stackoverflow.com/questions/41803937/func-vs-funcvoid-in-c99

반응형