티스토리 뷰
(1) 포인터와 배열
- int a = 7; // xxx번 주소에 인트니까 4바이트를 가지고 7을 저장
- int *a_ptr = &a; // &(ampersand) 주소연산자, *(asterisk) 주소값을 저장하는 변수를 선언할때 사용, int형 변수 a의 주소값을 저장하니까 변수 a_ptr도 int형이 되야 한다.
- 굳이 포인터를 사용하는 이유? 데이터를 직접 저장하면 데이터의 중복이 많아진다. 주소를 사용하면 어디있는지만 알면 된다.
- *a_ptr = 8; // 8을 a_ptr이 저장한 주소값의 변수 a에 8을 저장, indirection
- printf("%d %d", a, *a_ptr); // 둘다 8이 나온다
- int a = 123;
int *a_ptr = &a;
*a_ptr=456;
b=*a_ptr;
*a_ptr=789; // a는 123이었는데 간접접근으로 456을 저장, b는 456을 저장, a_ptr을 789로 바꾸어도 b는 그대로 이전 a_ptr의 값인 456을 저장하고 있다.
- int *a_ptr 과 int* a_ptr
- int *ptr = 1234; // 이런식으로 해버리면 런타임에러 난다, 사용이 불가능한건 아닌데 굳이?
- int *safer_ptr;
int a=123;
safer_ptr=&a; // 이경우는 safer_ptr이 a를 받을 것이기 때문에 상관없지만
int *safer_ptr=NULL; // 이런식으로 선언을 하자마자 초기화 하는게 좋다.
- sizeof시 포인터와 주소의 크기는 같다. 32bit는 4바이트, 64bit는 8바이트
- 포인터는 메모리의 시작주소를 저장
- call by value, 값에 의한 호출로는 함수를 통해 main함수 내부의 값을 바꿀 수 없다. call by pointer로는 가능

- 포인터에 1을 더하면 자료형 크기만큼 증가한다 ex) int* ptr=0; ptr++; 하면 ptr은 4가 된다.
- 포인터끼리 더하지는 못한다
- 포인터끼리 빼기는 할 수 있다. i는 2가 나오는데 arr[3]과 arr[5]가 double(8바이트)로 2칸 차이가 난다고 알 수 있다.

-

