배열을 C의 함수에 인수로 전달


89

배열을 인수로 포함하는 함수를 작성하고 다음과 같이 배열의 값을 전달하여 호출합니다.

내가 찾은 것은 arraytest()값을 전달하여 함수를 호출하고 있지만 원본 int arr[]이 변경 된다는 것 입니다.

이유를 설명해 주시겠습니까?


1
당신은 참조로 배열을 전달하고 있지만 그 내용을 수정 - 따라서 왜 데이터의 변화를보고있다
숀 와일드

main()반환해야합니다 int.
underscore_d

답변:


141

배열을 매개 변수로 전달할 때

정확히 같은 의미

그래서 당신 main의 값을 수정하고 있습니다.

역사적 이유로 배열은 일류 시민이 아니며 값으로 전달할 수 없습니다.


3
어떤 상황에서 어떤 표기법이 더 낫습니까?
Ramon Martinez

22
@Ramon-두 번째 옵션을 사용합니다. 덜 혼란스럽고 배열의 사본을 얻지 못한다는 것을 더 잘 나타냅니다.
Bo Persson 2015 년

2
"역사적 이유"를 설명 할 수 있습니까? 값을 전달하려면 복사본이 필요하므로 메모리 낭비가 필요하다고 생각합니다. 감사합니다
Jacquelyn.Marquardt

5
@lucapozzobon-원래 C는 단일 값을 제외하고는 값에 의한 전달이 없었습니다. 이것이 struct언어에 추가 되기 전까지 는 이것이 변경 되지 않았습니다 . 그리고 어레이에 대한 규칙을 변경하기에는 너무 늦은 것으로 간주되었습니다. 이미 10 명의 사용자가있었습니다. :-)
Bo Persson

1
... void arraytest(int a[1000])등등 과 정확히 같은 의미 입니다. 여기에 확장 된 답변 : stackoverflow.com/a/51527502/4561887 .
Gabriel Staples

9

당신이 할 경우 함수에 인수로 1 차원 배열을 통과 각각 때문에, 당신은 세 가지 방법으로 모두 세 가지 선언 방법을 다음 중 하나의 형식 매개 변수를 선언 비슷한 결과를 생산해야 정수 포인터가가는 것을 컴파일러를 알려줍니다 받을 수 있습니다 .

따라서 원래 값을 수정하고 있습니다.

감사 !!!


두 번째 예를 찾고 있었는데 각 방법의 장점이 무엇인지 자세히 설명해 주시겠습니까?

8

배열을 복사본으로 전달하지 않습니다. 배열의 첫 번째 요소가 메모리에있는 주소를 가리키는 포인터 일뿐입니다.


7

배열의 첫 번째 요소 주소를 전달합니다.


7

C의 배열은 대부분의 경우 배열 자체의 첫 번째 요소에 대한 포인터로 변환됩니다. 그리고 더 자세하게 함수에 전달 된 배열은 항상 포인터로 변환됩니다.

에서 인용 한 이곳까지 K & R2nd를 :

배열 이름이 함수에 전달 될 때 전달되는 것은 초기 요소의 위치입니다. 호출 된 함수 내에서이 인수는 지역 변수이므로 배열 이름 매개 변수는 포인터, 즉 주소를 포함하는 변수입니다.

쓰기:

쓰기와 같은 의미입니다.

따라서 명시 적으로 작성하지 않더라도 포인터를 전달하므로 기본 값을 수정하는 것입니다.

자세한 내용은 정말 읽기 제안 .

또한 여기 에서 다른 답변을 찾을 수 있습니다.


6

배열의 첫 번째 멤버의 메모리 위치 값을 전달합니다.

따라서 함수 내에서 배열 수정을 시작하면 원래 배열이 수정됩니다.

그 기억 a[1]이다 *(a+1).


