C의 "정적"기능은 무엇입니까?


506

문제는 일반에 관한 것이었다 기능이 아닌 static 의견에 명시된 바와 같이.

static변수가 무엇인지 이해 하지만 static함수 는 무엇 입니까?

그리고 왜 함수를 선언하면 (WITHOUT ) void print_matrix이라고 말하고 a.cinclude- 함께 -내가 그것을 선언 하면 컴파일하면 컴파일됩니까?a.h"a.c""print_matrix@@....) already defined in a.obj"static void print_matrix

업데이트 일을 정리하는 것만으로 .c도 많은 사람들이 지적한 것처럼 포함 이 나쁘다는 것을 알고 있습니다. 난 그냥 일시적으로 빈 공간에 어떻게 main.c내가 적절한으로하는 방법 그룹에 모든 기능의 더 나은 아이디어 때까지 .h.c파일을. 일시적인 빠른 솔루션입니다.

답변:


685

static함수는 동일한 파일 (보다 정확하게 동일한 변환 단위 )의 다른 함수에서만 볼 수있는 함수입니다 .

편집 : 생각하는 사람들을 위해, 질문의 저자는 '클래스 방법'을 의미한다고 생각합니다. 질문에 태그가 붙어 있기 때문에 C그는 평범한 오래된 C 함수를 의미합니다. (C ++ / Java / ...) 클래스 메소드의 static경우 클래스 자체에서이 메소드를 호출 할 수 있으며 해당 클래스의 인스턴스는 필요하지 않습니다.


2
실제로 나는 C ++에 태그를 지정하지 않았고 일부 관리자는 아마도 C ++에 관한 것이므로 C ++의 차이점은 무엇입니까?
Slava V

16
C ++ 메소드는 종종 "멤버 함수"라고 불리우므로 C ++이 약간의 모호함을 도입한다는 데 동의합니다. 그것은 당신의 잘못이 아닙니다. 언어는 단지 두 가지 다른 목적으로 키워드를 사용합니다.

2
그는 여전히 C ++ 함수를 의미합니다. C ++ 멤버 함수가 아닌 C ++ 무료 함수입니다.
궤도에서 가벼운 경주

3
@Chuck : C ++ 용어는 "method"라는 단어를 사용하지 않습니다. 그것은 Java 용어입니다. C ++ 표준 문서에서는 항상 "멤버 함수"라고합니다 ( 이 답변 또는 C ++ 대 Java 용어에 대한용어집 (예 : C ++은 "데이터 멤버"를 사용하고 Java는 "필드"등을 사용함) 참조).
ShreevatsaR

6
이 대답을 조금 명확히하기 위해 함수 의 이름 은 해당 이름 의 첫 번째 선언 아래에서 동일한 번역 단위의 다른 부분에서만 볼 수 있습니다. 함수는 다른 수단, 예를 들어 함수 포인터를 통해 다른 유닛 (및 동일한 유닛의 이전 부분)으로부터 호출 될 수있다.
MM

199

C의 정적 함수와 C ++의 정적 멤버 함수에는 큰 차이가 있습니다. C에서 정적 함수는 변환 단위 (컴파일 된 오브젝트 파일) 외부에서 볼 수 없습니다. 다시 말해, 함수를 정적으로 만들면 범위가 제한됩니다. 정적 함수는 * .c 파일에 대해 "비공개"인 것으로 생각할 수 있습니다 (엄격히 정확하지는 않지만).

C ++에서 "정적"은 멤버 함수 및 클래스의 데이터 멤버에도 적용 할 수 있습니다. 정적 데이터 멤버는 "클래스 변수"라고도하며 비 정적 데이터 멤버는 "인스턴스 변수"입니다. 이것이 스몰 토크 용어입니다. 이는 클래스의 모든 객체가 공유하는 정적 데이터 멤버의 사본이 하나만있는 반면 각 객체에는 비 정적 데이터 멤버의 고유 한 사본이 있습니다. 따라서 정적 데이터 멤버는 기본적으로 전역 변수, 즉 클래스 멤버입니다.

