태그 보관물: c

c

C 배열의 길이가 0이 아닌 이유는 무엇입니까? 0의 크기를 갖는 것이 완벽합니다. 또한 정적

C11 표준에 따르면 크기와 가변 길이가 모두 “0보다 큰 값을 갖습니다.” 길이가 0을 허용하지 않는 이유는 무엇입니까?

특히 가변 길이 배열의 경우 가끔씩 0의 크기를 갖는 것이 완벽합니다. 또한 정적 배열의 크기가 매크로 또는 빌드 구성 옵션 인 경우에도 유용합니다.

흥미롭게도 GCC (및 clang)는 길이가 0 인 배열을 허용하는 확장을 제공합니다. Java는 길이가 0 인 배열도 허용합니다.



답변

내가 베풀어야 할 문제는 C 배열이 할당 된 메모리 덩어리의 시작을 가리키는 포인터라는 것입니다. 크기가 0이면 포인터가 없음을 의미합니다. 당신은 아무것도 가질 수 없으므로 임의의 것이 선택되어야 할 것입니다. null길이가 0 인 배열은 null 포인터처럼 보이기 때문에을 사용할 수 없습니다 . 그리고 그 시점에서 모든 구현마다 다른 임의의 동작을 선택하여 혼란을 초래합니다.


답변

배열이 일반적으로 메모리에 어떻게 배치되는지 살펴 보겠습니다.

         +----+
arr[0] : |    |
         +----+
arr[1] : |    |
         +----+
arr[2] : |    |
         +----+
          ...
         +----+
arr[n] : |    |
         +----+

arr첫 번째 요소의 주소를 저장 하는 별도의 개체 이름은 없습니다 . 배열이 표현식에 나타나면 C 는 필요에 따라 첫 번째 요소의 주소를 계산 합니다.

그래서,하자이 생각 : 0 요소의 배열이없는 것이 더 저장 이 따로 세트를 배열 주소 계산 거기에 아무것도 의미 에서을 (다른 방법을 넣어은 식별자에 대한 객체의 매핑이 없습니다). ” int메모리를 차지하지 않는 변수 를 만들고 싶습니다 .” 무의미한 작업입니다.

편집하다

Java 배열은 C 및 C ++ 배열과 완전히 다른 동물입니다. 그것들은 기본 유형이 아니라에서 파생 된 참조 유형입니다 Object.

편집 2

아래 주석에서 제기 된 요점- “0보다 큰”제한 조건은 크기가 상수 표현식을 통해 지정된 배열에만 적용됩니다 . VLA의 길이는 0이 될 수 있습니다. 값이 0이 아닌 상수 식으로 VLA를 선언하는 것은 제약 조건 위반이 아니지만 정의되지 않은 동작을 호출합니다.

VLA는 일반 배열 다른 동물 이며, 구현시 0 크기를 허용 할 수 있습니다 . static프로그램을 시작하기 전에 이러한 오브젝트의 크기를 알아야하므로 선언 할 수 없거나 파일 범위에 있을 수 없습니다 .

C11에서 VLA를 지원하기 위해 구현이 필요하지 않은 것도 가치가 없습니다.


답변

일반적으로 제로 (사실 변수) 크기 배열이 런타임에 크기를 알기를 원합니다. 그런 다음 a로 묶고 다음 과 같이 유연한 배열 멤버를struct 사용하십시오 .

struct my_st {
   unsigned len;
   double flexarray[]; // of size len
};

분명히 유연한 배열 구성원은 마지막 배열이어야하며 struct이전에 무언가가 있어야합니다. 종종 이는 유연한 배열 구성원의 실제 런타임 점유 길이와 관련이 있습니다.

물론 다음을 할당합니다.

 unsigned len = some_length_computation();
 struct my_st*p = malloc(sizeof(struct my_st)+len*sizeof(double));
 if (!p) { perror("malloc my_st"); exit(EXIT_FAILURE); };
 p->len = len;
 for (unsigned ix=0; ix<len; ix++)
    p->flexarray[ix] = log(3.0+(double)ix);

AFAIK, 이것은 C99에서 이미 가능했으며 매우 유용합니다.

BTW, 유연한 배열 구성원은 C ++에 존재하지 않습니다 (구성 및 파괴시기와 방법을 정의하기 어렵 기 때문에). 그러나 미래 std :: dynarray 참조


답변