1
나는 *는 + 1이되어야한다 *에 대한 (A + 1) 실종) (가있는 가정
ShinTakezou

내가 C. 연주 한 이후하면서 감사를 @Shin하는되었습니다
알렉스

6

C에서 몇 가지 특별한 경우를 제외하고 배열 참조는 항상 배열의 첫 번째 요소에 대한 포인터를 "감쇠"합니다. 따라서 "값으로"배열을 전달할 수 없습니다. 함수 호출의 배열은 포인터로 함수에 전달되며 이는 참조로 배열을 전달하는 것과 유사합니다.

편집 : 배열이 첫 번째 요소에 대한 포인터로 붕괴되지 않는 세 가지 특별한 경우가 있습니다.

  1. sizeof a과 같지 않습니다 sizeof (&a[0]).
  2. &a&(&a[0])(와 &a[0]) 동일하지 않습니다 (과 동일하지도 않음 ).
  3. char b[] = "foo"과 같지 않습니다 char b[] = &("foo").

함수에 배열을 전달하면. 예를 들어 배열을 int a[10]만들고 각 요소에 임의의 값을 할당 했다고 가정 해 보겠습니다 . 지금 내가 사용하는 기능이 배열을 전달하는 경우 int y[]int y[10]또는 int *y.And 그 기능 I 사용에 sizeof(y)응답 바이트 포인터 될 것입니다 할당되었습니다. 따라서이 경우 포인터로 붕괴됩니다. 이도 포함하면 도움이 될 것입니다. 이 참조 postimg.org/image/prhleuezd
SURAJ 자이나교

sizeof원래 우리가 정의한 배열의 함수에서 작동을 사용하면 배열로 붕괴되지만 다른 함수를 전달하면 sizeof연산자를 사용 하여 포인터로 감쇠합니다.
Suraj Jain


나는 이것이 오래된 것을 압니다. 누군가가 이것을 본다면 두 가지 질문 :) 1. @ThomSmith는 그것이 배열 일 때 &a와 완전히 같지 않다고 썼습니다 . 어떻게? 내 테스트 프로그램에서 배열이 선언 된 함수와 다른 함수로 전달 될 때 둘 다 동일하게 표시됩니다. 2. 작가는 " 는 " 와 같지 않다 "고 씁니다 . 나에게 후자는 컴파일조차하지 않는다. 나 뿐인가요? &a[0]achar b[] = "foo"char b[] = &("foo")
Aviv Cohn

6

다차원 배열을 함수에 인수로 전달합니다. 하나의 희미한 배열을 인수로 전달하는 것은 다소 사소합니다. 2 차원 배열을 전달하는 더 흥미로운 경우를 살펴 보겠습니다. C에서는 int **2 차원 배열 대신 포인터 구문 ( )에 대한 포인터를 사용할 수 없습니다 . 예를 들어 보겠습니다.

여기에서는 5 개의 정수 배열에 대한 포인터를 첫 번째 인수로 취하는 함수를 지정했습니다. 열이 5 개인 2 차원 배열을 인수로 전달할 수 있습니다.

2 차원 배열을 허용하고 다음과 같이 함수 서명을 변경할 수있는보다 일반적인 함수를 정의하는 아이디어를 얻을 수 있습니다.

이 코드는 컴파일되지만 첫 번째 함수에서와 같은 방식으로 값을 할당하려고하면 런타임 오류가 발생합니다. 따라서 C에서 다차원 배열은 포인터에 대한 포인터 ... 포인터에 대한 포인터와 동일하지 않습니다. An int(*arr)[5]은 5 개 요소의 배열에 대한 포인터이고 an int(*arr)[6] 은 6 개 요소의 배열에 대한 포인터이며 서로 다른 유형에 대한 포인터입니다!

음, 더 높은 차원의 함수 인수를 정의하는 방법은 무엇입니까? 간단합니다. 우리는 그저 패턴을 따릅니다! 다음은 3 차원 배열을 사용하도록 조정 된 동일한 함수입니다.