- int arr[2][3] : 3개짜리가 2개 있다고 생각
- 컴퓨터의 메모리는 여전히 1차원 형태이다.
- int arr[2][3] = {1,2,3,4,5,6}; //초기화 방식은 같다
- for문으로 출력시 [i]쪽을 먼저 출력하는 것이 좋다. 메모리에 있는 순서대로 출력하는 것이 좋기 때문
![]() |
![]() |
- 2차원 배열이라 해도 어차피 메모리는 1차원으로 되어 있어서 포인터를 사용가능 하다
![]() |
![]() |
- sizeof(arr)은 전체 크기인 24(4*6)이 나오는데 sizeof(arr[0])은 12(4*3)이 나옴(arr[0]은 arr[0][0], arr[0][1], arr[0][2]을 포함하기 때문이다.
![]() |
![]() |
- 고차원배열도 굉장히 많이 쓰임
- 배열을 출력하는 코드

- 배열로 선언, 정의하면 연속된 주소를 할당 받는다
- 배열의 인덱스는 0부터 시작한다
- int arr[4] = {1,2,3,4}; 100 : &arr[0], 104 : &arr[1], 108 : &arr[2], 112 : &arr[4] 이 각각의 시작주소가 된다.
자료형의 크기 만큼의 간격을 갖는다. 배열의 이름인 arr은 &arr[0]와 같이 첫번째 주소를 가리킨다
- 배열선언이 끝나면 다시 선언할 순 없다. 하나씩 넣어줘야 함
- int arr[4]로 선언하면 arr[0]~arr[3]개가 만들어진다. arr[5]나 arr[-1]을 사용하면 runtime error가 난다
- const int arr[4]를 하면 배열의 원소를 바꾸지 못한다.
- 초기화하지 않은 배열에는 쓰레기 값이 들어간다.
- static int arr[4]를 하면 초기화를 안해도 0이 들어간다
- 배열 선언시 일부를 초기화하면 나머지는 0으로 채운다
- 배열의 개수를 넣지 않고 이런식으로 사용 가능. 그러나 동적할당에선 사용이 안됨. 이유는?
- 연평균 기온을 2차원 배열로 만들고 1. 각 연도의 기온 2. 3년간의 평균기온 3. 3년간 각월의 평균 기온 출력
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#define MONTHS 12
#define YEARS 3
int main()
{
double temp_data[YEARS][MONTHS] = { -3.2, 0.2, 7.0, 14.1, 19.6, 23.6, 26.2 ,28.0, 23.1, 16.1 ,6.8, 1.2 ,
-1.8 ,-0.2, 6.3, 13.9 ,19.5 ,23.3 ,26.9 ,25.9, 22.1 ,16.4, 5.6 ,-1.9,
-4.0 ,-1.6 ,8.1, 13.0 ,18.2 ,23.1, 27.8, 28.8, 21.5 ,13.1 ,7.8 ,-0.6 };
printf("[Temperature Data]\n");
printf("Year index :");
for (int i = 1; i <= 12; i++)
printf("\t%d", i);
printf("\n");
for (int i = 0; i < YEARS; i++)
{
printf("Year %d :",i);
for (int j = 0; j < MONTHS; j++)
printf("\t%.1f", temp_data[i][j]);
printf("\n");
}
printf("\n[Yearly average temperature of 3 years]\n");
for (int i = 0; i < YEARS; i++)
{
printf("Year %d : ", i);
double sum = 0;
for (int j = 0; j < MONTHS; j++)
{
sum += temp_data[i][j];
}
printf("average temperatue = %.1f\n",sum/MONTHS);
}
printf("\n[Monthly average temperatures for 3 years]\n");
printf("Year index :");
for (int i = 1; i <= 12; i++)
printf("\t%d", i);
printf("\n");
printf("Avg temps :");
for (int i = 0; i < MONTHS; i++)
{
double sum = 0;
for (int j = 0; j < YEARS; j++)
{
sum += temp_data[j][i];
}
printf("\t%.1f", sum / YEARS);
sum = 0;
}
printf("\n");
}
- 함수에서 배열을 받을땐 포인터로 받는다. 개수는 따로 받아야 한다.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
double avg(double* arr, int n)
{
double sum = 0;
for (int i = 0; i < n; i++)
sum += *(arr + i);
return sum / n;
}
int main()
{
double arr1[5] = { 10,13,12,7,8 };
double arr2[3] = { 1.8,-0.2,6.3 };
printf("Avg = %f\n", avg(arr1, 5)); //arr1은 시작 주소값만 가지고 있어서 배열의 개수도 넘겨줘야 함
printf("Avg = %f\n", avg(arr2, 3));
return 0;
}
- 위와 같이 평균을 구하는 코드지만 함수의 파라미터를 둘다 포인터로 받음(start는 시작주소값, end는 끝주소값에 하나 더한것)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
double average(double* start , double* end )
{
int count = end - start;
double avg = 0.0;
while (start < end)
avg += *start++;
return avg / (double)count;
}
int main()
{
double arr1[5] = { 10,13,12,7,8 };
printf("Avg=%f\n", average(arr1, arr1 + 5)); //배열의 개수가 5개라 개수맞추기 위해 +5
return 0;
}
- const
1. 값을 바꾸고 싶지 않은 변수에 사용하여 상수로 만들 수 있다.
2. 배열에 사용하면 각각의 원소값을 바꾸지 못한다. 그러나 배열을 포인터로 받으면 포인터를 통해 값의 수정이 가능하다. 포인터 정의에도 const를 사용해줘야 함. 그런데! 증가연산자는 된다. -> p를 const p로 바꾸면 증가연산자도 불가
#include <stdio.h>
int main()
{
const double arr[4] = { 1.0,2.0,3.0,4.0 };
const double* p = arr;
printf("%.1f %.1f\n", p[1], arr[1]); // 2.0 2.0 출력
p++;
printf("%.1f %.1f", p[1], arr[1]); // 3.0 2.0 출력. arr의 시작주소를 가리키고 있었는데
// 증가연산자로 인해 가리키는 값이 배열의 두번째가 되었다.
return 0; //거기서 1칸 이동한 값이기에 3.0이 출력된다
}
- 변하지 않아야 하는 곳에는 cosnt를 사용하여 준다
#include <stdio.h>
void printf_array(const int* arr, const int n) // 배열출력
{
for (int i = 0; i < n; i++)
printf("%4d", arr[i]);
printf("\n");
}
void add_value(int* arr, const int n, const int add) // 각 배열에 더하기
{
for (int i = 0; i < n; i++)
arr[i] += add;
}
int sum(const int* arr, const int n) // 배열의 합
{
int sum = 0;
for (int i = 0; i < n; i++)
sum += arr[i];
return sum;
}
int main()
{
int arr[] = { 1,2,3,4,5 };
const int n = sizeof(arr) / sizeof(arr[0]);
printf_array(arr, n);
add_value(arr, n, 100);
printf_array(arr, n);
int s = sum(arr, n);
printf("sum is %d\n", s);
return 0;
}
- 이중포인터 : 일차원 포인터 변수의 시작주소를 저장.
- 포인터배열 : 배열 원소로 포인터 변수를 가지는 배열
- 포인터의 배열 - 1
#include <stdio.h>
int main()
{
int arr0[3] = { 1,2,3 };
int arr1[3] = { 4,5,6 };
int* parr[2] = { arr0,arr1 }; //포인터의 배열, 포인터가 담길수 있는 배열
for (int j = 0; j < 2; j++)
{
for (int i = 0; i < 3; i++)
printf("%d(==%d, ==%d) ", parr[j][i], *(parr[j] + i), *(*(parr + j) + i)); //같은 결과나옴
printf("\n");
}
}
- 포인터의 배열 - 2
#include <stdio.h>
int main()
{
int arr[2][3] = { 1,2,3,4,5,6 };
int* parr[2]; //포인터의 배열
parr[0] = arr[0];
parr[1] = arr[1];
for(int j=0;j<2;j++)
{
for (int i = 0; i < 3; i++)
printf("%d %d %d %d\n", arr[j][i], parr[j][i], *(parr[j] + i), *(*(parr + j) + i)); //같은 결과 나온다
}
}
- 포인터의 배열 -3
#include <stdio.h>
int main()
{
int arr[2][3] = { 1,2,3,4,5,6 };
int* parr[2]; //포인터의 배열
parr[0] = arr[0]; //포인터로 가리킬 곳의 주소를 넣음
parr[1] = arr[1]; //넣어 주기전엔 쓰레기 값을 가진다
for(int j=0;j<2;j++)
{
for (int i = 0; i < 3; i++)
printf("%d %d %d %d\n", arr[j][i], parr[j][i], *(parr[j] + i), *(*(parr + j) + i)); //같은 결과 나온다
}
printf("%p\n", &parr[0]); //포인터배열의 시작주소 값
printf("%p\n", parr[0]); //이 밑으로는 같은 주소 값을 가진다
printf("%p\n", arr);
printf("%p\n", &arr[0]);
printf("%p\n", arr[0]);
printf("%p\n", &arr[0][0]);
return 0;
}
- 포인터의 배열 -4
#include <stdio.h>
int main()
{
char* name[] = { "apple","banana","melon","mango" }; //char type 포인터의 배열
const int n = sizeof(name) / sizeof(char*);
for (int i = 0; i < n; i++)
printf("%s at %u\n", name[i], (unsigned)name[i]);
printf("\n");
char name2[][15] = { "apple","banana","melon","mango","strawberry" };
const int n2 = sizeof(name2) / sizeof(char[15]);
for (int i = 0; i < n2; i++)
printf("%s at %u\n", name2[i], (unsigned)name2[i]);
printf("\n");
return 0;
}
- 2차원 배열 주소값 : 아직 확실하게 개념이 잡힌게 아니라 틀릴 수 있음(수정요망)
#include <stdio.h>
int main()
{
int arr2d[2][4] = { 1,2,3,4,5,6,7,8 };
printf("%p\n", arr2d); // 2차배열의 첫번째 줄의 주소값
printf("%p\n", arr2d+1); // 2차배열의 두번째 줄의 주소값
printf("%p\n", arr2d[0]); //2차배열 첫번째 줄의 첫번째 원소의 주소값
printf("%p\n", arr2d[0]+1); //2차배열 첫번째 줄의 두번째 원소의 주소값
printf("\n");
printf("%p\n", *arr2d); //2차배열 첫번째 주소값의 첫번째 원소의 주소값
printf("%p\n", &arr2d[0]); // 첫번째줄의 주소값
printf("%p\n", &arr2d[0][0]); // 첫번째 원소인 1의 주소값
printf("%d %d\n", arr2d[0][0], **arr2d); // 첫번째 원소의 값, 2차배열 첫번째 주소값->2차배열 첫번째 주소값의 첫번째 원소의 주소값 -> 그 값
printf("\n");
printf("%p\n", arr2d+1); // 2차배열의 두번째 줄의 주소값
printf("%p\n", &arr2d[1]); // 2차배열 두번째 줄의 주소값
printf("%p\n", arr2d[1]); // 2차배열 두번째 줄의 첫번째 원소의 주소값
printf("%p\n", *(arr2d+1)); // 2차배열 두번째 주소값의 첫번째 원소의 주소값
printf("%p\n", &arr2d[0]+1); // 2차배열 두번째 줄의 주소값
printf("%p\n", &arr2d[1][0]); //2차배열 첫번째 값의 주소값
printf("\n");
printf("%d\n", *(*(arr2d+1)+2)); //2차배열 두번째 줄에서 세번째에 있는 주소안의 값
printf("\n");
for (int j = 0; j < 2; j++)
{
printf("[%d] = %p, %p\n", j, arr2d[j], *(arr2d + j)); //행의 주소값
for (int i = 0; i < 4; i++)
printf("[%d][%d] = %d, %d\n", j, i, arr2d[j][i], *(*(arr2d + j) + i)); //각 주소에 있는 값
}
return 0;
}
- 배열포인터와 포인터배열 : 아직 확실하게 개념이 잡힌게 아니라 틀릴 수 있음(수정요망)
#include <stdio.h>
int main()
{
int arr2d[2][4] = { 1,2,3,4,5,6,7,8 };
int(*pa)[4]; // datatype이 int[4]인 포인터, 배열포인터
int* pb[2]; // datatype이 int*가 2개인 배열, 포인터배열 //내가 이해한거라 정확하진 않음
printf("%d\n", sizeof(pa)); //8 (64bit에서 주소의 크기)
printf("%d\n", sizeof(pb)); //16(=8*2) (배열이 2개라서)
printf("\n");
pa = arr2d; // arr2d를 4개씩 묶은거 첫번째의 주소를 받음
pb[0] = arr2d[0]; //첫째줄의 주소를 받음
pb[1] = arr2d[1]; //둘째줄의 주소를 받음
printf("%p %p\n", pa, pa + 1); //첫번째 줄의 주소, 두번째 줄의 주소 //16바이트 차이
printf("%p %p\n", arr2d[0], arr2d[1]); //첫번째 줄의 주소, 두번째 줄의 주소
printf("%p %p\n", pa[0], pa[0] + 1);//첫번재 줄의 첫번재 원소의 주소, 첫번째 줄의 두번재 원소의 주소 //4바이트 차이
printf("%d\n", pa[0][0]); //첫번째 원소의 값
printf("%d\n", *pa[0]);//첫번째줄 첫번째 원소의 주소안의 값
printf("%d\n", **pa);//첫번째 원소의 값
printf("%d\n", pa[1][3]);//8번째 원소의 값(4*1+3+1)
printf("%d\n", *(*(pa + 1) + 3));////8번째 원소의 값
printf("\n");
printf("%p %p\n", pb, pb + 1); //2차원배열의 첫번째 줄의 주소가 있는 포인터의 주소, 두번째줄의 주소 있는 포인터의 주소
printf("%p %p\n", arr2d[0], arr2d[1]);//첫번째 줄의 주소, 두번째 줄의 주소
printf("%p %p\n", pb[0], pb[0] + 1);//첫번째 줄의 주소, 첫번째줄 두번째 원소의 주소
printf("%d\n", pb[0][0]);//첫번째 원소의 값
printf("%d\n", *pb[0]);//첫번째줄 첫번째 원소의 주소안의 값
printf("%d\n", **pb);//첫번째 원소의 값
printf("%d\n", pb[1][3]);//8번째 원소의 값 // 2행 4열
printf("%d\n", *(*(pb + 1) + 3));////8번째 원소의 값
printf("\n");
return 0;
}
-
#include <stdio.h>
int main()
{
int n = 5;
double x;
x = n; // int를 double에 넣으니 no error(자동 형변환)
int* p1 = &n;
double* pd = &x;
//pd = p1; double*에 int*를 넣는건은 warning이 나온다.
int* pt;
int(*pa)[3];
int ar1[2][3] = { 3, };
int ar2[3][2] = { 7, };
int** p2;
pt = &ar1[0][0]; //같다
pt = ar1[0];
pa = ar1;
//pa = ar2; 배열개수가 다르기 때문에 안된다
p2 = &pt; // 2차원포인터
*p2 = ar2[0];
//p2 = arr2; ar2는 int (*)[2]가 datatype이다
return 0;
}
-
#include <stdio.h>
int main()
{
int x = 20;
const int y = 23;
int* p1 = &x;
const int* p2 = &y; //*p2=123; 이런식으로 간접접근이 안됨. p2의 값을 못바꾸게 하려면 const p2를 하면 됨
const int** pp2 = &p2;
int x2 = 30;
int* p3 = &x2;
*pp2 = p3; //const int* const* pp2
pp2 = &p1; //const int** const pp2
return 0;
}
-
#include <stdio.h>
#define ROWS 3
#define COLS 4
int sum2d_1(int ar[ROWS][COLS])
{
int sum = 0;
for (int j = 0; j < ROWS; j++)
for (int i = 0; i < COLS; i++)
sum += ar[j][i];
return sum;
}
int sum2d_2(int arr[][COLS], int row)
{
int sum = 0;
for (int j = 0; j < row; j++)
for (int i = 0; i < COLS; i++)
sum += arr[j][i];
return sum;
}
int sum2d_3(int* ar, int row, int col) //동적할당에서 자세히
{
int sum = 0;
for (int j = 0; j < row; j++)
for (int i = 0; i < col; i++)
sum += *(ar + i + col * j);
return sum;
}
int main()
{
int data[ROWS][COLS] = { {1,2,3,4},
{5,6,7,8},
{9,0,1,2} };
printf("%d\n", data[2][3]);
int* ptr = &data[0][0];
printf("%d\n", *(ptr + 3 + COLS * 2));
printf("Sum of all elements = %d\n", sum2d_1(data));
printf("Sum of all elements = %d\n", sum2d_2(data,ROWS));
printf("Sum of all elements = %d\n", sum2d_3(&data[0][0],ROWS,COLS));
return 0;
}
-
#include <stdio.h>
#define COLS 4
int sum_1d(int arr[], int n)
{
int sum = 0;
for (int i = 0; i < n; i++)
sum += arr[i];
return sum;
}
int sum_2d(int arr[][COLS], int row)
{
int sum = 0;
for (int j = 0; j < row; j++)
for (int i = 0; i < COLS; i++)
sum += arr[j][i];
return sum;
}
int main()
{
int arr1[2] = { 1,2 };
int arr2[2][COLS] = { {1,2,3,4},{5,6,7,8} };
(int[2]) {1, 2}; //복합리터럴? 이름이 없다
printf("%d\n", sum_1d(arr1, 2));
printf("%d\n", sum_2d(arr2, 2));
printf("\n");
printf("%d\n", sum_1d((int[2]) { 1, 2 }, 2));
printf("%d\n", sum_2d((int[2][COLS]){ {1,2,3,4},{5,6,7,8 }},2));
printf("\n");
int* ptr1;
int(*ptr2)[COLS];
ptr1 = (int[2]){ 1,2, };
ptr2 = (int[2][COLS]){ {1,2,3,4},{5,6,7,8} };
printf("%d\n", sum_1d(ptr1, 2));
printf("%d\n", sum_2d(ptr2, 2));
return 0;
}
'프로그래밍' 카테고리의 다른 글
리눅스 명령어, Vi 명령어 (0) | 2020.05.16 |
---|