비 정적 멤버 함수는 정적 및 비 정적 클래스의 모든 데이터 멤버에 액세스 할 수 있습니다. 정적 멤버 함수는 정적 데이터 멤버에서만 작동 할 수 있습니다.

이것을 생각하는 한 가지 방법은 C ++ 정적 데이터 멤버와 정적 멤버 함수가 객체가 아니라 전체 클래스에 속한다는 것입니다.


42
C ++에는 파일 정적 기능도 있습니다. C를 이것으로 가져올 필요가 없습니다.
궤도에서 가벼움 경주

17
C ++에서 정적 함수는 정적 함수입니다. 정적 멤버 함수는 메소드라고도하는 정적 멤버 함수입니다. C에 멤버가 없다고해서 함수가 "C"인 것은 아닙니다.
Gerasimos R

3
전역 var와 클래스 정적 var (네임 스페이스 제외) 사이에 차이점이 있습니까?
Alexander Malakhov 2016 년

3
네임 스페이스가 주요 차이점입니다. 또 다른 차이점은 정적 데이터 멤버를 비공개로 만들 수 있으므로 클래스의 멤버 함수 내에서만 액세스 할 수 있다는 것입니다. 즉, 전역 변수와 비교하여 정적 데이터 멤버를 훨씬 더 많이 제어 할 수 있습니다.
Dima

2
정적 함수를 .c 파일의 전용으로 생각하는 것이 왜 정확하지 않은지 누군가 설명 할 수 있습니까? 뭐라고 남았 어?
YoTengoUnLCD

77

C ++의 함수에서 static 키워드는 두 가지 용도로 사용됩니다.

첫 번째는 함수가 내부 연결이있는 것으로 표시하여 다른 변환 단위에서 참조 할 수 없도록하는 것입니다. 이 사용법은 C ++에서 더 이상 사용되지 않습니다. 이름없는 네임 스페이스가이 사용법에 선호됩니다.

// inside some .cpp file:

static void foo();    // old "C" way of having internal linkage

// C++ way:
namespace
{
   void this_function_has_internal_linkage()
   {
      // ...
   }
}

두 번째 사용법은 클래스와 관련이 있습니다. 클래스에 정적 멤버 함수가있는 경우 함수는 클래스의 멤버이며 다른 멤버에 대한 일반적인 액세스 권한이 있지만 특정 객체를 통해 호출 할 필요는 없습니다. 다시 말해, 그 함수 내부에는 "this"포인터가 없습니다.


1
문제는 c의 정적에 관한 것입니다.
Deqing

8
@Deqing 질문은 원래 C ++로 태그되었고 저자는 ".cpp"파일 사용에 대해 이야기합니다.
Brian Neal

57

실행 가능한 최소 다중 파일 범위 예

여기 static에서는 여러 파일에서 함수 정의 범위에 어떤 영향을 미치는지 설명 합니다.

ac

#include <stdio.h>

/* Undefined behavior: already defined in main.
 * Binutils 2.24 gives an error and refuses to link.
 * /programming/27667277/why-does-borland-compile-with-multiple-definitions-of-same-object-in-different-c
 */
/*void f() { puts("a f"); }*/

/* OK: only declared, not defined. Will use the one in main. */
void f(void);

/* OK: only visible to this file. */
static void sf() { puts("a sf"); }

void a() {
    f();
    sf();
}

main.c

#include <stdio.h>

void a(void);        

void f() { puts("main f"); }

static void sf() { puts("main sf"); }

void m() {
    f();
    sf();
}

int main() {
    m();
    a();
    return 0;
}

GitHub의 상류 .

컴파일하고 실행하십시오.

gcc -c a.c -o a.o
gcc -c main.c -o main.o
gcc -o main main.o a.o
./main

산출:

main f
main sf
main f
a sf