예상대로 두 번째 차원에는 4 개의 요소가 있고 세 번째 차원에는 5 개의 요소가있는 3 차원 배열을 인수로 사용할 수 있습니다. 이와 같은 것은 괜찮습니다.

하지만 첫 번째 크기까지 모든 크기를 지정해야합니다.


6

배열에서 ptr로 자연 형 붕괴가있는 C의 표준 배열 사용

@Bo Persson은 여기 에 그의 위대한 대답에서 올바르게 설명합니다 .

배열을 매개 변수로 전달할 때

정확히 같은 의미

그러나 위의 두 가지 형식도 추가하겠습니다.

  1. 정확히 같은 의미

  2. 정확히 같은 의미

  3. 정확히 같은 의미

  4. 정확히 같은 의미

  5. 기타

에 대한 입력 매개 변수 유형의 붕괴, 위의 배열 예제의 모든 하나 하나에서int * , 심지어 빌드 옵션없이 경고 및 오류없이, 호출 할 수 있습니다 -Wall -Wextra -Werror켜져 (참조 여기 내 REPO를 같이,이 3 빌드 옵션에 대한 자세한 내용은) 이:

사실, "크기"값 ( [0], [1], [2], [1000]배열 매개 변수의 내부 등) 여기에 미적 / 자기 문서의 목적을 위해 분명히 단지이며, 양의 정수 할 수있다 ( size_t당신이 원하는 내가 생각 입력)!

그러나 실제로는 함수가받을 것으로 예상되는 배열의 최소 크기를 지정하는 데 사용해야합니다. 그래야 코드를 작성할 때 쉽게 추적하고 확인할 수 있습니다. MISRA-C-2012 표준 ( 구매 / 여기 £ 15.00를위한 표준의 236-PG 2012 버전의 PDF를 다운로드 지금까지 상태 (강조 추가)에게로 간다)

규칙 17.5 배열 유형을 갖는 것으로 선언 된 매개 변수에 해당하는 함수 인수는 적절한 수의 요소를 가져야합니다.

...

매개 변수가 지정된 크기의 배열로 선언 된 경우 각 함수 호출의 해당 인수는 최소한 배열만큼 많은 요소가있는 객체를 가리켜 야합니다.

...

함수 매개 변수에 배열 선언자를 사용하면 포인터를 사용하는 것보다 더 명확하게 함수 인터페이스를 지정합니다. 함수에서 예상하는 최소 요소 수는 명시 적으로 명시되어 있지만 포인터로는 불가능합니다.

즉, C 표준이 기술적으로 적용하지 않더라도 명시 적 크기 형식을 사용하는 것이 좋습니다 . 적어도 개발자는 코드를 사용하는 다른 사용자에게 함수가 예상하는 크기 배열을 명확히하는 데 도움이됩니다. 통과해야합니다.


C의 배열에 대한 형식 안전성 강제

@Winger Sendon이 내 대답 아래의 주석에서 지적했듯이 C가 배열 크기 에 따라 배열 유형 을 다르게 처리하도록 할 수 있습니다 !

첫째, 당신은을 사용하여, 단지 내 위의 예제에서 인식해야합니다 int array1[2];같이 : arraytest(array1);원인을 array1자동으로 붕괴에로 int *. 그러나이 당신이 가지고가는 경우 의 주소 array1 대신과 전화를 arraytest(&array1), 당신은 완전히 다른 행동을 얻을! 이제는 int *! 대신, 유형은 "int 의 크기 2 배열에 대한 포인터" 또는 "int 유형의 크기 2 배열에 대한 포인터 " 를 의미하는 &array1is 입니다. 따라서 다음과 같이 FORCE C가 배열의 유형 안전성을 확인할 수 있습니다.int (*)[2]

