programing

C 컴파일러와 C++ 컴파일러는 강제하지 않는데 함수 시그니처에 어레이 길이를 허용하는 이유는 무엇입니까?

prostudy 2022. 6. 18. 09:13
반응형

C 컴파일러와 C++ 컴파일러는 강제하지 않는데 함수 시그니처에 어레이 길이를 허용하는 이유는 무엇입니까?

학습 기간 중에 발견한 것은 다음과 같습니다.

#include<iostream>
using namespace std;
int dis(char a[1])
{
    int length = strlen(a);
    char c = a[2];
    return length;
}
int main()
{
    char b[4] = "abc";
    int c = dis(b);
    cout << c;
    return 0;
}  

int dis(char a[1]) , . . . . . . . .[1]도 안 하고 일도 안 것 요.
사용할 수 a[2].같은int a[] ★★★★★★★★★★★★★★★★★」char *a어레이명은 포인터이며 어레이를 전달하는 방법을 알고 있기 때문에 이 부분에 관한 퍼즐은 아닙니다.

가 알고 싶은 것은 가 왜 입니다.int a[1]아면면른른른른른른른른른른른른른?

이는 어레이를 기능에 전달하기 위한 구문의 특이점입니다.

실제로 C에서는 어레이를 전달할 수 없습니다.어레이를 통과해야 하는 것처럼 보이는 구문을 쓰면 어레이의 첫 번째 요소에 대한 포인터가 대신 전달됩니다.

에는 길이되어 있지 않기 은 다음과 .[]실제로는 무시됩니다.

이 구문을 허용하기로 한 결정은 1970년대에 내려졌고 그 이후로 많은 혼란을 야기했다...

첫 번째 치수의 길이는 무시되지만 컴파일러가 오프셋을 올바르게 계산하기 위해서는 추가 치수의 길이가 필요합니다.예제에서는 음음 in in in in in in 。foo함수는 2차원 배열에 포인터를 전달합니다.

#include <stdio.h>

void foo(int args[10][20])
{
    printf("%zd\n", sizeof(args[0]));
}

int main(int argc, char **argv)
{
    int a[2][20];
    foo(a);
    return 0;
}

번째 치수 [10]을 사용하다컴파일러는 사용자가 끝부분에서 인덱스를 작성하는 것을 막지 않습니다(포멀에서는 10개의 요소가 필요하지만 실제는 2개만 제공합니다)., 두 치수의 입니다.[20]각 행의 보폭을 결정하는 데 사용됩니다.여기서 포멀은 실제와 일치해야 합니다.컴파일러에 의해서도, 2차원의 끝을 색인화하는 것을 막을 수 없습니다.

args[row][col]하다

sizeof(int)*(col + 20*row)

「」의 경우는, 과 같이 주의해 주세요.col >= 20그 후 실제로 후속 행(또는 어레이 전체의 끝)에 인덱스를 붙입니다.

sizeof(args[0])하다, 반환하다80sizeof(int) == 4, 만약 sizeof(args)컴파일러 경고는 다음과 같습니다.

foo.c:5:27: warning: sizeof on array function parameter will return size of 'int (*)[20]' instead of 'int [10][20]' [-Wsizeof-array-argument]
    printf("%zd\n", sizeof(args));
                          ^
foo.c:3:14: note: declared here
void foo(int args[10][20])
             ^
1 warning generated.

여기서 컴파일러는 어레이 자체의 크기가 아니라 어레이가 소멸된 포인터의 크기만 표시한다고 경고합니다.

첫째, C는 어레이 경계를 확인하지 않습니다.로컬이든 글로벌이든 스태틱이든 파라미터든 상관없습니다.배열 경계를 확인하는 것은 더 많은 처리를 의미하며, C는 매우 효율적이기 때문에 배열 경계 검사는 필요에 따라 프로그래머에 의해 수행됩니다.

둘째, 배열을 함수에 대한 값을 전달할 수 있는 방법이 있습니다.함수에서 배열을 값으로 반환할 수도 있습니다.구조를 사용하여 새 데이터 유형을 생성하기만 하면 됩니다.예를 들어 다음과 같습니다.

typedef struct {
  int a[10];
} myarray_t;