해석

  • sf각 파일마다 하나씩 두 개의 개별 기능이 있습니다.
  • 단일 공유 기능이 있습니다 f

평소와 같이 범위가 작을수록 좋습니다. 가능하면 항상 함수를 선언하십시오 static.

C 프로그래밍에서 파일은 종종 "클래스"를 나타내는 데 사용되며 static함수는 클래스의 "비공개"메소드를 나타냅니다.

일반적인 C 패턴은 this구조체를 첫 번째 "메서드"인수로 전달하는 것인데, 기본적으로 C ++이 수행하는 작업입니다.

그것에 대해 말하는 표준

C99 N1256 draft 6.7.1 "스토리지 클래스 지정자"는 "스토리지 클래스 지정자"라고 말합니다 static.

6.2.2 / 3 "식별자 링크"는 다음을 static의미합니다 internal linkage.

객체 또는 함수에 대한 파일 범위 식별자 선언에 스토리지 클래스 지정자 정적이 포함되어 있으면 식별자에 내부 연결이 있습니다.

그리고 6.2.2 / 2는 internal linkage우리의 예제에서와 같이 동작 한다고 말합니다 :

전체 프로그램을 구성하는 변환 단위 및 라이브러리 세트에서 외부 링크가있는 특정 식별자의 각 선언은 동일한 객체 또는 함수를 나타냅니다. 하나의 번역 단위 내에서 내부 연결이있는 식별자의 각 선언은 동일한 객체 또는 함수를 나타냅니다.

여기서 "번역 단위"는 사전 처리 후 소스 파일입니다.

GCC가 ELF (Linux)를 위해 어떻게 구현합니까?

STB_LOCAL바인딩.

우리가 컴파일하면 :

int f() { return 0; }
static int sf() { return 0; }

다음과 같이 심볼 테이블을 분해하십시오.

readelf -s main.o

출력에는 다음이 포함됩니다.

Num:    Value          Size Type    Bind   Vis      Ndx Name
  5: 000000000000000b    11 FUNC    LOCAL  DEFAULT    1 sf
  9: 0000000000000000    11 FUNC    GLOBAL DEFAULT    1 f

바인딩은 그들 사이의 유일한 중요한 차이점입니다. 섹션에 Value대한 오프셋 일 뿐이 .bss므로 차이가있을 것으로 예상합니다.

STB_LOCALhttp://www.sco.com/developers/gabi/2003-12-17/ch4.symtab.html 의 ELF 사양에 설명되어 있습니다 .

STB_LOCAL 로컬 심볼은 해당 정의를 포함하는 오브젝트 파일 외부에 표시되지 않습니다. 동일한 이름의 로컬 기호가 서로 방해하지 않고 여러 파일에 존재할 수 있습니다

표현하기에 완벽한 선택 static입니다.

정적이없는 함수는 STB_GLOBAL이며 사양은 다음과 같습니다.

링크 편집기가 재배치 가능한 여러 오브젝트 파일을 결합 할 때 동일한 이름을 가진 STB_GLOBAL 기호의 다중 정의를 허용하지 않습니다.

이는 여러 비 정적 정의의 링크 오류와 일치합니다.

로 최적화를 크랭크 -O3하면 sf심볼이 심볼 테이블에서 완전히 제거됩니다. 어쨌든 외부에서는 사용할 수 없습니다. TODO 최적화가 없을 때 심볼 테이블에 정적 함수를 유지하는 이유는 무엇입니까? 그들은 무엇이든 사용할 수 있습니까?

또한보십시오

C ++ 익명 네임 스페이스

C ++에서는 정적 대신 익명 네임 스페이스를 사용하여 비슷한 효과를 얻을 수 있지만 유형 정의를 숨 깁니다. 이름이없는 / 익명 네임 스페이스와 정적 함수


3
참고 : void f() { puts("sf"); }(즉,의 두 정의 f())는 진단이 필요없는 정의되지 않은 동작을 유발합니다. 실제로 오류 메시지를 보는 것은 링커 품질 문제입니다.
MM

