Programming

이 코드는 sizeof ()를 사용하지 않고 어떻게 배열 크기를 결정합니까?

procodes 2020. 7. 2. 08:10
반응형

이 코드는 sizeof ()를 사용하지 않고 어떻게 배열 크기를 결정합니까?


C 인터뷰 질문을 통해 다음과 같은 해결책으로 "sizeof 연산자를 사용하지 않고 C에서 배열의 크기를 찾는 방법은 무엇입니까?"라는 질문을 발견했습니다. 작동하지만 이유를 이해할 수 없습니다.

#include <stdio.h>

int main() {
    int a[] = {100, 200, 300, 400, 500};
    int size = 0;

    size = *(&a + 1) - a;
    printf("%d\n", size);

    return 0;
}

예상대로 5를 반환합니다.

편집 : 사람들은 답변을 지적 했지만 구문은 약간 다릅니다. 즉 인덱싱 방법

size = (&arr)[1] - arr;

두 질문이 모두 유효하며 문제에 대한 접근 방식이 약간 다릅니다. 엄청난 도움과 철저한 설명에 감사드립니다!


포인터에 1을 추가하면 결과는 지정된 유형 (즉, 배열)의 객체 시퀀스에서 다음 객체의 위치입니다. 객체를 p가리키는 경우 시퀀스에서 int다음 p + 1을 가리 킵니다 int. 경우 p의 5 요소의 배열에 점 int(이 경우,식이 &a), 다음 p + 1다음를 가리 5 요소의 배열int 순서이다.

두 포인터를 빼면 (두 포인터가 모두 같은 배열 객체를 가리 키거나 하나가 배열의 마지막 요소를 가리키고있는 경우) 두 포인터 사이의 객체 수 (배열 요소)가 생성됩니다.

이 표현식 &a은의 주소를 산출하고 a유형 int (*)[5](의 5- 요소 배열에 대한 포인터)을 갖습니다 int. 발현은 &a + 1차기 5 소자 어레이의 어드레스 수득 int다음 a과 같은 형식을 갖는다 int (*)[5]. 이 표현식 *(&a + 1)은의 결과를 역 참조하여 마지막 요소 다음에 오는 &a + 1첫 번째 주소를 생성하고 type을 가지며 ,이 문맥에서 type의 표현식으로 "부패"합니다 .intaint [5]int *

마찬가지로, 표현식 a은 배열의 첫 번째 요소에 대한 포인터로 "부패"하고 유형이 int *있습니다.

사진이 도움이 될 수 있습니다.

int [5]  int (*)[5]     int      int *

+---+                   +---+
|   | <- &a             |   | <- a
| - |                   +---+
|   |                   |   | <- a + 1
| - |                   +---+
|   |                   |   |
| - |                   +---+
|   |                   |   |
| - |                   +---+
|   |                   |   |
+---+                   +---+
|   | <- &a + 1         |   | <- *(&a + 1)
| - |                   +---+
|   |                   |   |
| - |                   +---+
|   |                   |   |
| - |                   +---+
|   |                   |   |
| - |                   +---+
|   |                   |   |
+---+                   +---+

이것은 동일한 스토리지에 대한 두 가지보기입니다. 왼쪽은 5 요소 배열의 int시퀀스로보고 있고 오른쪽은 int. 또한 다양한 표현과 유형을 보여줍니다.

식,주의 *(&a + 1)의 결과 정의되지 않은 동작 :

...
결과가 배열 객체의 마지막 요소를 지난 지점을 가리키는 경우 평가되는 단항 * 연산자의 피연산자로 사용되지 않습니다.

C 2011 온라인 초안 , 6.5.6 / 9


이 라인은 가장 중요합니다 :

size = *(&a + 1) - a;

보시다시피, 먼저 주소를 가져와 주소를 a추가합니다. 그런 다음 포인터를 역 참조하고 원래 값을 뺍니다 a.

Pointer arithmetic in C causes this to return the number of elements in the array, or 5. Adding one and &a is a pointer to the next array of 5 ints after a. After that, this code dereferences the resulting pointer and subtracts a (an array type that has decayed to a pointer) from that, giving the number of elements in the array.

Details on how pointer arithmetic works:

Say you have a pointer xyz that points to an int type and contains the value (int *)160. When you subtract any number from xyz, C specifies that the actual amount subtracted from xyz is that number times the size of the type that it points to. For example, if you subtracted 5 from xyz, the value of xyz resulting would be xyz - (sizeof(*xyz) * 5) if pointer arithmetic didn't apply.

As a is an array of 5 int types, the resulting value will be 5. However, this will not work with a pointer, only with an array. If you try this with a pointer, the result will always be 1.

Here's a little example that shows the addresses and how this is undefined. The the left-hand side shows the addresses:

a + 0 | [a[0]] | &a points to this
a + 1 | [a[1]]
a + 2 | [a[2]]
a + 3 | [a[3]]
a + 4 | [a[4]] | end of array
a + 5 | [a[5]] | &a+1 points to this; accessing past array when dereferenced

This means that the code is subtracting a from &a[5] (or a+5), giving 5.

Note that this is undefined behavior, and should not be used under any circumstances. Do not expect the behavior of this to be consistent across all platforms, and do not use it in production programs.


Hmm, I suspect this is something that would not have worked back in the early days of C. It is clever though.

Taking the steps one at a time:

  • &a gets a pointer to an object of type int[5]
  • +1 gets the next such object assuming there is an array of those
  • * effectively converts that address into type pointer to int
  • -a subtracts the two int pointers, returning the count of int instances between them.

I'm not sure it is completely legal (in this I mean language-lawyer legal - not will it work in practice), given some of the type operations going on. For example you are only "allowed" to subtract two pointers when they point to elements in the same array. *(&a+1) was synthesised by accessing another array, albeit a parent array, so is not actually a pointer into the same array as a. Also, while you are allowed to synthesise a pointer past the last element of an array, and you can treat any object as an array of 1 element, the operation of dereferencing (*) is not "allowed" on this synthesised pointer, even though it has no behaviour in this case!

I suspect that in the early days of C (K&R syntax, anyone?), an array decayed into a pointer much more quickly, so the *(&a+1) might only return the address of the next pointer of type int**. The more rigorous definitions of modern C++ definitely allow the pointer to array type to exist and know the array size, and probably the C standards have followed suit. All C function code only takes pointers as arguments, so the technical visible difference is minimal. But I am only guessing here.

This sort of detailed legality question usually applies to a C interpreter, or a lint type tool, rather than the compiled code. An interpretter might implement a 2D array as an array of pointers to arrays, because there is one less runtime feature to implement, in which case dereferencing the +1 would be fatal, and even if it worked would give the wrong answer.

Another possible weakness may be that the C compiler might align the outer array. Imagine if this was an array of 5 chars (char arr[5]), when the program performs &a+1 it is invoking "array of array" behaviour. The compiler might decide that an array of array of 5 chars (char arr[][5]) is actually generated as an array of array of 8 chars (char arr[][8]), so that the outer array aligns nicely. The code we are discussing would now report the array size as 8, not 5. I'm not saying a particular compiler would definitely do this, but it might.

참고URL : https://stackoverflow.com/questions/56154380/how-does-this-piece-of-code-determine-array-size-without-using-sizeof

반응형