myarray_t my_function(myarray_t foo) {

  myarray_t bar;

  ...

  return bar;

}

foo.a[1]와 같은 요소에 액세스해야 합니다.추가 ".a"가 이상하게 보일 수 있지만 이 트릭은 C 언어에 뛰어난 기능을 추가합니다.

마음이 내키면 효과적으로 자신의 발을 쏠 수 있는 C의 재미있는 기능입니다.

그 이유는 C가 어셈블리 언어보다 한 단계 위이기 때문이라고 생각합니다.최대 성능을 위해 크기 확인 및 이와 유사한 안전 기능이 제거되었습니다. 프로그래머가 매우 부지런하다면 이는 나쁘지 않습니다.

또한 function 인수에 크기를 할당하면 다른 프로그래머가 함수를 사용할 때 크기 제한을 인식할 수 있다는 장점이 있습니다.포인터를 사용하는 것만으로 다음 프로그래머에게 그 정보가 전달되지 않습니다.

C++에서의 문제 및 해결 방법

그 문제는 Pat과 Matt에 의해 광범위하게 설명되어 왔다.컴파일러는 기본적으로 전달된 인수의 크기를 무시하는 배열 크기의 첫 번째 차원을 무시하고 있습니다.

한편, C++ 에서는, 다음의 2개의 방법으로 이 제한을 간단하게 극복할 수 있습니다.

  • 참조 사용
  • 를 사용합니다.std::arrayC++11 이후)

레퍼런스

기존 어레이를 복사하지 않고 읽기 또는 변경만 시도하고 있는 경우 참조를 쉽게 사용할 수 있습니다.

를 들어, 10으로 해 보겠습니다.int를 「모든 요소」로 합니다.0다음 함수 시그니처를 사용하면 쉽게 할 수 있습니다.

void reset(int (&array)[10]) { ... }

기능은 정상적으로 동작할 뿐만 아니라 어레이의 치수도 적용합니다.

템플릿을 사용하여 위의 코드를 범용화할 수도 있습니다.

template<class Type, std::size_t N>
void reset(Type (&array)[N]) { ... }

으로 이 할 수 .const그럼 10개요소해 보겠습니다.

void show(const int (&array)[10]) { ... }

「 」를 하면,const수식자 수정 가능성을 차단하고 있습니다.


어레이의 표준 라이브러리 클래스

위의 구문이 추악하고 불필요하다고 생각되는 경우 C++11 이후 캔에 넣고 대신 사용할 수 있습니다.

리팩터링된 코드는 다음과 같습니다.

void reset(std::array<int, 10>& array) { ... }
void show(std::array<int, 10> const& array) { ... }

대단하지 않은가?앞서 알려드린 일반적인 코드 트릭이 여전히 유효하다는 것은 말할 것도 없습니다.

template<class Type, std::size_t N>
void reset(std::array<Type, N>& array) { ... }

template<class Type, std::size_t N>
void show(const std::array<Type, N>& array) { ... }

뿐만 아니라 무료로 복사와 의미 이동을 할 수 있습니다.:)

void copy(std::array<Type, N> array) {
    // a copy of the original passed array 
    // is made and can be dealt with indipendently
    // from the original
}

그럼 뭘 망설이는 거야?를 사용해 주세요.

컴파일러에 myArray가 최소 10ints의 배열을 가리키고 있음을 알리려면:

void bar(int myArray[static 10])

myArray [ 10 ]에 액세스 하면, 적절한 컴파일러로 경고가 표시됩니다.static 키워드가 없으면 10은 전혀 의미가 없습니다.

이것은 C의 잘 알려진 "기능"으로, C++는 C 코드를 올바르게 컴파일해야 하기 때문에 C++로 전달됩니다.

문제는 다음 몇 가지 측면에서 발생합니다.

  1. 배열 이름은 포인터와 완전히 동일합니다.
  2. C는 빠르고 원래 개발자는 일종의 "고급 어셈블리"(특히 최초의 "휴대용 운영 체제"를 작성하도록 설계됨)이므로 "숨김" 코드를 삽입하지 않아야 합니다. 따라서 런타임 범위 검사는 "금지"됩니다.
  3. 스태틱 어레이에 액세스하기 위해 생성되는 머신 코드와 다이내믹 어레이(스택 내 또는 할당된 것)는 실제로 다릅니다.
  4. 호출된 함수는 인수로 전달된 배열의 "종류"를 알 수 없기 때문에 모든 것이 포인터가 되어 동일하게 취급됩니다.