2
이것이 가장 정확하고 정확한 설명입니다! 당신보다!
Aqua

20

다음은 일반 C 함수에 관한 것입니다. C ++ 클래스에서 수정 자 '정적'은 다른 의미를 갖습니다.

파일이 하나 뿐인 경우이 수정자는 아무런 차이가 없습니다. 파일이 여러 개인 대규모 프로젝트에서는 차이점이 있습니다.

C에서는 모든 "모듈"(sample.c와 sample.h의 조합)이 독립적으로 컴파일 된 후 컴파일 된 모든 객체 파일 (sample.o)이 링커에 의해 실행 파일에 함께 연결됩니다.

기본 파일에 포함 된 여러 파일이 있고 그 중 두 개는 편의상 내부에서만 사용되는 기능을 가지고 있다고 가정 해 봅시다 add(int a, b). 컴파일러는 두 모듈에 대한 객체 파일을 쉽게 만들 수 있지만 링커는 오류를 발생시킵니다. 동일한 이름을 가진 두 개의 함수를 찾고 어떤 함수를 사용해야하는지 알지 못합니다 (링크가없는 경우에도 다른 곳에서 사용되지 않고 자체 파일로 사용되기 때문에).

이것이 바로 내부 정적 함수 인이 함수를 만드는 이유입니다. 이 경우 컴파일러는 링커에 대해 일반적인 "이 링크를 연결할 수 있습니다"플래그를 만들지 않으므로 링커에서이 기능을 볼 수 없으며 오류가 발생하지 않습니다.


16

첫째 : 일반적으로 .cpp다른 파일에 파일 을 포함시키는 것은 좋지 않습니다. 다음 과 같은 문제가 발생합니다. :-) 일반적인 방법은 별도의 컴파일 단위를 만들고 포함 된 파일의 헤더 파일을 추가하는 것입니다.

둘째 :

C ++에는 혼란스러운 용어가 있습니다. 댓글에서 지적 할 때까지 그것에 대해 알지 못했습니다.

a) static functions-C에서 상속되었으며 여기에서 말하는 내용. 수업 외 정적 함수 는 현재 컴파일 단위 외부에서는 보이지 않으므로 a.obj에는 복사본이 있고 다른 코드에는 독립적 인 복사본이 있습니다. (여러 코드 사본으로 최종 실행 파일을 블로 팅).

b) static member function-정적 메소드 인 Object Orientation 용어 . 수업 안에 살고 있습니다. 이것을 객체 인스턴스가 아닌 클래스로 호출합니다.

이 두 가지 정적 함수 정의는 완전히 다릅니다. 조심하십시오-여기에 용이 있습니다.


글쎄, 적절한 .hpp와 함께 파일을 라이브러리로 구성하는 방법을 결정할 때까지 main.cpp에서 임시로 공간을 정리하기 위해 수행합니다. 이것을하는 더 좋은 아이디어가 있습니까?
Slava V

1
C ++의 올바른 용어는 메서드가 아니라 멤버 함수입니다. C ++ 법률에는 "방법"이 없습니다. 방법은 일반적인 OO 용어입니다. C ++는 멤버 함수를 통해 구현합니다.
Brian Neal

14

정적 함수 정의는이 기호를 내부로 표시합니다. 따라서 외부에서 링크하는 것은 보이지 않지만 동일한 컴파일 단위, 일반적으로 동일한 파일의 함수에만 표시됩니다.


7

정적 함수는 클래스 인스턴스와 달리 클래스 자체에서 호출 할 수있는 함수입니다.

예를 들어 비정 적은 다음과 같습니다.

Person* tom = new Person();
tom->setName("Tom");

이 메소드는 클래스 자체가 아닌 클래스의 인스턴스에서 작동합니다. 그러나 인스턴스 없이도 작동 할 수있는 정적 메서드를 사용할 수 있습니다. 이것은 때때로 팩토리 패턴에서 사용됩니다 :