표현식 type name[count]이 일부 함수로 작성된 경우 C 컴파일러에 스택 프레임 sizeof(type)*count바이트 를 할당 하고 배열의 첫 번째 요소 주소를 계산하도록 지시 합니다.

표현식 type name[count]이 모든 함수 외부에 작성되고 정의를 정의하는 경우 C 컴파일러에게 데이터 세그먼트 sizeof(type)*count바이트 를 할당 하고 배열의 첫 번째 요소 주소를 계산하도록 지시 합니다.

name실제로 배열의 첫 번째 요소의 주소를 저장하는 상수 객체이며 일부 메모리의 주소를 저장하는 모든 객체를 포인터라고하므로 name배열이 아닌 포인터로 취급하는 이유 입니다. C의 배열은 포인터를 통해서만 액세스 할 수 있습니다.

count0으로 평가되는 상수 식인 경우 C 컴파일러에 스택 프레임 또는 데이터 세그먼트에 0 바이트를 할당하고 배열의 첫 번째 요소 주소를 반환하도록 지시하지만이 작업의 문제점은 첫 번째 요소라는 것입니다. 길이가 0 인 배열이 존재하지 않으며 존재하지 않는 주소를 계산할 수 없습니다.

이것은 요소 번호에 합리적입니다. 길이 배열에 count+1존재하지 않으므로 count이것이 C 컴파일러가 길이가 0 인 배열을 함수 안팎의 변수로 정의하는 것을 금지하는 이유입니다. 당시 내용은 무엇 name입니까? 어떤 주소가 name정확히 저장됩니까?

경우 p포인터가 그 표현은 p[n]동일하다*(p + n)

올바른 표현 별표 * 수단에 의해 지시 된 메모리 액세스 포인터의 참조 해제 조작 인 경우 p + n, 그 주소에 저장되어있는 메모리 나 액세스 p + n, p + n포인터의 표현을 그것의 주소를 얻어 p,이 어드레스 번호를 추가 n곱 포인터 타입의 크기 p.

주소와 번호를 추가 할 수 있습니까?

예, 주소는 일반적으로 16 진수 표기법으로 표시되는 부호없는 정수이므로 가능합니다.


답변

메모리 주소에 대한 포인터를 원하면 선언하십시오. 배열은 실제로 예약 한 메모리 청크를 가리 킵니다. 함수에 전달 될 때 포인터가 포인터로 붕괴되지만, 가리키는 메모리가 힙에 있으면 아무런 문제가 없습니다. 크기가 0 인 배열을 선언 할 이유가 없습니다.


답변

원래의 C89 시대부터 C 표준이 정의되지 않은 동작을 가지고 있다고 명시했을 때의 의미는 “특정 대상 플랫폼에서 구현이 의도 한 목적에 가장 적합한 것을 수행하는 것은 무엇입니까?”라는 의미였습니다. 이 표준의 저자는 어떤 행동이 특정 목적에 가장 적합한 지 추측하려고하지 않았습니다. VLA 확장을 사용하는 기존 C89 구현은 크기가 0 일 때 다르지만 논리적으로 동작했을 수 있습니다 (예 : 일부는 배열을 주소 표현식으로 처리하여 NULL을 생성하는 반면 다른 일부는이를 주소와 동일한 주소 표현식으로 처리 할 수 ​​있음) 다른 임의의 변수이지만 트래핑없이 안전하게 0을 추가 할 수 있습니다). 코드가 그러한 다른 행동에 의존했을 수 있다면 표준 작성자는

표준 구현 자들은 어떤 구현이 무엇을하는지 추측하거나 어떤 행동이 다른 행동보다 우월한 것으로 간주되어야한다고 제안하기보다는 구현 자들이 그 사건을 가장 잘 처리 할 수 있도록 판단 할 수 있도록 허용 했다. 장면 뒤에서 malloc ()을 사용하는 구현은 배열 주소를 NULL로 처리 할 수 ​​있고 (크기-제로 malloc이 널을 생성하는 경우) 스택 주소 계산을 사용하는 구현은 다른 변수의 주소와 일치하는 포인터를 생성 할 수 있으며 일부 다른 구현에서는 다른 것들. 필자는 컴파일러 작성자가 제로 크기의 코너 케이스를 의도적으로 쓸모없는 방식으로 작동시키기를 기대하지는 않았다고 생각합니다.


답변