어레이는 실제로 C에서 지원되지 않는다고 할 수 있습니다(앞에서 말씀드렸듯이 실제로는 사실이 아니지만 근사치입니다). 어레이는 실제로 데이터 블록에 대한 포인터로 취급되며 포인터 산술로 액세스됩니다.C에는 RTI 형식이 없기 때문에 (포인터 산술 지원을 위해) 함수 프로토타입에서 배열 요소의 크기를 선언해야 합니다.이는 다차원 어레이의 경우 더욱 사실적입니다.

어쨌든 위의 모든 것은 더 이상 사실이 아닙니다.p

대부분의 최신 C/C++ 컴파일러는 경계체크를 지원하지만 표준에서는 (하위 호환성을 위해) 기본적으로 꺼야 합니다.예를 들어 최신 버전의 gcc는 "-O3 -Wall -Wextra"를 사용하여 컴파일 시간 범위 검사를 수행하고 "-founds-checking"을 사용하여 전체 런타임 경계 검사를 수행합니다.

는 타입 C의 하는 것이 .int[5]*int;;;;;;;;;;;;;;;;;;;;;;;typedef int intArray5[5];는 타입의 intArray5로로 합니다.*int되는 .va_list되어 있습니다.stdargs.h(어느쪽이든) 매개 이다.int[5]않다int[5]직접 지정할 수 있습니다.

어레이 타입의 파라미터에 대한 C의 처리는 불합리하다고 생각합니다만, 그 대부분은 명확하게 정의되어 있지 않거나 충분히 고려되지 않았던 애드혹 언어를 도입해, 기존의 실장 프로그램에 대해 행해진 동작 사양과 일치하도록 노력한 결과입니다.그런 관점에서 보면 C의 많은 기발한 행동들은 말이 됩니다. 특히 그 중 많은 것들이 발명되었을 때 오늘날 우리가 알고 있는 언어의 많은 부분이 아직 존재하지 않았다고 생각하면 더욱 그렇습니다.제가 알기로는 C의 이전 버전인 BCPL에서는 컴파일러가 변수 유형을 잘 추적하지 못했습니다.int arr[5];와 동등했다.int anonymousAllocation[5],*arr = anonymousAllocation; 알지도, 알지도, 알지도, 알지도, .arr포인터 또는 배열입니다. 쪽인가로 했을 arr[x] ★★★★★★★★★★★★★★★★★」*arr는, 선언 방법에 관계없이, 포인터로 간주됩니다.

한 가지 미답은 실제 질문입니다.

이미 제시된 답변에서는 배열을 C 또는 C++의 함수에 값으로 전달할 수 없다는 것을 설명하고 있습니다. ''가 '모수'로 이 됩니다.int[] 타입이 있었던 됩니다.int *타입의 변수입니다.int[]을 사용법

그러나 어레이 길이를 명시적으로 지정하는 데 오류가 발생하지 않은 이유는 설명되지 않습니다.

void f(int *); // makes perfect sense
void f(int []); // sort of makes sense
void f(int [10]); // makes no sense

왜 이 마지막이 오류가 아닐까요?

그 이유는 typedefs에 문제가 발생하기 때문입니다.

typedef int myarray[10];
void f(myarray array);

파라미터에서 " "는 할 수 .myarray이름을 지정합니다., 표준 에는 「 」, 「 」, 「 」의 .va_list 「」를 「」로 할 가 있습니다.jmp_buf 타입은 하여 함수 매우 가 됩니다.기능이 , 「어레이 타입」과 할수.vprintf.

컴파일러는 전달된 어레이의 크기가 예상과 동일한지 여부를 확인할 수 있습니다.그렇지 않으면 컴파일러가 문제를 경고할 수 있습니다.

언급URL : https://stackoverflow.com/questions/22677415/why-do-c-and-c-compilers-allow-array-lengths-in-function-signatures-when-they

반응형