ptr[n] = *(ptr+n)2[ptr] 이런 것이 가능하다. (int ptr[]={1,2,3,4,5}; 인 경우에도)ptr[n] 표현식은 컴파일러에 의해 *(ptr+n)으로 바뀐 후 컴파일되며 생성되는 기계어 코드도 완전히 동일하다.ar[2][1]은 *(*(ar+2)+1)과 동일하다.2[ar][1], 1[2[ar]], 1[ar[2]] 와 동일하다.int *arpi[5];*과 [ ]는 둘다 구두점이다.*가 앞쪽에 있으므로 arpi는 먼저 정수형 포인터가 되고,[ ]에 의해 그런 포인터 변수 5개를 모아 배열을 선언하게 된다.char *carpi[]={"고양이", "개", "오랑우탄", "돼지"}; 이러한 배열은pi는 고유의 메모리를 차지하고 있어 언제든지 다른 대상을 가리킬 수 있지만 ar은 선언할 때 그 위치가 이미 고정되므로 다른 대상을 가리킬 수 없다.pi가 가리키는 배열의크기는 동적으로 결정할 수 있지만, ar이 가리키는 배열의 크기는 선언할 때 정적으로 결정된다.*pi는 pi가 가리키는 곳을 바로 읽지만 ar[n]은 *(ar+n)으로 일단 번지를 더한 후 읽어야 하므로 조금 느리다.int ar[2][3][4]; 배열을 가리키는 배열 포인터는 int (*par)[3][4]로 선언한다.char arps[5][9]={"고양이", "개", "오랑우탄", "돼지", "지렁이"};
char (*ps)[9];
ps=arps;
for int(i=0;i<5;i++){
printf("%s\n", *ps++);
}
int ari [][7]={{1,2,3,4,5,6,7},{8,9,10,11,12,13,14}};
int (*pa)[7];
int (*pb)[8];
pa=ari; // 가능
pb=ari; // 에러 발생
//pb=(int (*)[8])ari; // 로 강제 캐스팅이 가능하긴 하다.
// 실제 사용 예
int GetTotalForWeek(int (*pa)[7]){
// 파라미터 값으로 넘어온 배열은 1차원 배열이긴 하지만 1차 첨자가 크기가 1인 2차원 배열로도 볼 수 있다.
// pa는 2차원 배열 포인터이기 때문이다.
int sum=0;
for (int i=0; i<7; i++){
sum+=pa[0][i];
}
return sum;
}
void main(){
int ari[][7]={
{1,2,3,4,5,6,7},
{8,9,10,11,12,13,14},
{15,16,17,18,19,20,21}
};
for (int i=0;i<3;i++){
printf("%d\n", GetTotalForWeek(&ari[i])); // ari[i]와 &ari[i]의 주소값은 실제로 같다.
// 자세한 내용은 뒤 편의 &ar 참고할 것.
}
}
OutArray함수는 크기 5의 정수형 배열을 전달닫는 것처럼 보이지만 사실은 그렇지 않다.void OutArray(int ar[5]){ // int ar[5], int *ar, int ar[] 모두 동일한 표현이다.
// int *ar이든 int ar[]로 표기하든 컴파일러는 둘 다 정수형 포인터로 해석한다.
// 따라서 [ ] 안에 배열의 크기는 생략할 수도 있고 상수값을 적을 수도 있지만 컴파일러는 여기에 표기된 상수값은 완전히 무시한다.
// 만약 함수 내에서 배열의 크기를 꼭 알아야 한다면 배열의 시작 번지와 함께 별도의 인수로 배열의 크기를 전달해야 한다.
// 이렇게 표기할 수도 있기 때문에 위 예제의 GetTotalForWeek 함수의 정의에서도 int (*pa)[7]를 int [][7]로 표기할 수 있다.
for (int i=0; i<5; i++){
printf("%d\n",ar[i]);
}
}
void main(){
int ar[]={1,2,3,4,5};
OutArray(ar);
}
int *ar 형식과 int ar[]이라는 표기는 동일하게 해석되지만int *ar : 이 인수가 포인터라는 것을 강조한다. 호출원에서 &i나 pi 등을 넘길 때는 이런 표기법을 쓰는 것이 좋으며, 함수를 쓰는 사람은 이 표기를 보고 이 인수가 정수형 변수의 번지를 전달받는다고 생각할 것이다.int ar[] : 이 인수가 배열로부터 온 포인터라는 것을 강조한다. 호출원에서는 arScore나 arValue같은 배열명으로 평가된 포인터 상수를 넘길 때 이런 표기법을 쓰는 것이 좋으며 이렇게 표기된 인수는 배열이라는 것을 쉽게 할 수 있다.void func(int (*ar)[3], int size){ // int *ar[][3] 로 표현 가능.
for (int i=0;i<size;i++){
for(int j=0;j<3;j++){
printf("%d\n", ar[i][j]);
}
}
void main(){
int ar1[2][3]={{1,2,3},{4,5,6}};
int ar1[3][3]={{7,8,9},{10,11,12},{13,14,15}};
func(ar1, 2);
func(ar2, 3);
이차배열 할당
malloc(3*4*size(char)를 이용할 수는 있다.&ar
&연산자를 쓸 수 없다.&의 피연산자가 될 때 포인터 상수가 아니라 배열 그 자체를 가리키는 것으로 변경되었다.int ar[5]={1,2,3,4,5};에서 ar과 &ar의 주소값은 같다.int *pi; 변수를 선언하고 pi=ar이라고 입력하면 정상적으로 실행되지만 pi=&ar을 입력하면 에러가 발생한다.ar은 정수형 배열의 시작번지를 가리키는 포인터 상수이므로 정확한 타입은 int * const 이며 대상체는 int다.&ar의 타입은 대상체가 크기 5의 정수형 배열이며 타입은 int(*)[5] const가 된다.&ar은 크기 5인 정수형 배열을 가리키는 배열 포인터 상수다.void main(){
int ar[5]={1,2,3,4,5};
int *p1;
int (*p2)[5];
p1=ar;
p2=&ar;
printf("before : %p\n", p1); // before : 0x7fffdb557120
printf("before : %p\n", p2); // before : 0x7fffdb557120
p1++; // 4바이트를 이동시킨다.
p2++; // 4*5 바이트를 이동시킨다.
printf("after : %p\n", p1); // after : 0x7fffdb557124
printf("after : %p\n", p2); // after : 0x7fffdb557134
printf("===\n");
p1++;
printf("after : %p\n", p1); // after : 0x7fffdb557128
p1++;
printf("after : %p\n", p1); // after : 0x7fffdb55712c
p1++;
printf("after : %p\n", p1); // after : 0x7fffdb557130
p1++;
printf("after : %p\n", p1); // after : 0x7fffdb557134
}
char name[20]; scanf("%s", name);는 왜 잘 작동되는지 이해가 될 것이다.scanf("%s", &name); 도 잘 작동이 된다.name과 &name은 타입이 다르지만 가리키는 주소는 우연히 같다.name과 &name은 둘다 포인터형이므로 4바이트이고 %s 서식과 대응될 수 있으므로 동작에도 이상이 없는 것이다.&를 붙이지 말아야 한다.&name[0]라고 쓰는 것이 정상적이다.포인터 고급에서 다룬다.printf("%s\n", "aa" "bb" " cc");의 출력 결과물은 aabb cc다.char str[]="2002 Korea/Japan Worldcup";= 구두점 다음에 큰 따옴표로 싸여진 문자열 상수를 적으면 이 문자열을 구성하는 문자들을 str 배열 요소에 순서대로 복사한다.(뿐만 아니라 배열의 제일 끝에 널 종료 문자도 자동으로 붙이며 배열 크기를 생략할 경우 문자열 길이 +1로 알아서 길이를 계산하기도 한다.)char *pr="Korea";char *ptr; ptr="Korea"; ptr="China";char str[10]; str="Korea";char str[]="Korea";
char *ptr="Korea";
//-------------------
ptr="China";
//str="China";
//-------------------
ptr[0]='C';
//ptr[0]='C';char str[]="Korea"는 str 배열의 번지가 정적 데이터 영역에 있는 "Korea"로 바뀌는 것이 아니라 정적 데이터 영역의 "Korea" 문자열이 str배열로 복사되는 것이다. 즉 str 배열은 사본 문자열이다. 또한 str배열은 문자열의 사본을 가지고 있으므로 그 내용을 바꿀 수 있다.char arCon[][32]={"Korea", "America", "Iran", "Russia"};char *pCon[]={"Korea", "America", "Iran", "Russia"};char* 배열을 선언하고 각 요소에 대해 원하는 길이만큼 동적 할당하면 된다.char **변수를 선언하고 포인터 배열을 동적 할당하고 각 배열 요소인 포인터를 또 동적할당하면 된다.int len=10,num=5,i;
char **name;
//--
name=(char **)malloc(num*sizeof(char *));
for (i=0;i<num;i++) {
name[i]=(char *)malloc(len*sizeof(char));
}
//--
for (i=0;i<num;i++) {
sprintf(name[i],"string %d",i);
puts(name[i]);
}
//--
for (i=0;i<num;i++) {
free(name[i]);
}
free(name);출처 : 혼자 연구하는 C/C++ 1 / 김상형 저 / 와우북스