어레이의 경우 a[5] == 5[a]인 이유는 무엇입니까?
Joel이 Stack Overflow 팟캐스트 #34, C Programming Language(일명 K&R)에서 지적했듯이 C에서는 어레이의 다음 속성에 대해 언급하고 있습니다.a[5] == 5[a]
조엘이 포인터 산수 때문이라고 하는데 난 아직도 이해가 안 돼.왜?
C 규격에 의해 정의되는 것은[]연산자는 다음과 같습니다.
a[b] == *(a + b)
그러므로a[5]평가 대상:
*(a + 5)
그리고.5[a]평가 대상:
*(5 + a)
a는 배열의 첫 번째 요소에 대한 포인터입니다. a[5]5가지 요소에서 더 멀리 있는 값입니다.a(이것은, 와 같습니다.*(a + 5)초등학교 수학부터 우리는 그것들이 같다는 것을 안다.
다른 답변들이 뭔가 놓치고 있는 것 같아요.
네.p[i]정의상 와 동등하다*(p+i)(더하는 것은 치환적이기 때문에) 이 값은*(i+p)(다시 말씀드리지만) 의 정의에 의해[]연산자)는 다음과 같습니다.i[p].
(또한array[i]어레이명은 암묵적으로 어레이의 첫 번째 요소에 대한 포인터로 변환됩니다.)
그러나 이 경우 덧셈의 교환성은 그다지 명확하지 않습니다.
양쪽 오퍼랜드가 같은 타입이거나 공통 타입으로 승격되는 다른 수치 타입일 경우, 정류성은 지극히 타당합니다.x + y == y + x.
그러나 이 경우에는 포인터 산술에 대해 구체적으로 이야기하고 있습니다.한쪽 피연산자는 포인터이고 다른쪽 피연산자는 정수입니다.(정수+정수는 다른 연산이고 포인터+포인터는 넌센스입니다.)
C 규격의 설명+오퍼레이터(N1570 6.5.6)는 다음과 같이 말합니다.
또한, 두 피연산자 중 하나는 산술 유형을 가져야 하며, 한 피연산자는 완전한 객체 유형에 대한 포인터여야 하며, 다른 한 피연산자는 정수 유형을 가져야 한다.
이렇게 쉽게 말할 수 있었다.
또한, 두 피연산자 모두 산술 유형을 가져야 하며, 또는 왼쪽 피연산자는 완전한 객체 유형에 대한 포인터여야 하며, 오른쪽 피연산자는 정수 유형을 가져야 한다.
이 경우 둘 다i + p그리고.i[p]불법이 될 수도 있어요
C++로 표현하면 실제로는 2개의 오버로드 세트가 있습니다.+연산자, 대략 다음과 같이 설명할 수 있습니다.
pointer operator+(pointer p, integer i);
그리고.
pointer operator+(integer i, pointer p);
그 중 첫 번째 것만이 정말 필요한 거죠
그럼 왜 이런 식일까요?
C++는 이 정의를 B에서 따온 C에서 따온 것입니다(어레이 인덱싱의 교환성은 1972년 B에 대한 사용자 참조에서 명시적으로 언급되어 있습니다). BCPL에서 따온 것입니다(수동 1967년)는 훨씬 더 오래된 언어(CPL?Algol?)에서 따온 것입니다.
그래서 그 아이디어는 배열 인덱싱 덧셈의 정의한 것은 추가, 심지어는 포인터와 정수의, 가환, 다시 수십년, C의 조상 언어에 간다.
그것들은 훨씬 덜 강력하게보다 현대 C입력했다.특히, 포인터와 정수 사이의 차별은 종종.(초기 C프로그래머들이 가끔 부호 없는 정수로서, 그 전에 포인터를 사용했다 무시되었다.unsigned키워드는 언어에.)추가되었다.그래서기 때문에 피연산자 다른 종류의고 있어non-commutative한다는 발상은 아마도 그 언어들 중 디자이너들에게 일어나지 않았을 것이다.만약 사용자들"것들"이 정수, 포인터 또는 다른 무언가, 두"것들"을 추가하기 원해, 그것은 언어까지 그것을 막지 않았다.
그리고 몇년에 걸쳐, 그 규칙의 어떠한 변화 기존 코드( 하지만 1989년 ANSIC표준은 좋은 기회 있었을 것)이 부러졌을 거래.
변경하면 C또는 C++왼쪽에서 포인터를 놓고 오른쪽에 정수지만, 실제 나타난 표현력을 아무런 손해도 되지 않다 기존의 코드를 해독할 수 있도록 해야 한다.
그래서 지금 우리가 가지arr[3]그리고.3[arr]의미 같은 물건, 비록 후자 형태는 IOCCC 밖에서 나타나야 한다.
왜냐하면 배열 접속 포인터의 조건에 정의되어 있다. a[i]의미에 대한 자세한 내용은*(a + i), 교환 있다.
그리고 물론.
("ABCD"[2] == 2["ABCD"]) && (2["ABCD"] == 'C') && ("ABCD"[2] == 'C')
이의 주된 이유는 70년대 때 C설계되어 C컴파일러 많이 하지 않았어, 컴퓨터, 많은 메모리(최고 64KB에 많은)이 없었어 구문 검사에 있었다.따라서"X[Y]"차라리 맹목적으로로 번역되었고".*(X+Y)"
또, 「」의 설명도 있습니다.+=" 및 "++" 구문.폼의 모든 것"A = B + C"는 컴파일된 형식이 동일합니다.단, B가 A와 동일한 객체일 경우 어셈블리레벨 최적화를 사용할 수 있습니다.그러나 컴파일러는 이를 인식할 수 있을 만큼 밝지 않았기 때문에 개발자는 (를) 해야 했습니다.A += C)와 마찬가지로C이었다1, 다른 어셈블리 레벨의 최적화를 사용할 수 있었습니다.또한 개발자는 컴파일러가 인식하지 않았기 때문에 그것을 명확하게 해야 했습니다.(더 최근에는 컴파일러가 인식하고 있기 때문에 오늘날에는 이러한 구문은 거의 불필요합니다.)
이 보기 흉한 구문은 '유용하다'거나 같은 배열의 위치를 참조하는 인덱스의 배열을 다루고 싶을 때 적어도 매우 재미있다는 것을 알게 되었습니다.네스트된 대괄호를 대체하여 코드를 보다 읽기 쉽게 만들 수 있습니다!
int a[] = { 2 , 3 , 3 , 2 , 4 };
int s = sizeof a / sizeof *a; // s == 5
for(int i = 0 ; i < s ; ++i) {
cout << a[a[a[i]]] << endl;
// ... is equivalent to ...
cout << i[a][a][a] << endl; // but I prefer this one, it's easier to increase the level of indirection (without loop)
}
물론 실제 코드에 그런 사용 사례는 없다고 생각합니다만, 어쨌든 재미있었습니다.
아무도 디나의 문제에 대해 언급하지 않은 것 같다.sizeof:
포인터에는 정수만 추가할 수 있으며 두 포인터를 함께 추가할 수는 없습니다.이렇게 하면 포인터를 정수에 추가하거나 포인터에 정수를 추가할 때 컴파일러는 항상 고려해야 할 크기가 있는 비트를 알 수 있습니다.
Ted Jensen의 A Tutorial ON Pointers and Arrays IN C에 매우 잘 설명되어 있습니다.
Ted Jensen은 다음과 같이 설명했다.
사실, 이것은 사실입니다. 즉, 글을 쓰는 곳마다
a[i]로 대체할 수 있다.*(a + i)문제없이.실제로 컴파일러는 어느 경우든 동일한 코드를 만듭니다.따라서 포인터 산술은 배열 인덱싱과 동일합니다.어느 구문을 사용해도 같은 결과가 됩니다.포인터와 어레이가 같은 것이 아니라, 같은 것이 아닙니다.어레이의 특정 요소를 식별하기 위해서는 두 가지 구문 중 하나를 어레이 인덱싱을 사용하는 구문과 포인터 산술을 사용하는 구문 중에서 선택할 수 있습니다.이 구문들은 동일한 결과를 산출합니다.
자, 이 마지막 표현을 보면, 그 일부가..
(a + i)는 + 연산자를 사용한 단순한 덧셈이며, C의 규칙은 이러한 식이 가환적이라는 것을 나타냅니다.즉, (a + i)는(i + a)그래서 우리는 글을 쓸 수 있었다.*(i + a)마찬가지로 쉽게*(a + i).그렇지만*(i + a)에서 올 수도 있었다i[a]이 모든 것에서 다음과 같은 흥미로운 사실이 나옵니다.char a[20];쓰기
a[3] = 'x';글쓰기랑 똑같다
3[a] = 'x';
이제 약간의 역사도 남았습니다.다른 언어들 중에서 BCPL은 C의 초기 발달에 상당히 큰 영향을 끼쳤다.BCPL에서 다음과 같은 방법으로 어레이를 선언한 경우:
let V = vec 10
10단어가 아니라 11단어 메모리를 할당했습니다.일반적으로 V가 첫 번째이며 바로 다음 단어의 주소가 포함되어 있습니다.따라서 C와 달리 V 이름을 지정하면 해당 위치로 이동하여 어레이의 0번째 요소의 주소를 얻을 수 있습니다.따라서 BCPL에서의 어레이인다이렉션은 다음과 같이 표시됩니다.
let J = V!5
정말 해야 했다J = !(V + 5)(BCPL 구문 사용) 어레이의 기본 주소를 가져오려면 V을 가져와야 했기 때문입니다.따라서V!5그리고.5!V동의어였다.일례로 WAFL(Warwick Functional Language)은 BCPL로 작성되었으며, 내 기억으로는 데이터 스토리지로 사용되는 노드에 액세스하기 위해 WAFL(Warwick Functional Language)이 아닌 후자의 구문을 사용하는 경향이 있었다.35년에서 40년 전쯤의 일이라 기억은 좀 녹슬었어요:)
스토리지의 추가 단어를 사용하지 않고, 어레이의 이름을 지정할 때 컴파일러가 기본 주소를 삽입하도록 하는 혁신은 나중에 나타났습니다.C 이력서에 따르면 이 일은 구조물이 C에 추가될 무렵에 일어났다.
주의:!BCPL에서는 단항 프리픽스 연산자 및 바이너리 infix 연산자였습니다.두 경우 모두 간접 처리를 합니다.바이너리 형식에 2개의 오퍼랜드가 추가되어 있는 것만으로, 인다이렉션을 실행할 수 있습니다.BCPL(및 B)의 단어 지향적 특성을 고려할 때, 이는 실제로 매우 타당했습니다.데이터 타입을 취득했을 때, C에서는 「포인트와 정수」의 제한이 필요하게 되었습니다.sizeof물건으로 변했어요.
좋은 질문/답변입니다.
C 포인터와 배열은 동일하지 않다는 점을 지적하고 싶지만, 이 경우에는 차이가 꼭 필요한 것은 아닙니다.
다음 선언을 고려합니다.
int a[10];
int* p = a;
인a.out, 기호a배열의 선두에 있는 주소에 있습니다.기호가 표시됩니다.p는 포인터가 저장되어 있는 주소에 있으며, 그 메모리 위치에 있는 포인터의 값이 배열의 선두가 됩니다.
말 그대로 대답하다.라는 것은 항상 사실이 아니다x == x
double zero = 0.0;
double a[] = { 0,0,0,0,0, zero/zero}; // NaN
cout << (a[5] == 5[a] ? "true" : "false") << endl;
인쇄하다
false
정답은 아니고 생각할거리만 좀 주세요.클래스에 index/subscript 연산자가 오버로드된 경우 식0[x]동작하지 않습니다.
class Sub
{
public:
int operator [](size_t nIndex)
{
return 0;
}
};
int main()
{
Sub s;
s[0];
0[s]; // ERROR
}
int 클래스에 액세스 할 수 없기 때문에, 이것은 할 수 없습니다.
class int
{
int operator[](const Sub&);
};
c 컴파일러로
a[i]
i[a]
*(a+i)
배열 내의 요소를 참조하는 방법은 다양합니다. (전혀 이상하지 않습니다.)
왜냐하면 C 컴파일러는 항상 배열 표기법을 포인터 표기법으로 변환하기 때문입니다. a[5] = *(a + 5)또한.5[a] = *(5 + a) = *(a + 5)그러니까 둘 다 똑같아요
C 어레이에서는arr[3]그리고.3[arr]동일하며 이에 대응하는 포인터 표기는 다음과 같습니다.*(arr + 3)로.*(3 + arr)하지만 그 반대입니다.[arr]3또는[3]arr올바르지 않고 구문 오류가 발생합니다.(arr + 3)*그리고.(3 + arr)*올바른 표현이 아닙니다.그 이유는 decreference 연산자를 주소 뒤에 배치하지 않고 식에 의해 산출된 주소 앞에 배치해야 하기 때문입니다.
주식회사
int a[]={10,20,30,40,50};
int *p=a;
printf("%d\n",*p++);//output will be 10
printf("%d\n",*a++);//will give an error
포인터p는 "syslog", 어레이 이름입니다.a'니모닉' 또는 '니모닉'이기 때문에p++유효하지만a++무효입니다.
a[2]와 같다2[a]왜냐하면 이 두 가지에 대한 내부 연산은 내부적으로 다음과 같이 계산되는 "포인터 산술"이기 때문입니다.*(a+2)동등.*(2+a)
C의 포인터는
a[5] == *(a + 5)
그리고 또
5[a] == *(5 + a)
그렇기 때문에 사실이다a[5] == 5[a].
질문에 답한 건 알지만, 이 설명을 공유하지 않을 수 없었다.
컴파일러 설계의 원리를 기억합니다.a는 입니다.int배열과 크기int2바이트, 베이스 주소a1000 입니다.
어떻게.a[5]동작합니다 ->
Base Address of your Array a + (5*size of(data type for array a))
i.e. 1000 + (5*2) = 1010
그렇게,
마찬가지로 C 코드가 3주소 코드로 분할되면5[a]->가 됩니다.
Base Address of your Array a + (size of(data type for array a)*5)
i.e. 1000 + (2*5) = 1010
따라서 기본적으로 두 문장은 메모리 내의 동일한 위치를 가리키고 있습니다.a[5] = 5[a].
이 설명은 배열의 음의 인덱스가 C에서 작동하는 이유이기도 합니다.
즉, 접속할 수 있는 경우a[-5]그것은 나에게 줄 것이다
Base Address of your Array a + (-5 * size of(data type for array a))
i.e. 1000 + (-5*2) = 990
990번지에 있는 물체를 돌려줄 겁니다
둥지를 틀지 않게 하는 데 유용하기 때문입니다.
다음 내용을 읽어 보시겠습니까?
array[array[head].next].prev
또는 다음과 같습니다.
head[array].next[array].prev
덧붙여서 C++는 함수 호출에 대해 유사한 교환 속성을 가집니다.쓰는 것보다g(f(x))C에서 반드시 해야 하는 것처럼, 멤버 함수를 사용하여 작성할 수 있습니다.x.f().g()f와 g를 lookup table로 대체하면 쓸 수 있습니다.g[f[x]](기능 스타일) 또는(x[f])[g](oop 스타일).후자는 인덱스를 포함하는 구조에서 매우 적합합니다.x[xs].y[ys].z[zs]. 보다 일반적인 표기법 사용:zs[ys[xs[x].y].z].
이것은 언어 지원이 있기 때문에 가능한 기능입니다.
컴파일러가 통역합니다.a[i]~하듯이*(a+i)그리고 그 표현은5[a]까지 평가하다.*(5+a)덧셈은 가환성이기 때문에 둘 다 같은 것으로 나타났습니다.따라서 이 표현은 다음과 같이 평가됩니다.true.
언급URL : https://stackoverflow.com/questions/381542/with-arrays-why-is-it-the-case-that-a5-5a
'programing' 카테고리의 다른 글
| 는 URL은 주어진 문자열이 포함되 어떻게 확인해야 하죠? (0) | 2022.09.21 |
|---|---|
| Python에서 현재 모듈 내의 모든 클래스 목록을 얻으려면 어떻게 해야 합니까? (0) | 2022.09.21 |
| 문자열에서 영숫자 문자만 반환하는 함수입니까? (0) | 2022.09.21 |
| ImportError: 'Tkinter'라는 이름의 모듈이 없습니다. (0) | 2022.09.18 |
| Android용 줄 바꿈 위젯 레이아웃 (0) | 2022.09.18 |