답변:
그것들은 정확히 동등합니다. 그러나
int *myVariable, myVariable2;
myVariable의 유형은 int * 이지만 myVariable2의 유형은 int 입니다. 에
int* myVariable, myVariable2;
그것은 모두가 형의 것을 분명 보일 수 INT의 * 하지만, 같은 그것은 정확하지 않은 myVariable2
타입이 INT를 .
따라서 첫 번째 프로그래밍 스타일이 더 직관적입니다.
int* someVar
개인 프로젝트를 고수하고 있습니다. 더 의미가 있습니다.
int[10] x
. 이것은 단순히 C의 구문이 아닙니다. 문법은 명시 적으로로 해석 int (*x)
되지 않고로 해석 (int *) x
되므로 왼쪽에 별표를 표시하는 것은 오해의 소지가 있으며 C 선언 구문의 오해에 기초합니다.
int* myVariable; int myVariable2;
대신에 혼란이 없습니다 .
다른 방법으로 살펴보면 *myVariable
유형이 int
있습니다.
myVariable
NULL 일 수 있으며이 경우 *myVariable
세그먼트 오류가 발생하지만 NULL 유형은 없습니다.
int x = 5; int *pointer = &x;
int 자체가 *pointer
아닌 어떤 값으로 설정했음을 제안하기 때문에 이러한 맥락에서 오해의 소지 가 pointer
있습니다.
해당 행의 *는 유형보다 변수에 더 가깝게 바인딩되므로
int* varA, varB; // This is misleading
@Lundin이 아래에서 지적한 것처럼 const는 더 많은 미묘함을 추가합니다. 한 줄에 하나의 변수를 선언하여이를 완전히 회피 할 수 있습니다.
int* varA;
int varB;
명확한 코드와 간결한 코드 사이의 균형은 깨지기 어렵습니다. 12 줄의 중복 된 줄도 int a;
좋지 않습니다. 여전히, 나는 한 줄에 하나의 선언을 기본으로하고 나중에 코드를 결합하는 것에 대해 걱정합니다.
int *const a, b;
. * "바인드"는 어디에 있습니까? 의 유형 a
되고 int* const
, 그래서 당신은 어떻게 *가이 유형 자체의 일부인 변수에 속한다고 말할 수 있습니까?
지금까지 아무도 언급하지 않은 것은이 별표가 실제로 C에서 " 역 참조 연산자 "라는 것입니다.
*a = 10;
선 위의 I 할당 할 것을 의미하지 않는다 10
에 a
, 내가 할당 할 뜻 10
대로 메모리 위치를 a
가리 킵니다. 그리고 나는 글을 쓰는 사람을 본 적이 없다
* a = 10;
당신은? 따라서 역 참조 연산자 는 항상 공백없이 작성됩니다. 이것은 아마 여러 줄에 걸쳐 곱셈과 구별되는 것일 것입니다.
x = a * b * c * d
* e * f * g;
여기 *e
그것은하지 않을 오해의 소지가 될 것이다?
이제 다음 줄은 실제로 무엇을 의미합니까?
int *a;
대부분의 사람들은 다음과 같이 말합니다.
이는 값에 a
대한 포인터 임을 의미합니다 int
.
이것은 기술적으로 정확하며 대부분의 사람들은 그것을 그렇게 읽거나 읽는 것을 좋아하며 이것이 현대 C 표준이 그것을 정의하는 방식입니다 (언어 C 자체는 모든 ANSI 및 ISO 표준보다 우선합니다). 그러나 그것을 보는 유일한 방법은 아닙니다. 이 줄을 다음과 같이 읽을 수도 있습니다.
의 역 참조 된 값은 a
유형 int
입니다.
따라서 실제로이 선언에서 별표는 역 참조 연산자로 표시 될 수 있으며이 연산자는 배치를 설명합니다. 그리고 그것은 a
포인터가 실제로 선언되지 않았으며, 실제로 당신이 실제로 역 참조 할 수있는 유일한 것은 포인터라는 사실에 의해 암시 적입니다.
C 표준은 *
운영자 에게 두 가지 의미 만 정의합니다 .
그리고 간접는 단지 하나의 의미는 별도의 의미는 포인터를 선언하는가이다, 참조 취소 조작이하는 일,이 같은 문 이내에 또한, 간접적 인 접근을 수행 다만 간접,이 int *a;
이 간접적 인 액세스 ( *
수단 간접 액세스) 따라서 위의 두 번째 진술은 첫 번째 진술보다 표준에 훨씬 가깝습니다.
int a, *b, (*c)();
"다음 객체를 int
객체 a
, 객체가 가리키는 b
객체 및 함수가 가리키는 객체를 다음과 같이 선언하십시오"와 같은 것을 읽습니다 c
.
*
에서이 int *a;
오퍼레이터되지 않고, 비 참조되지 않고 a
(아직 정의되지 않은)
*
(그것은하지로, 전체 표현 만 의미를 부여 *
식 내!). 그것은 "int a;"*
a
라고 말합니다 . 는 포인터를 선언하지만, 다른 식으로 주장하지는 않았지만에 주어진 의미가없는 경우 *를 참조하지 않은 값은 int 로 읽는 것은 동일한 사실적 의미를 지니고 있기 때문에 여전히 유효합니다. 6.7.6.1로 작성된 내용은 그 진술과 모순되지 않습니다.
int *a;
식이 아닌 선언입니다. a
에 의해 참조 해제되지 않습니다 int *a;
. 처리되는 a
시점에는 아직 존재하지 않습니다 *
. int *a = NULL;
널 포인터를 역 참조하기 때문에 버그 라고 생각하십니까 ?
여기에서 사지로 나가서 변수 선언과 매개 변수 및 반환 유형 모두에 대해이 질문에 대한 정답이 있다고 말합니다. 즉 별표가 이름 옆에 있어야합니다 int *myVariable;
. 이유를 이해하려면 C에서 다른 유형의 기호를 선언하는 방법을 살펴보십시오.
int my_function(int arg);
기능을 위해;
float my_array[3]
배열.
선언은 use 다음에 나오는 일반적인 패턴으로, 심볼의 유형은 이름 앞 부분과 이름 주위 부분으로 나뉘며 이름 주위 의 이러한 부분은 왼쪽에있는 유형의 값 :
int a_return_value = my_function(729);
float an_element = my_array[2];
그리고 : int copy_of_value = *myVariable;
.
참조를 사용하는 지점의 구문이 값 유형의 구문과 동일하므로 C ++은 참조 작업에 스패너를 던지므로 C ++이 C에 대해 다른 접근 방식을 취한다고 주장 할 수 있습니다. 반면에 C ++은 동일하게 유지됩니다. 포인터의 경우 C의 동작이므로 참조는 실제로이 점에서 이상한 것입니다.
그것은 단지 선호의 문제입니다.
코드를 읽을 때 두 번째 경우 변수와 포인터를 구분하는 것이 더 쉽지만, 공통 유형의 변수와 포인터를 한 줄에 넣을 때 혼동을 일으킬 수 있습니다. 가독성이 떨어지기 때문에).
유형 이름 옆에 해당 기호로 포인터를 선언하는 것을 선호합니다.
int* pMyPointer;
한 위대한 전문가는 "컴파일러의 방식을 읽으십시오."라고 말했습니다.
http://www.drdobbs.com/conversationsa-midsummer-nights-madness/184403835
이것이 const 배치에 관한 것이 었음에도 동일한 규칙이 적용됩니다.
컴파일러는 다음과 같이 읽습니다.
int (*a);
로 :
(int*) a;
변수 옆에 별을 배치하는 습관을 들이게되면 선언을보다 쉽게 읽을 수 있습니다. 또한 다음과 같은 눈가리개를 피합니다.
int* a[10];
-- 편집하다 --
나는로서 해석 말할 때 무슨 뜻인지 정확히 설명하는 int (*a)
수단 있다는 것을, *
보다 긴밀하게 결합 a
은보다가 int
, 매우 방식으로 발현에 있음을 4 + 3 * 7
3
보다 긴밀하게 결합 7
이보다 할 4
의 높은 우선 순위로 인해 *
.
ascii art에 대한 사과와 함께 파싱을위한 AST의 개요 int *a
는 대략 다음과 같습니다.
Declaration
/ \
/ \
Declaration- Init-
Secifiers Declarator-
| List
| |
| ...
"int" |
Declarator
/ \
/ ...
Pointer \
| Identifier
| |
"*" |
"a"
명확하게 보여지는 바와 같이, 그들의 공통 조상이 있기 때문에 *
더 밀접하게 묶는 반면, 당신은 나무를 올라가서 공통 조상을 찾아야합니다 .a
Declarator
Declaration
int
(int*) a
.
int
이 경우 2 단계. 모든 유형의 장식을 포함한 선언문을 읽으십시오. *a
이 경우 3 단계 다음 문자를 읽습니다. 쉼표이면 소비하고 2 단계로 돌아가십시오. 세미콜론이 중지 된 경우. 다른 것이 있으면 구문 오류가 발생합니다. ...
int* a, b;
는 포인터 쌍 을 쓰고 얻을 수 있습니다 . 내가하고있는 요점 *
은 선언의 "기본 유형"을 형성하는 유형이 아니라 변수에 바인딩하고 변수로 구문 분석한다는 것입니다. 이는 typedef가 typedef int *iptr;
iptr a, b;
몇 가지 포인터를 만들 수 있도록 도입 된 이유이기도합니다 . 형식 정의를 사용하여 당신은 할 수 결합 *
받는 int
.
Declaration-Specifier
"데코레이션"과 "결합"하여 Declarator
각 변수의 최종 유형에 도달합니다. 그러나 장식을 선언 지정자로 "이동"하지 않으면 int a[10], b;
완전히 어리석은 결과가 생성 int *a, b[10];
됩니다 int
*a
,
b[10]
;
. 그것을 이해하는 다른 방법은 없습니다.
int*a
" a
유형 int*
" 이 컴파일러에서 읽은 유형 입니다. 그것이 원래의 의견으로 의미하는 것이 었습니다.
다음과 같은 선언이있을 때 더 의미가 있기 때문입니다.
int *a, *b;
int* a, b;
만들어 b
포인터를 int
. 그러나 그들은하지 않았다. 그리고 합당한 이유가 있습니다. 제안 된 시스템 b
에서 다음 선언 의 유형은 무엇 int* a[10], b;
입니까?
int* x;
유형이 왼쪽에 있고 식별자 (이름)가 오른쪽에있는 가상의 세계로 코드를 강요하려는 사람들이 선호합니다 .
나는 "가상"이라고 말합니다.
C 및 C ++에서는 일반적으로 선언 된 식별자가 형식 정보로 둘러싸입니다.
미친 소리로 들릴지 모르지만 당신은 그것이 사실이라는 것을 알고 있습니다. 여기 몇 가지 예가 있어요.
int main(int argc, char *argv[])
" main
는 int
및에 대한 포인터 배열 을 가져 와서 char
반환하는 함수입니다 int
." 즉, 대부분 의 유형 정보가 오른쪽에 있습니다. 어떤 사람들은 함수 선언이 어떻게 든 "특별"하기 때문에 계산에 포함되지 않는다고 생각합니다. 자, 변수를 사용해 봅시다.
void (*fn)(int)
means fn
는 an를 가져 와서 int
아무것도 반환하지 않는 함수에 대한 포인터 입니다.
int a[10]
'a'를 10 int
의 배열로 선언합니다 .
pixel bitmap[height][width]
.
분명히, 나는 내 주장을 할 수있는 오른쪽에 많은 유형 정보가있는 체리를 고른 예제를 보았습니다. 유형의 대부분이 전부는 아니지만 왼쪽에있는 선언은 많이 있습니다 struct { int x; int y; } center
.
이 선언 구문은 선언이 사용법을 반영하려는 K & R의 요구에서 비롯되었습니다. 간단한 선언을 읽는 것은 직관적이며 오른쪽-왼쪽-오른쪽 규칙 (나선형 규칙 또는 오른쪽-왼쪽 규칙이라고 함)을 배우면 더 복잡한 선언을 익힐 수 있습니다.
C는 많은 C 프로그래머가이 스타일을 수용하고 int *p
.
C ++에서 문법은 조금 더 복잡해졌으며 (클래스, 참조, 템플릿, 열거 형 클래스로), 그 복잡성에 대한 반응으로, 많은 선언에서 식별자와 형식을 분리하는 데 더 많은 노력을 기울일 것입니다. 즉, 더 많은 것을 볼 수 있습니다int* p
C ++ 코드를 확인하면 스타일 선언을 .
어느 언어에서나 (1) 동일한 명령문에서 여러 변수를 선언하지 않고 (2) s (또는 별칭으로 아이러니하게 선언하는 별칭 선언 )를 사용하여 변수 선언 의 왼쪽에 항상 유형을 가질 수 있습니다 . 유형 왼쪽의 식별자). 예를 들면 다음과 같습니다.typedef
typedef int array_of_10_ints[10];
array_of_10_ints a;
(*fn)
포인터 fn
는 반환 유형 이 아닌 포인터와 연결되어 있기 때문에
이 주제의 많은 주장은 명백한 주관적이며 "별이 변수 이름에 바인딩됩니다"에 대한 주장은 순진합니다. 다음은 의견이 아닌 몇 가지 주장입니다.
잊혀진 포인터 유형 한정자
공식적으로 "별"은 유형이나 변수 이름에 속하지 않으며 pointer 라는 자체 문법 항목의 일부입니다 . 공식 C 구문 (ISO 9899 : 2018)은 다음과 같습니다.
(6.7) 선언 :
선언 지정자 init-declarator-list opt;
어디 선언 - 지정자 유형 (및 저장)를 포함하고 초기화 - 선언자 목록은 포인터와 변수의 이름이 포함되어 있습니다. 이 선언자 목록 구문을 더 자세히 분석하면 다음과 같습니다.
(6.7.6) 선언자 :
포인터 옵트 직접 선언자
...
(6.7.6) 포인터 :
*
type-qualifier-list opt
*
type-qualifier-list opt pointer
선언자가 전체 선언 인 경우 직접 선언자는 식별자 (변수 이름)이고 포인터는 별표 다음에 포인터 자체에 속하는 선택적 유형 한정자 목록이옵니다.
"별이 변수에 속함"에 대한 다양한 스타일 인수가 일치하지 않는 이유는 이러한 포인터 유형 한정자에 대해 잊어 버린 것입니다. int* const x
, int *const x
또는 int*const x
?
고려 int *const a, b;
의 종류 무엇 a
과 b
? 더 이상 "별이 변수에 속한다"는 것은 분명하지 않습니다. 오히려 사람이 const
속한 곳을 깊이 생각하기 시작 합니다.
별이 포인터 유형 한정자에 속한다는 것을 분명히 말할 수는 있지만 그 이상은 아닙니다.
포인터의 형식 한정자 목록은 int *a
스타일을 사용하는 사람들에게 문제를 일으킬 수 있습니다 . 내부에 포인터를 사용하는 사람들 typedef
(우리는 그렇게 나쁜 습관은 아닙니다!) "별은 변수 이름에 속합니다"라고 생각 하여이 미묘한 버그를 작성하는 경향이 있습니다.
/*** bad code, don't do this ***/
typedef int *bad_idea_t;
...
void func (const bad_idea_t *foo);
이것은 깨끗하게 컴파일됩니다. 이제 코드가 const라고 생각할 수도 있습니다. 별로! 이 코드는 실수로 잘못된 const 정확성입니다.
의 유형 foo
은 실제로 int*const*
-가장 바깥 쪽 포인터는 데이터가 아닌 읽기 전용으로 만들어졌습니다. 그래서이 함수 안에서 우리는 할 수 **foo = n;
있고 호출자의 변수 값을 바꿀 것입니다.
이는 표현식 const bad_idea_t *foo
에서 *
가 변수 이름에 속하지 않기 때문입니다 ! 의사 코드에서이 매개 변수 선언은 읽을 수있다 const (bad_idea_t *) foo
및 하지 로 (const bad_idea_t) *foo
. 이 경우 별은 숨겨진 포인터 유형에 속합니다. 유형은 포인터이며 const 한정 포인터는로 작성됩니다 *const
.
그러나 위의 예제에서 문제의 근본은 포인터가 typedef
아닌 *
스타일 뒤에 포인터를 숨기는 연습 입니다.
한 줄에 여러 변수 선언과 관련하여
한 줄에 여러 변수를 선언하는 것은 나쁜 습관으로 널리 인식됩니다 1) . CERT-C는 다음과 같이 훌륭하게 요약합니다.
DCL04-C. 선언 당 하나 이상의 변수를 선언하지 마십시오
영어를 읽는 것만 으로도 선언이 하나 의 선언이어야 한다는 상식에 동의합니다 .
변수가 포인터인지 여부는 중요하지 않습니다. 한 줄에 각 변수를 선언하면 거의 모든 경우에 코드가 명확 해집니다.
따라서 프로그래머가 혼란스러워한다는 논쟁 int* a, b
은 나쁘다. 문제의 근원은의 배치가 아니라 여러 개의 선언자를 사용하는 것 *
입니다. 스타일에 관계없이 대신 이것을 작성해야합니다.
int* a; // or int *a
int b;
또 다른 건전하지만 주관적인 주장은 int* a
의 유형에 a
의문의 여지 int*
가 없으므로 별이 유형 한정자에 속한다는 것입니다.
그러나 기본적으로 나의 결론은 여기에 게시 된 많은 주장이 단지 주관적이고 순진하다는 것입니다. 두 스타일 모두에 대해 올바른 주장을 할 수는 없습니다. 이는 주관적인 개인적 취향의 문제입니다.
1) CERT-C DCL04-C .
typedef int *bad_idea_t;
void func(const bad_idea_t bar);
, 위대한 선지자 댄 삭스 (Dan Saks)가 가르치는 const
것처럼 "의미 적 의미를 바꾸지 않고 가능한 한 오른쪽으로 배치하면"이것이 완전히 중단된다는 주제에 대한 짧은 섹션 이 있습니다. 문제가 될 수 있습니다. 또한 const
선언을보다 일관성있게 읽을 수 있습니다. "const라는 단어의 오른쪽에있는 것은 const 인 것입니다. 왼쪽에있는 모든 것이 그 유형입니다." 이것은 선언의 모든 const에 적용됩니다. 시도해보세요int const * * const x;
치다
int *x = new int();
이것은 번역하지 않습니다
int *x;
*x = new int();
실제로는
int *x;
x = new int();
이로 인해 int *x
표기법이 다소 일치하지 않습니다.
new
C ++ 연산자입니다. 그의 질문은 "C ++"가 아닌 "C"로 태그됩니다.
typedefs
있지만 IMHO는 불필요한 복잡성을 추가합니다.