Person* tom = Person::createNewPerson();

2
"기능"이 아닌 정적 "방법"에 대해 이야기하고있는 것 같습니다.
Slava V

클래스 내에서 정적 함수를 참조한다고 가정했습니다.
앵무새

C ++에서 "메소드"를 "메소드 함수"라고하면 더 명확 할 것입니다. 글쎄, 지금은 :) 어쨌든 감사합니다
Slava V

5
C ++에는 "메소드"가없고 함수 만 있습니다. C ++ 표준에서는 "함수"만 언급하지 않고 "함수"만 언급합니다.
Brian Neal

1
@Puddle 나는 당신이 말하는 것을 알고 있지만 C ++ 표준에는 "방법"에 대한 정의가 없습니다. C ++에는 다양한 종류의 함수 만 있습니다. "방법"은 일반적인 OO 용어이며 다른 언어로 그리고 비공식적으로 C ++로 사용됩니다. 메소드는 공식적으로 C ++에서 "멤버 함수"로 알려져 있습니다.
브라이언 닐

7

사소한 의미 : 정적 함수는 변환 단위에 표시되며 대부분의 실제 경우에는 함수가 정의 된 파일입니다.받는 오류는 일반적으로 하나의 정의 규칙 위반이라고합니다.

표준은 아마도 다음과 같이 말합니다.

"모든 프로그램에는 해당 프로그램에서 사용되는 모든 비 인라인 함수 또는 객체에 대한 정의가 정확히 하나만 있어야하며 진단이 필요하지 않습니다."

이것이 정적 함수를 보는 C 방식입니다. 그러나 이것은 C ++에서 더 이상 사용되지 않습니다.

또한 C ++에서는 멤버 함수를 정적으로 선언 할 수 있습니다. 이것들은 대부분 메타 함수입니다. 즉, 특정 객체의 동작 / 상태를 설명 / 수정하지 않지만 전체 클래스 자체에 작용합니다. 또한 이는 정적 멤버 함수를 호출하기 위해 오브젝트를 작성할 필요가 없음을 의미합니다. 또한 이는 해당 함수 내에서만 정적 멤버 변수에만 액세스 할 수 있음을 의미합니다.

Parrot의 예제에 프로그램의 수명 기간 동안 단일 객체를 가져 오거나 사용하기 위해 이러한 종류의 정적 멤버 함수를 기반으로하는 Singleton 패턴을 추가하려고합니다.


7

정적 함수에 대한 답변은 언어에 따라 다릅니다.

1) C와 같은 OOPS가없는 언어에서는 정의 된 파일 내에서만 함수에 액세스 할 수 있습니다.

2) C ++과 같은 OOPS를 사용하는 언어에서는 인스턴스를 만들지 않고 클래스에서 함수를 직접 호출 할 수 있습니다.


사실이 아닙니다. 두 번째 단락의 설명 "을 참조 정적 멤버 함수 클래스의"가 아닌 " 정적 기능 ". C ++에서 정규화 된 함수에는 C와 static마찬가지로 파일 범위도 있습니다.
RobertS는

0

정적 기능은이 파일에서만 볼 수 있기 때문입니다. 실제로 어떤 함수에 "정적"을 선언하면 컴파일러에서 최적화 를 수행 할 수 있습니다 .

다음은 간단한 예입니다.

main.c

#include <stdio.h>

static void test() 
{
    ghost(); // This is an unexist function.
}

int main()
{
    int ret = 0;

#ifdef TEST
#else
    test();
#endif
    return (ret);
} 

그리고 컴파일

gcc -o main main.c

실패했음을 알 수 있습니다. ghost () 함수도 구현하지 않기 때문입니다.

그러나 다음 명령을 사용하면 어떨까요?

gcc -DTEST -O2 -o main main.c