이 구문은 읽기 어렵지만 함수 포인터 의 구문과 유사합니다 . 온라인 도구, cdecl 규칙은 , 우리에게 그 이야기 int (*a)[2]수단을 : "INT의 배열이 포인터로 선언" (2 명의 배열에 대한 포인터를 int들). : 괄호가없는 버전과 혼동하지 마십시오 int * a[2]수단 : "int와 포인터의 배열이 같은 선언" (이 배열 포인터int).

이제이 함수 &는 올바른 크기의 배열에 대한 포인터를 입력 매개 변수로 사용하여 다음과 같이 주소 연산자 ( ) 로 호출해야합니다 .

그러나 이것은 경고를 생성합니다.

여기에서이 코드를 테스트 할 수 있습니다 .

C 컴파일러가이 경고를 오류로 강제로 전환 arraytest(&array1);하여 항상 올바른 크기 유형 ( int array1[2];이 경우) 의 입력 배열 만 사용하여 호출 -Werror하도록하려면 빌드 옵션에 추가 하십시오. onlinegdb.com에서 위의 테스트 코드를 실행하는 경우 오른쪽 상단의 톱니 바퀴 아이콘을 클릭하고 "Extra Compiler Flags"를 클릭하여이 옵션을 입력합니다. 이제이 경고는 다음과 같습니다.

이 빌드 오류로 바뀝니다.


다음과 같이 주어진 크기의 배열에 대한 "유형 안전"포인터를 만들 수도 있습니다.

...하지만 필자는 이것을 반드시 추천 하지는 않습니다. 언어 구문 복잡성, 장황함, 코드 설계의 어려움이라는 예외적으로 높은 비용으로 모든 곳에서 유형 안전성을 강요하는 데 사용되는 많은 C ++ 익살스러운 행동을 상기시키기 때문입니다. 이전에 여러 번 외쳤습니다 (예 : "C ++에 대한 내 생각"참조 ).


추가 테스트 및 실험은 바로 아래 링크를 참조하십시오.

참고 문헌

위의 링크를 참조하십시오. 또한:

  1. 내 코드 실험 온라인 : https://onlinegdb.com/B1RsrBDFD

2
void arraytest(int (*a)[1000])크기가 잘못되면 컴파일러에서 오류가 발생하기 때문에 더 좋습니다.
wingerse

@WingerSendon, 나는 내가 여기에 확인하는 데 필요한 몇 가지 미묘한가 알고 있었다, 나는 내 시간을 보냈다 마지막이라는 큰 새 섹션 내 대답을 업데이 트했습니다 그래서 그 구문은, (이다가 혼란 함수 PTR 구문처럼) 혼란 Forcing type safety on arrays in C, 취재하여 포인트.
Gabriel Staples

@GabrielStaples, 감사합니다. 귀하의 답변은 매우 유용합니다. 이런 식으로 고급 c를 배우기위한 참고 자료를 참조 할 수 있습니까?
daryooosh

@daryooosh, 불행히도 할 수 없습니다. 나는 훌륭한 참고 문헌이 없습니다. 나는 수년에 걸쳐 깊이 파고 들어서 조금 여기, 조금 거기에서 이것을 집어 들었습니다. 제가 할 수있는 최선의 방법은 제가 이처럼 배운 내용 중 일부를 여기 eRCaGuy_hello_world 리포지토리에 가끔 넣는 것입니다. 위에서 사용한 C 유형 안전 장치는 매우 드물게 사용해야한다는 것을 명심하십시오. 코드를 복잡하게 만들고 가독성을 크게 떨어 뜨리며 그만한 가치가 없습니다. 가능한 경우 간단한 구문에 초점을 맞추고 내용을 읽기 쉽게 만듭니다.
Gabriel Staples

표준 클래식 C 교과서는이 K & R The C Programming Language 책인 en.wikipedia.org/wiki/The_C_Programming_Language 입니다.
Gabriel Staples

0

a[]또는 사용하는 경우 배열은 항상 참조로 전달됩니다 *a.


나는 이것을 찬성하고있다. 왜 반대표를 받았는지 모르겠습니다.
Gabriel Staples
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.