2014-09-09 1 views
1

코드가 있다고 가정하십시오.두 문자열 입력 방법이 모두 작동하는 이유는 무엇입니까?

1a.

char str1[50]; 
scanf("%s",&str1); 

이 작동합니다.

1b.

char str1[50]; 
scanf("%s",str1); 

이 또한 정상적으로 작동합니다.

그러나 다음 코드는 사용자가 입력 할 수 없습니다.

1c.

char str1[50]; 
scanf("%c",str1[0]); 

나는 &는 사용자로부터 입력을하고 주소를 저장하는 데 필요한 것을 알고 있지만 첫 번째 경우에 무슨 일입니까? 문자열에서 읽을 수

+3

유형에 관한 것만 큼 주소가 아닙니다. 'str1'은 기본적으로 포인터입니다. 'str1 [0]'은'char'입니다. – raina77ow

+1

어레이가 포인터로 감퇴하고 있습니다 (1b에서). 이러한 현상을 검색하는 데 도움이되는 몇 가지 키워드입니다. – Cornstalks

+1

[배열의 주소가 C의 값과 어떻게 다릅니 까?] (http://stackoverflow.com/questions/2528318/how-come-an-arrays-address-is-equal-to-its- value-in-c) – raina77ow

답변

3

올바른 방법은 다음 중 하나입니다 :

scanf("%s", &str1[0]); // Correct 
scanf("%s", str1);  // Also correct 

첫 번째 명시 적으로 정확 - 첫 번째 char의 주소를 전달하려는. 두 번째는 동등합니다. 함수에 배열을 전달하면 포인터가 자동으로 "쇠퇴"합니다. 컴파일러는 array과 같은 인수를 &array[0]으로 변환합니다. 첫 번째 문자의 주소 대신 첫 번째 문자를 전달

scanf("%c",str1[0]);  // Incorrect 

잘못된 밖으로 평평하다. 컴파일이 실패하거나 프로그램을 실행할 때 충돌이 발생할 가능성이 높습니다. 문자는 주소와 매우 다릅니다.

이러한 구분을 허용 할 수 있다면 잘못되었지만 "편평하지 않은"것은 아닙니다.

&str1을 전달하는 것은 일반적인 오류입니다. 그 이유는 실제로 미묘합니다. &str[0]은 문자열의 첫 번째 문자의 주소입니다. &str은 50 자 전체 배열의 주소입니다. 여기 미묘한 점은 실제로 이것들이 메모리에서 같은 주소가된다는 것입니다. 배열은 첫 번째 문자에서 시작됩니다.

그럼 왜 잘못 되었나요?

답변은 타이핑 중입니다. 문자 포인터 -

  • &str[0]의 유형은 char *입니다. %s을 사용할 때 예상되는 유형입니다.

  • &str의 유형은 char (*)[50] - 50 문자 배열을 가리키는 포인터입니다. 이것은 이 아니며과 같으며 char *과 같습니다. 자세한 내용은 How come an array's address is equal to its value in C?을 참조하십시오.

&str&str[0]과는 동일하지만, 종류가 다르다. 이 유형 불일치는 pedantically 말하기, 오류입니다. 정의되지 않은 동작이 발생합니다.

나는 당신이 생각하는 것을 안다. "좋아, 똑똑한 녀석 -이게 실수라면, 왜 효과가 있니?" 음, C는 재미있는 언어입니다. 그것은 정말로 신뢰하고 있습니다. 과도하게 신뢰하고 있습니다. 절벽에서 뛰어 내리라고 말하면 절벽에서 뛰어 내릴 것입니다. 그것은 당신이 절대적으로 정확하고 분별있는 일을하고 있음을 확인하기 위해 방해가되지 않습니다.

기술적으로 무언가를하도록 요청할 수있는 경우가 많습니다. 컴파일러는 정의되지 않은 동작을 진단하고 방지 할 필요가 없습니다. 효율성이라는 명목으로, 그들이 원하는 어떤 것도 할 수 있습니다 : 충돌, 또는 오류 메시지 인쇄 ... 또는 일반적으로 실제로 작동하는 것처럼 보입니다. 그 중 하나입니다. &str&str[0]의 값은 동일하며 scanf()은 varargs 함수이므로 모든 컴파일러가 형식 불일치를 catch하지는 않습니다.

사기는 다음과 같습니다. 컴파일러가 실수 할 때마다 알려주지 마십시오. 혼란 스럽거나 정의되지 않은 동작을 호출하는 경우 잠시 동안 벗어날 수 있습니다. 코드가 작동하는 것처럼 보일 수 있습니다. 하지만 시한 폭탄에 앉아있어. 어느 날 그것은 당신의 얼굴에서 날아 올 것입니다. 당신이 그런 char str1[50]으로 배열을 선언 할 때

+0

isnt & str은 50 문자 배열의 주소를 저장하는 변수의 주소입니까? –

+0

'str1'은 매우 배열입니다. 그것은 배열의 주소를 보유하지 않습니다 ** ** ** 배열입니다. 'str1'은 배열이고'& str'는 배열의 주소입니다. –

0

, 배열에 대한 메모리 스택에 할당 및처럼 보이는 : 이제

str1 
+----+----+----+----+----+----+----+ 
| 0 | 1 | ........ | 48 | 49 | 
+----+----+----+----+----+----+----+ 

당신이 배열의 요소를 가지고가는 경우에, 그들은 다음과 같습니다

str1[0] 
+----+----+----+----+----+----+----+ 
| 0 | 1 | ........ | 48 | 49 | 
+----+----+----+----+----+----+----+ 

    str1[1] 
+----+----+----+----+----+----+----+ 
| 0 | 1 | ........ | 48 | 49 | 
+----+----+----+----+----+----+----+ 

등 같은 배열의

는 그렇게 일어나는 str1의 주소 나 같은 주소로 str1[0].

Synantically, scanf를 사용하여 사용하는 올바른 방법은 다음과 같습니다

scanf("%s", str1); 

또는

scanf("%s", &str1[0]); 

&str1 이후

scanf("%s", &str1); 

작동하며 &str1[0]은 동일합니다.

동적으로 할당 된 배열을 사용하면 작동하지 않습니다.

str2 
+-----+ 
| AAA | 
+-----+ 
주소 AAA에서 메모리의 모습

:

+----+----+----+----+----+----+----+ 
| 0 | 1 | ........ | 48 | 49 | 
+----+----+----+----+----+----+----+ 

char* str2 = malloc(50); 

의이 malloc에 의해 반환되는 값이 str2의 메모리 레이아웃의 모습을 AAA

입니다 가정 해 봅시다

배열 요소는 AAA부터 시작합니다. 이 경우에 대해

str2[0] 
+----+----+----+----+----+----+----+ 
| 0 | 1 | ........ | 48 | 49 | 
+----+----+----+----+----+----+----+ 

    str2[1] 
+----+----+----+----+----+----+----+ 
| 0 | 1 | ........ | 48 | 49 | 
+----+----+----+----+----+----+----+ 

, &str2 &str2[0]는 동일하지 않다. 사용하는 경우 :

scanf("%s", &str2); 

정의되지 않은 동작이 발생합니다.

+0

첫 번째 그래프는 당신이'str1' **이 ** 포인터임을 제안하는 것처럼 내 눈으로 보입니다. 그렇지 않습니다. –

+0

@ JohnDibling, 다이어그램을 그릴 방법을 모르겠습니다. –

+0

개체 이름에서 해당 표현으로 포인터를 뺍니까? – Deduplicator

1

는 이제 모든 보자 하나씩 :

내가 &는 사용자로부터 입력을하고 주소를 저장하는 데 필요한 것을 알고 있지만 첫 번째 경우에 무슨 일이 일어나고 있는지?

사실, 그건 잘못되었습니다. 주소는 수정할 수 없기 때문에 주소에 물건을 저장할 수 없습니다.
당신이 말하고자하는 것은, C가 엄격하게 값으로 전달된다는 것입니다. 따라서 포인터를 전달해야하는 객체 (예 : &을 사용해야 함)를 함수가 수정하도록 할 수 있습니다.

이제, 코드를 살펴 보자 :

char str1[50]; 
scanf("%s",&str1); 

Undefined Behavior입니다 scanf는 문자 유형 (char*unsigned char*signed char*const restrict로 버전)에 대한 포인터를 기대하기 때문이다.
char(*)[50] 유형의 값을 전달하는 것은 좋지 않습니다. 아직도 일을하는 이유는 무엇입니까?
the address of an array and its first element are guaranteed to be identical이므로 구현시 데이터 포인터 유형이 하나 뿐이며 행운을 빕니다 (나중에 물어 봅니다).

대기중인 버퍼 오버플로를 무시하면 정상입니다. 항상 적절한 제한을 전달합니다

char str1[50]; 
scanf("%s",str1); 

은 다음과 같아야합니다

char str1[50]; 
scanf("%49s",str1); 

당신의 다음 예를 수행 *str1에 입력 스트림에서 하나의 다음 char를 읽고, 뭔가 완전히 다른 :

char str1[50]; 
scanf("%c",str1[0]); 

그것은 아마도 당신이 원하는 것이 아닙니다.

추가 참고 사항 : 할당 된 곳의 수를 확인하십시오. 반환 값은 scanf입니다.

0

제 의견으로는 C/C++에서는 배열 이름을 값이 아니라 참조로 취급하며 일부 C/C++ 컴파일러는 배열 이름에 대한 명시적인 참조 (예 : & str)를 처리합니다. , str과 동일합니다. 예를 들어 다음 코드를 사용하면 일부 32 비트 컴파일러 (Visual C/C++ 4.0)는 str1의 크기가 32이고 & str1 인 경우 32를 출력하지만 다른 비주얼 스튜디오 2005 이상에서는 sizer str1에 32를 출력합니다. sizeof & str1

#include <stdio.h> 
main() 
{ 
char str1[32]; 
    printf("%d %d\n", sizeof(str1), sizeof(&str1)); 
    return (0); 
} 
관련 문제