티스토리 뷰

프로그래밍

C - 1

쥬브62 2020. 4. 4. 03:47

(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
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함