그것은 성공 ,이 프로그램은 일반적으로 실행할 수 있습니다.

왜? 세 가지 핵심 사항이 있습니다.

  1. -O2 : 컴파일러 최적화 수준 2 이상
  2. -DTEST : TEST를 정의하므로 test ()가 호출되지 않습니다.
  3. test ()에 "정적"으로 정의되었습니다.

이 세 가지 조건이 모두 충족되는 경우에만 컴파일을 전달할 수 있습니다. 이 "정적"선언으로 인해 컴파일러는 test ()가 다른 파일에서 절대 호출되지 않음을 확인할 수 있습니다. 컴파일러는 컴파일 할 때 test ()를 제거 할 수 있습니다. test ()가 필요하지 않기 때문에 ghost ()가 정의되어 있는지 구현되어 있는지는 중요하지 않습니다.


0

" C에서 " static"기능 은 무엇입니까 ? "

처음부터 시작합시다.

그것은 모두 "linkage"라는 것을 기반으로합니다.

" 다른 범위 나 같은 범위에서 한 번 이상 선언 된 식별자는 연결이라는 프로세스에 의해 동일한 객체 나 기능을 참조하도록 만들 수 있습니다. 29) 외부, 내부 및 없음이라는 세 가지 종류의 연결이 있습니다. "

출처 : C18, 6.2.2 / 1


"전체 프로그램을 구성하는 번역 단위 및 라이브러리 세트에서 외부 연계 가있는 특정 식별자의 각 선언은 동일한 객체 또는 기능을 나타냅니다. 하나의 번역 단위 내에서 내부 연계 가있는 식별자의 각 선언은 동일한 객체 또는 기능을 나타냅니다. 연결이없는 식별자의 각 선언은 고유 한 엔티티를 나타냅니다. "

출처 : C18, 6.2.2 / 2


스토리지 클래스 지정자없이 함수가 정의 된 경우 함수는 extern기본적으로 al 링크를 갖습니다 .

"함수에 대한 식별자 선언에 스토리지 클래스 지정자가없는 경우, 해당 링크는 스토리지 클래스 지정자 extern 으로 선언 된 것처럼 정확하게 결정됩니다 ."

출처 : C18, 6.2.2 / 5

즉, 프로그램에 여러 번역 단위 / 소스 파일 ( .c또는 .cpp) 이 포함 된 경우 프로그램에 있는 모든 번역 단위 / 소스 파일에이 기능이 표시됩니다 .

경우에 따라 문제가 될 수 있습니다. fe 두 개의 다른 함수 (정의)를 사용하지만 두 개의 다른 컨텍스트 (실제로는 파일 컨텍스트)에서 동일한 함수 이름을 사용하려면 어떻게해야합니까?

C C ++ static에서 파일 범위의 함수 ( C ++에서 클래스 의 정적 멤버 함수아니 거나 다른 블록 내의 함수 가 아님)에 적용된 스토리지 클래스 한정자 는 이제 각 함수가 내부에서만 볼 수 있음을 나타냅니다. 다른 TLU / 파일이 아닌 정의 된 변환 단위 / 소스 파일

"객체 또는 함수의 파일 범위 식별자 선언에 스토리지 클래스 지정자 static이 포함되어 있으면 식별자에 내부 연결이 있습니다. 30)"


30) 함수 선언은 스토리지 클래스 지정자를 정적 일 수 있습니다 (파일 범위에있는 경우에만). 6.7.1을 참조하십시오.

출처 : C18, 6.2.2 / 3


따라서 static함수는 의미가 있습니다.

  1. 프로그램이 여러 번역 단위 / 소스 파일 ( .c또는 .cpp)로 구성되어 있습니다.

  2. 함수의 범위를 특정 함수가 정의 된 파일로 제한하려고합니다.

두 가지 요구 사항이 모두 일치하지 않으면로 자격을 부여하는 것에 대해 머리를 감쌀 필요가 없습니다 static.


사이드 노트 :

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.