10장 배열

배열 선언

int scores[10];

배열의 가장 큰 장점은 반복문을 사용하여서 배열의 원소를 간편하게 처리할 수 있음

 

score[0] = 0;

score[1] = 0;

score[2] = 0;

score[3] = 0;

score[4] = 0;

 

이래되는걸

 

#define SIZE 5

...

for(i=0 ; i<SIZE ; i++)

    score[i] = 0;

 

이래버리면됨.

 

배열의 초기화

이런식으로 초기값을 일부만 주면 나머지 원소들은 0으로 초기화됨.

배열의 초기화가 주어지지 않은 경우에는 초기값 개수가 배열의 크기가됨.

 

배열의 이름: 상수주소

int scores[] = { 10, 20, 30, 40, 50, 60 };

int i, size;

size = sizeof(scores) / sizeof(scores[0]);

for(i = 0; i < size ; i++)

        printf("%d ", scores[i]);

 

배열의 크기와 원소들을 알고싶다면 이런식으로 하면됌

sizeof(scores)를하면 24가 나옴 배열의 크기는 6이고 int형이니 4 * 6 하면 24

그리고 여기에 sizeof(scores[0])를하면 원소하나의 크기가 나옴(int)

그래서 이렇게 두개를 나누면 배열의 크기가 나오고 이 크기를 반복문을 돌려서 원소들을 배열에서 하나씩

볼수있음.

 

근데 배열을 복사할때는

int score1[SIZE];

int score2[SIZE];

score1 = score2;  

이런식으로 복사하면 컴파일 오류가 나게됨

 

int score1[SIZE];

int score2[SIZE];

int i;

for(i = 0; i < SIZE; i++)

         score1[i] = score2[i]; 

그래서 이렇게 반복문을 통해서 일일이 다 복사해야됌.

 

배열을 비교할때도 마찬가지로 이렇게 배열을 통째로 한번에 비교하려고하면 에러가남.

#include <stdio.h>

#define SIZE 5

int main(void)

{

        int i;

        int a[SIZE] = { 1, 2, 3, 4, 5 };

        int b[SIZE] = { 1, 2, 3, 4, 5 };

        if( a == b )                  // ① 올바르지 않은 배열 비교

                printf(올바른 결과입니다.\n");

        else

                printf("잘못된 결과입니다.\n");

 

그래서 이렇게 하는게아니라 아까 배열 복사처럼 일일이 반복문을 통하여 비교를 해야됌

for (i = 0; i < SIZE ; i++) // ② 올바른 배열 비교

{

  if ( a[i] != b[i] )  {

  printf("a[]b[]는 같지 않습니다.\n");   

    break;

  }  

}

if (i>=SIZE)

  printf("a[]b[]는 같습니다.\n");

 

그렇다면 배열로 최소값 찾기를 해보자

#include <stdio.h>

#include <stdlib.h>

#include <time.h>

#define SIZE 5

int main(void)

{

  int prices[SIZE] = { 0 };

  int i, minimum;

   srand( (unsigned)time( NULL ) );

  for(i = 0; i < SIZE; i++)  

    prices[i] = (rand()%100)+1

  minimum = prices[0];

  for(i = 1; i < SIZE; i++) {

  if( prices[i] < minimum )

  minimum = prices[i];

  }

  printf("최소값은 %d입니다.\n", minimum);

  return 0;

}

최소값을 찾기전 우선 배열에 랜덤값을 다 넣어줘야해서 srand 함수를 사용하여 배열에 랜덤값들을 다 넣어준다.

그 후 미니멈 변수를 선언해주고 초기값은 배열의 첫값으로 넣어준다. 그 후 배열의 크기를 미리 설정해둔 전역변수

size로 반복문을 실행하여서 미니멈값이랑 배열값이랑 비교를해서 더 작은값을 미니멈에 넣어준다.

 

#include <stdio.h>

#define STUDENTS 5

int get_average(int scores[], int n);     // ①

int main(void)

{

        int scores[STUDENTS] = { 1, 2, 3, 4, 5 };

        int avg;

        avg = get_average(scores, STUDENTS);

        printf("평균은 %d입니다.\n", avg);

        return 0;

}

int get_average(int s[], int n)      // ②

{

        int i;

        int sum = 0;

        for(i = 0; i < n; i++)

                sum += s[i];

        return sum / n;

}

이런식으로 평균을 구해주는 함수를보면 배열의 주소가 s[]로 전달되는걸 볼 수 있음.

 

변수의 값을 서로 교환할때는 어떻게 해야될까?

우리가 원래 알던방식인

int x= 1, y=2;
다음과 같이 하면 안됨
x = y;// x기존값은 파괴된다!
y = x;
 
올바른 방법
int x= 1, y=2, temp;
temp = x;
x = y;
y = temp;

이런식으로 변수의 값을 교환해주면됨.

배열에서는?

 

다음과 같이 하면 안됨

 

score[i] = score[least];

         // score[i]기존값은 파괴된다!

score[least] = score[i];

 

올바른 방법

 

temp = list[i];

list[i] = list[least];

list[least] = temp;

이런식으로 배열도 마찬가지로 해주면됨.

 

순차탐색

배열에서 순차탐색을 할땐 우선 배열의 크기를 알아야하고 이 크기를 통하여 사용자에게 탐색할

변수를 입력받은후 반복문을 통하여 배열안의 값과 사용자에게 받은 값을 비교하여서 순차적으로 

탐색하는것이 순차탐색임.

그냥 말그대로 처음부터 탐색한다는 뜻

 

#include <stdio.h>

#define SIZE 10

int main(void)

{

  int key, i;

  int list[SIZE] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

  printf("탐색할 값을 입력하시오:");

  scanf("%d", &key);

  for(i = 0; i < SIZE; i++)

  if(list[i] == key)

    break;

  if (i<SIZE)

    printf("탐색 성공 인덱스= %d\n", i);

  else

  printf("탐색 실패\n");

  return 0;

}

 

이진탐색

이진탐색은 쉽게말하자면 정렬된 배열의 중앙에 위치한 원소와 비교 되풀이하는 것임.

이 이진 탐색 함수는 파라미터로 배열과 배열의 크기와 탐색할 값을 받아서 이루어짐.

int binary_search(int list[], int n, int key)

{

  int low, high, middle;

  low = 0;

  high = n-1;

  while( low <= high ){   // 아직 숫자들이 남아있으면

  printf("[%d %d]\n", low, high);  // 하한과 상한을 출력한다.

  middle = (low + high)/2;  // 중간 위치를 계산한다.

  if( key == list[middle] )  // 일치하면 탐색 성공

  return middle;

  else if( key > list[middle] )        // 중간 원소보다 크다면

  low = middle + 1;  // 새로운 값으로 low 설정

  else

  high = middle - 1;  // 새로운 값으로 high 설정 

  }

  return -1;

}

 

 

2차원 배열

int s[10];       // 1차원 배열

int s[3][10];    // 2차원 배열

int s[5][3][10]; // 3차원 배열

2차원 배열 선언

#include <stdio.h>

#include <stdlib.h>

#define ROWS 3

#define COLS 5

int main(void)

{

  int s[ROWS][COLS]; // 2차원 배열 선언

  int i, j; // 2개의 인덱스 변수

  for (i = 0; i < ROWS; i++)

  for (j = 0; j < COLS; j++)

  s[i][j] = rand() % 100;

  for (i = 0; i < ROWS; i++)

  {

  for (j = 0; j < COLS; j++)

  printf(" % 02d ", s[i][j]);

  printf("\n");

  }

  return 0;

}

이런식으로 랜덤 수를 2차원 배열로 들어가게끔해서 2차원 배열을 선언할 수 있음

int s[3][5] = { //int s[][5];

  {  0,  1,  2,  3,  4 }, // 첫 번째 행의 원소들의 초기값

  { 10, 11, 12, 13, 14 }, // 두 번째 행의 원소들의 초기값

  { 20, 21, 22, 23, 24 } // 세 번째 행의 원소들의 초기값

};

이런식으로도 가능!

 

2차원 배열을 함수로 전달하는 방법은?

#include <stdio.h>

#define  YEARS   3

#define  PRODUCTS   5

int sum(int grade[][PRODUCTS]);

int main(void)

{

  int sales[YEARS][PRODUCTS] =

    { {1, 2, 3},

      {4, 5, 6},

                   {7, 8, 9} };

  int total_sale;

  total_sale = sum(sales);

  printf("총매출은 %d입니다.\n", total_sale);

  return 0;

}

 

int sum(int grade[][PRODUCTS])

{

  int y, p;

  int total = 0;

  for(y = 0; y < YEARS; y++)

  for(p = 0; p < PRODUCTS; p++)

  total += grade[y][p];

  return total;

}

이런식으로 sum이라는 함수에서 파라미터로 배열을 받아서 total에 모든 배열의 값들을 다 더한값을 넣어주면서

그 값을 리턴하여 배열에 있는 모든 값들의 합을 알 수 있다.

 

 

11장 포인터

포인터: 주소를 가지고 있는 변수

변수는 메모리에 저장됨. 메모리는 바이트 단위로 엑세스됨.

변수의 크기에 따라서 차지하는 메모리 공간이 달라짐.(char형(1바이트), int형(4바이트))

int main(void)

{

  int i = 10;

  char c = 69;

  float f = 12.3;

}

변수의 주소를 계산하는 연산자: &

변수 i의 주소: &i

int main(void)
{
	int i = 10;
	char c = 69;
	float f = (float)12.3;

	printf("i의 주소: %u\n", &i);	    // 변수 i의 주소 출력, %p
	printf("c의 주소: %u\n", &c);  // 변수 c의 주소 출력
	printf("f의 주소: %u\n", &f);   // 변수 f의 주소 출력
	return 0;
}

이런식으로 변수의 주소를 출력하게되면

이런식으로 값이 나오게됨.

 

포인터는 변수의 주소를 가지고 있는 변수임.

포인터의 선언

 

&는 주소연산자임.

*는 간접 참조 연산자임(포인터가 가리키는 값을 가져옴)

 

int i = 10;  // 정수형 변수 i 선언

int *p;  // 포인터 변수 p 선언

p = &i;    // 변수 i의 주소가 포인터 p대입

printf(“%d, %d\n”, i, *p); //10, 10

변수 i의 주소가 포인터 p로 대입이되었다.

출력해보면 변수 i의 값은 10이고, 포인터변수 p에 *를 붙힌 *p는 p가 가리키는 값을 가져오니 p는

i를 가리키니 i인 10이 출력된다.

 

printf(“%u, %u, %u\n”, &i, p, &p); //4, 4, 12

&i는 i의 주소 4이고,

p는 아까 변수 i의 주소가 p로 대입되었으니 마찬가지로 4이다.

&p는 포인터변수 p의 주소이므로 그림에 나온거처럼 12이다.

 

 

char c = 'A';    // 문자형 변수 c

float f = 36.5;    // 실수형 변수 f

double d = 3.141592;  // 실수형 변수 d

char *pc = &c;    // 문자를 가리키는 포인터 pc

float *pf = &f;    // 실수를 가리키는 포인터 pf

double *pd = &d;    // 실수를 가리키는 포인터 pd

 

#include <stdio.h>

int main(void)
{
	int i = 10;
	double f = 12.3;
	int *pi = NULL;
	double *pf = NULL;

	pi = &i;
	pf = &f;

	printf(“%u %u\n”, pi, &i);
	printf(“%u %u\n”, pf, &f);
	return 0;
}

pi: 변수 i의 주소값이 출력됌

&i: 변수 i의 주소값이 출력됌

pf: 변수 f의 주소값이 출력됌

&f: 변수 f의 주소값이 출력됌

 

*pi : 변수 i의 값인 10이 출력됌

*pf: 변수 f의 값인 12.3이 출력됌

 

&pi: 포인터 변수 pi의 주소값이 출력됌

&pf: 포인터 변수 pf의 주소값이 출력됌

 

int i;
char c;
double d;

int *p= &i;
char *pc= &c;
double *pd= &d;

 

int *p = &i;가 뭐랑 같냐면

p = &i; 이거랑 똑같음 (헷갈 ㄴㄴ)

 

 

*p = 8;  // 변수 i8을 저장 (4 byte)

*pc = 8;  // 변수 c8저장 (1 byte)

*pd = 8;  // 변수 d8저장 (8 byte)

 

*p는 i의 값인데 *p에 8을 넣으면 마찬가지로 i에 8이 들어감.

*pc는 문자열c의 값인데 *pc에 8을 넣으면 마찬가지로 문자열 c에 8이 들어감.

*pd는 실수형 d의 값인데 *pd에 8을 넣으면 d에 8이 들어감.

 

#include <stdio.h>

int main(void)
{
	int i = 3000;
	int *p=NULL;	

	p = &i;	
	
	printf("i = %d\n", i);	// 변수의 값 출력
	printf("&i = %u\n", &i);	// 변수의 주소 출력

	printf("p = %u\n", p);	
	printf("*p = %d\n", *p);	
			
	return 0;
}

여기서 뭐가 출력되는지 한번 봐보자

i: 3000 출력

&i: 변수i의 주소값 출력

p: 변수 i의 주소값 출력

*p: 변수 i의 값인 3000출력

 

그냥 쉽게 말하자면 포인터 변수에 *붙혀져있으면 포인터가 가리키는 변수의 값이고

*안붙혀져있으면 포인터가 가리키는 변수의 주소값임

 

#include <stdio.h>

int main(void)
{
	int x=10, y=20;
	int *p;

	p = &x;
	printf("p = %d\n", p);
	printf("*p = %d\n\n", *p);

	p = &y;
	printf("p = %d\n", p);
	printf("*p = %d\n", *p);
	return 0;
}

p=&x; 일때

 p: 변수 x의 주소값

*p: 변수 x의 값인 10

 

p=&y; 일때

p: 변수 y의 주소값

*p: 변수 y의 값인 20

 

 

int x= 10;

float *p= &x; //경고 발생

*p= 20;

printf("%d, %f\n", x, *p);

근데 이런식으로 형 안맞추고 써버리면 경고발생함

 

#include <stdio.h>
int main(void)
{
	int i=10;
	int *p;

	p = &i;
	printf("i = %d\n", i);

	*p = 20;
	printf("i = %d\n", i);
	return 0;
}

p=&i;일때 

i: i의 값인 10 출력

 

*p = 20을 하면

*p는 변수 i의 값임 근데 여기에 20을 대입해준거니까 결론적으로는

i=20이랑 같은말임. 즉 이후에 i의 값을 출력하면 20을 넣었으니

20이 출력됌.

(이런식으로 포인터를 통하여 변수의 값 변경할 수 있음)

 

포인터 사용시에는 초기화가 안된 포인터를 사용하면 안됨.

int main(void) 
{ 
	int *p; 	// 포인터 p는 초기화가 안되어 있음
	*p = 100; 	// 위험한 코드, 컴파일시 경고만 발생하고 실행시 오류 발생
	return 0;
}

포인터p가 가르키는 위치가없음

그래서 포인터가 아무것도 가리키고 있지 않은 경우에는 NULL로 초기화 시켜줘야함.

int *p = NULL;

  if (p!=NULL) //pNULL이 아닌 것을 확인 후 사용 

 

포인터 타입과 변수의 타입은 일치해야함.

 

포인터가 가능한 연산은 증가, 감소, 덧셈, 뺄셈 연산임.

자기 크기만큼 증가됌

#include <stdio.h>
int main(void)
{
	char *pc; 
	int *pi;
	double *pd;

	pc = (char *)10000;			
	pi = (int *)10000;			
	pd = (double *)10000;			
	printf("증가 전 pc = %d,  pi = %d,  pd = %d\n", pc, pi, pd);

	pc++;
	pi++;
	pd++;
	printf("증가 후 pc = %d,  pi = %d,  pd = %d\n", pc, pi, pd);
	printf(“pc+2 = %d, pi+2 = %d, pd+2 = %d\n”, pc+2, pi+2, pd+2);
	return 0;
}

 

포인터를 초기화 할땐 이렇게 하나봄

pc = (char *)10000;

암튼 이렇게하면 초기화가되고 출력해주면 초기값인 10000이 나옴

근데 이제 ++연산 후에 값들을 출력해주면

증가후:

pc: 10001, pi: 10004, pd: 10008

 

*p++: p가 가리키는 위치에서 값을 가져온후! p를 증가한다.

(*p)++: p가 가리키는 위치의 값을 증가한다

 

 

그렇다면 이걸 이해했으면 다음을 해석해보자

#include <stdio.h>

int main(void)
{
	int i = 10, j;
	int *pi = &i;

	printf("i = %d,  pi = %p\n", i, pi);
	(*pi)++;	
	printf("i = %d,  pi = %p\n", i, pi);

	printf("i = %d,  pi = %p\n", i, pi);
	j= *pi++; //j= *pi; pi++;	
	printf("i = %d, j = %d, pi = %p\n", i, j, pi);

	return 0;
}

i: 변수 i의 값인 10 출력

pi: 포인터변수 pi가 가리키는 변수i의 주소값 출력

(*pi)++; 연산후 // 포인터 변수 pi가 가리키는 값에 ++ 연산을 하여라 라는뜻

i: 변수 i가 가리키는 값 +1한 값인 11 출력

pi: 아까와 동일하게 포인터변수 pi가 가리키는 변수i의 주소값 출력

j= *pi++; //j= *pi; pi++; 연산후 // 포인터 변수 pi가 가리키는 값을 j에 대입한후, pi가 가리키는 값인 변수 i의 주소값에 ++연산을 취하여라 이때 ++연산은 1을 더해지는게 아니라 포인터변수의 형만큼 더해짐 얜 지금 Int 형(4바이트)이니 4가 더해짐)

i: 아까와 동일하게 11출력

j: 포인터 pi가 가리키는 값인 변수 i의 값을 가져온거니 11출력

pi: 포인터 변수 pi가 가리키는 변수i의 값을 가져온후 j에 대입하고 난뒤 pi의 값에 ++연산을 한것이니

변수 i의 주소값 + int형(4바이트) 출력

 

포인터의 형변환

c언어에서는 꼭 필요한 경우에 명시적으로 포인터의 타입을 변경할 수 있지만 권장하진않음.

 

#include <stdio.h>

int main(void)
{
	int data = 0x0A0B0C0D;
	char *pc;
	pc = (char *)&data;
	
	for (int i = 0; i < 4; i++) {
		printf(“*(pc + %d) = %02X \n”, i, *(pc + i));
	}
	return 0;
}

이것의 출력값

*(pc + 0) = 0D

*(pc + 1) = 0C

*(pc + 2) = 0B

*(pc + 3) = 0A

 

인수 전달 방법

함수 호출 시에 인수 전달 방법
1.값에 의한 호출 (복사본이 전달됨)
2. 참조(주소)에 의한 호출 (원본이 전달됨, 포인터 이용)
 
#include <stdio.h>
void swap(int x, int y);
int main(void)
{
	int a = 100, b = 200;
	printf(“a=%d b=%d\n”,a, b);

	swap(a, b);

	printf(“a=%d b=%d\n”,a, b);
	return 0;
}
void swap(int x, int y)
{
	int tmp;
	printf(“x=%d y=%d\n”,x, y);

	tmp = x;
	x = y;
	y = tmp;

	printf(“x=%d y=%d\n”,x, y);
}
출력값
a: 100, b: 200
swap(a, b);연산

x: 100, y: 200

x:200, y: 100

swap(a, b);연산후

a: 100, b:200

스왑 함수를 사용하였음에도 불구하고 ab를 다시 출력하였을때는 값이 안바뀜

왜? 이게 아까 말한 값에 의한 호출임 (복사본을 전달)

호출된 함수의 결과가 호출하는 함수로 전달이안됌.

 

그렇다면 이제 참조(주소)에 의한 호출을 살펴보자

#include <stdio.h>
void swap(int x, int y);
int main(void)
{
	int a = 100, b = 200;
	printf(“a=%d b=%d\n”,a, b);

	swap(&a, &b);

	printf(“a=%d b=%d\n”,a, b);
	return 0;
}
void swap(int *px, int *py)
{
	int tmp;

	tmp = *px;
	*px = *py;
	*py = tmp;

}

출력값

a: 100, b: 200

swap(&a, &b); 연산후

a: 200 b: 100

이렇게 참조(주소)에 의한 호출 을 사용하면 값이 바뀜 포인터에 의한 호출은 주소를 전달하기 때문

 

#include <stdio.h>
// 기울기와 y절편을계산
int get_line_parameter(int x1, int y1, int x2, int y2, float *slope, float *yintercept)
{
	if( x1 == x2 ) 
		return -1;
	else {
	  *slope = (float)(y2 - y1)/(float)(x2 - x1);
	  *yintercept = y1 - (*slope)*x1;
	  return 0;
	}
}
int main(void)
{
	float s, y;
	if( get_line_parameter(3,3,6,6,&s,&y) == -1 )
		printf("에러\n");
	else 
		printf("기울기는 %f, y절편은 %f\n", s, y);
	return 0;
}

이건 두점의 좌표를 받아서 기울기를 계산해서 출력해주는 함수인데 이렇게 참조에의한 호출을 사용하면 값이

아예 바뀜.

 

포인터와 배열

배열의 이름이 포인터임

포인터는 배열처럼 사용이 가능함

#include <stdio.h>

int main(void)
{
	int a[] = { 10, 20, 30, 40, 50 };

	printf("&a[0] = %u\n", &a[0]); 
	printf("&a[1] = %u\n", &a[1]); 
	printf("&a[2] = %u\n", &a[2]); 

	printf("a = %u\n", a); 

	return 0;
}

출력값: 

&a[0] = 1245008

&a[1] = 1245012 

&a[2] = 1245016 

a = 1245008

 

#include <stdio.h>

int main(void)
{
	int a[] = { 10, 20, 30, 40, 50 };
	printf(“a = %u\n”, a);
	printf(“a + 1 = %u\n”, a + 1);
	printf(“*a = %d\n”, *a);
	printf(“*(a+1) = %d\n”, *(a+1));
	return 0;
}

이것의 출력값은 

a = 1245008

a + 1 = 1245012

*a = 10  // *a==a[0]

*(a+1) = 20  // *(a+1)==a[1]

이것이다 출력값을 자세히 살펴보자

배열 a를 출력하면 배열 a의 주소값이 나온다

그리고 a+1을 출력하면 int형 배열답게 4가 추가된 주소값이 나온다

근데 포인터형을 붙힌 *a를 출력해보면 배열의 첫번째 값이 나온다

그리고 포인터형을 붙인 *(a+1)을 출력해보면 배열의 두번째 인덱스 값이 나온다.

아까 처음에 언급한말인 배열의 이름이 포인터임 라는 말을 잘 살펴보면 배열의 이름이 포인터라는 말이 이해가

갈것이다. 배열에 이름에 포인터를 붙혀야 배열이 가리키는 값을 출력해주니까

int *p = a 를 하면 이렇게 포인터와 연결이된다

 

#include <stdio.h>
int main(void)
{
	int a[] = { 10, 20, 30, 40, 50 };
	int *p;

	p = a;
	printf("a[0]=%d a[1]=%d a[2]=%d \n", a[0], a[1], a[2]);
	printf("p[0]=%d p[1]=%d p[2]=%d \n\n", p[0], p[1], p[2]);

	p[0] = 60;
	p[1] = 70;
	p[2] = 80;

	printf("a[0]=%d a[1]=%d a[2]=%d \n", a[0], a[1], a[2]);
	printf("p[0]=%d p[1]=%d p[2]=%d \n", p[0], p[1], p[2]);
	return 0;
}

출력값:

a[0] = 10, a[1] = 20, a[2] = 30

p[0] = 10, p[1] = 20, p[2] = 30

 

a[0] = 60, a[1] = 70, a[2] = 80

p[0] = 60, p[1] = 70, p[2] = 80

 

일반 매개변수 vs 배열 매개변수 

배열 매개변수는 포인터로 생각할 수 있다

배열의 이름은 포인터이며 함수에서 포인터를 이용하여 원본 배열을 변경할 수 잇음.

// 포인터와 함수의 관계
#include <stdio.h>

void sub(int b[], int n);

int main(void)
{
	int a[3] = { 1,2,3 };
	
	printf("%d %d %d\n", a[0], a[1], a[2]);
	sub(a, 3);
	printf("%d %d %d\n", a[0], a[1], a[2]);
	
	return 0;
}

void sub(int b[], int n)
{
	b[0] = 4;
	b[1] = 5;
	b[2] = 6;
}

출력값

1 2 3

sub(a, 3); 함수 실행후

4 5 6

 

다음 두가지 방법은 완전히 동일함

포인터가 인덱스 표기법보다 빠른 이유는?

인덱스를 주소로 변환할 필요가 없다

이렇게 배열에서 값을 가져올때 포인터로하면 즉각적으로 가져와지는데 배열인덱스로 하려면 인덱스를

주소로변환해야됌

 

단 지역변수의 주소를 반환하면 함수가 종료되어 사라지기 때문에 오류가남

 

영상처리

영상의 모든 픽셀의 값을 10씩 올려주는 코드

void brighten_image(int image[][SIZE])
{
	int r,c;
	int *p;
	p = &image[0][0]; //p= image;
	for(r=0; r<SIZE; r++){
		for(c=0; c<SIZE; c++){
			*p += 10;
			p++;
		}
	}
}
int image[5][5] = {
		{ 10, 20, 30, 40, 50},
		{ 10, 20, 30, 40, 50},
		{ 10, 20, 30, 40, 50},
		{ 10, 20, 30, 40, 50},
		{ 10, 20, 30, 40, 50}};

보면 포인터로 진행하는데 배개면수로 2차원 배열을 받아서 포인터변수 p에 배열의 주소를 할당하여서 배열의 크기

만큼 반복문을 돌려서 p에 10씩 더해주고 p스스로 ++연산을 하여 앞으로 가게 해준다.

 

포인터 사용의 장점

연결리스트나 이진트리등의 향상된 자료구조를 만들 수 있다.

포인터를 매개변수로 이용하여 함수 외부의 변수의 값을 변경할수있다.

 

문자열

문자열 표현 방법

 

문자열: 문자들이 여러개 모인 것(하나의 문자는 char형 변수로 저장)

문자열 변수: 변경 가능한 문자열을 저장할 수 있는 변수

NULL 문자('\0'): 문자열의 끝을 나타낸다.
문자열은 어디서 종료되는지 알수가 없으므로 표시를 해주어야한다.
#include <stdio.h>
int main(void)
{	
	int i;
	char str[4];
	str[0] = 'a';
	str[1] = 'b';
	str[2] = 'c';
	str[3] = '\0';		
	
	i = 0;
	while(str[i] != '\0')	{
		printf("%c", str[i]);		
		i++;
	}
	return 0;
}

이렇게 배열에 있는 문자들을 하나씩 출력해보자(NULL)문자가 나오면 반복을 종료하도록함.

출력결과: abc

 

문자배열의 초기화

문자열 출력

char str[] = “abc”;

printf(“%s”, str);

이렇게 %s로 출력하면됌

char str[] = “abc”;

printf(str);

이렇게도 가능

#include <stdio.h>

int main(void)
{
	char str1[6] = "Seoul";
	char str2[3] = { ‘i’, ‘s’ , ‘\0’};
	char str3[] = "the capital city of Korea.";
	printf("%s %s %s\n", str1, str2, str3);
	return 0;
}

출력결과: Seoul is the capital city of korea

 

#include <stdio.h>
int main(void)
{
	char src[] = "The worst things to eat before you sleep";
	char dst[100];	
	int i;
	printf("원본 문자열=%s\n", src);
	for(i=0 ; src[i] != ‘\0’ ; i++)
		dst[i] = src[i];	
	dst[i] = ‘\0’;
	printf("복사된 문자열=%s\n", dst);
	return 0;
}

출력결과: 원본 문자열=The worst things to eat before you sleep

               복사된 문자열=The worst things to eat before you sleep

 

문자열 길이 계산하기

// 문자열의 길이를 구하는 프로그램
#include <stdio.h>

int main(void)
{
	char str[30] = "C language is easy";
	int i = 0;
	
	while(str[i] != 0)
		i++;

	printf("문자열\"%s\"의 길이는 %d입니다.\n", str, i);

	return 0;
}

출력결과: 문자열 "C language is easy" 의 길이는 18입니다.

 

이런식으로 문자 배열의 각각의 원소를 개별적으로 변경하는 방법도 있지만

strcpy()를 이용하여 문자열을 문자 배열에 복사하는 방법도있음

strcpy(str, "World");

이런식으로

str배열에 저 단어를 넣는다는 의미

 

문자열 상수: HelloWorld 같이 프로그램 소스 안에 포함된 문자열

문자열 상수는 메모리 영역중에서 상수 영역에 저장됨.

char *p = "HelloWorld";

이건 무슨 의미일까?

데이터 세그먼트: 값을 변경할 수 있는 메모리 영역(스택영역)

텍스트 세그먼트: 값을 읽기만하고 변경할 수는 없는 메모리 영역(상수 영역)

즉 저 p를 통해서 텍스트 세그먼트에 새로운 값을 저장하려고하면 오류가 생김

 

#include <stdio.h>

int main(void)
{
	char *p = "HelloWorld";
	printf("%s \n", p);

	p = "Welcome to C World!"; // 가능
	printf("%s \n", p);

	p = "Goodbye"; // 가능
	printf("%s \n", p);
	// p[0] = ‘a’; // 오류가 발생한다.

	return 0;
}

출력값:

HelloWorld

Welcome to C World!

Goodbye

이런식으로는 가능

 

문자 입출력 라이브러리

getchar()의 사용

// getchar()의 사용
#include <stdio.h>
int main(void)
{
	int ch;		// 정수형에 주의
	while( (ch = getchar()) != EOF )
		putchar(ch);	
	return 0;
}

출력결과:

여기서 EOF는 end of file을 나타내는 문자.

 

_getch(), _putch()의 사용

 

#include <stdio.h>
#include <conio.h>

int main(void)
{
	int ch;
	while( (ch = _getch()) != 'q' )
		_putch(ch);
	return 0;
}

이건 버퍼를 사용하지않아서 엔터 안눌러도 입력하면 자동으로출력됌

출력값:

abc를 입력했으면 abc

 

비교

버퍼가없이 바로 받으려면 _getch()사용해야함.

 

문자 입출력 라이브러리 함수

 

gets_s()puts() 문자열 입출력

char buf[100];
gets_s(buf, 100); //사용자로부터 한줄을 입력받는다. 최대 입력개수는 100
puts(buf); //한줄을 출력한다.
#include <stdio.h>
int main(void)
{
	char name[100];
	char address[100];
	printf("이름을 입력하시오: ");
	gets_s(name, 100); //한줄 단위(여러단어)로 입력받을때 사용한다.
	printf("현재 거주하는 주소를 입력하시오: ");
	gets_s(address, 100);
	puts(name);
	puts(address);
	return 0;
}

출력값:

이름을 입력하시오: 홍길동

현재 거주하는 주소를 입력하시오: 서울시 종로구 100번지

 

홍길동

서울시 종로구 100번지

 

문자열 복사하기 (strcpy)

char dst[6];

char src[6] = "Hello";

strcpy(dst,src);

이렇게 하면 문자열이 복사됌

 

문자열 연결하기 (strcat)

char dst[12]="Hello";

char src[6] = "World";

strcat(dst,src);

//이렇게하면 dst가 "HelloWorld"가 됌.

 

// strcpy와 strcat
#include <string.h>
#include <stdio.h>

int main( void )
{
	char string[80];

	strcpy( string, "Hello world from " ); 
	strcat( string, "strcpy " );           
	strcat( string, "and " );              
	strcat( string, "strcat!" );          
	printf( "string = %s\n", string );
	return 0;
}

출력값: string = Hello world from strcpy strcat!

 

문자열 비교 (strcmp)

int result = strcmp("dog","dog");

//strcmp는 문자열 s1,s2를 비교하여 사전적인 순서에서 s1이 앞에있으면 음수가 반환되고, 같으면 0 

뒤에있으면 양수가 반환된다. (문자열이 같으면 strcmp는 0을 반환함.)

 

// strcmp() 함수
#include <string.h>
#include <stdio.h>

int main( void )
{
	char s1[80];	// 첫번째 단어를 저장할 문자배열
	char s2[80];	// 두번째 단어를 저장할 문자배열
	int result;

	printf("첫번째 단어를 입력하시오:");
	scanf("%s", s1);
	printf("두번째 단어를 입력하시오:");
	scanf("%s", s2);
    result = strcmp(s1, s2);
	if( result < 0 )
		printf("%s가 %s보다 앞에 있읍니다.\n", s1, s2);
	else if( result == 0 )
		printf("%s가 %s와 같습니다.\n", s1, s2);
	else 
		printf("%s가 %s보다 뒤에 있습니다.\n", s1, s2);
	return 0;
}

출력값: 

첫번째 단어를 입력하시오: Hello

두번째 단어를 입력하시오: World

Hello가 World보다 앞에 있습니다. //알파벳 순으로 즉 사전에서 앞에있다는 의미임.

 

 

문자검색(strchr)

char *p = strchr("dog", 'g');

//이렇게하면 g문자의 주소를 반환함.

#include <string.h>
#include <stdio.h>
int main( void )
{	char s[] = "language";	 
	char c = 'g';	
	char *p;
	int loc;

	p = strchr(s, c);
	loc = (int)(p - s);
	printf("s 주소:%u, p 주소:%u\n", s, p);
	if ( p != NULL )		
		printf( "첫번째 %c가 %d에서 발견되었음\n", c, loc );
	else
		printf( "%c가 발견되지 않았음\n", c );
	return 0;
}

p = strchr(s,c); // 이렇게하면 s안에서 문자 c를 찾아줌

만약 문자열에 찾으려는 문자가 없다면 문자가 발견되지않았음이라고 출력함

 

출력값: 

s주소: 문자열 s의 첫번째 인덱스 주소값 p주소: 찾으려하는 c(p)의 주소값

첫번째 g가 3에서 발견되었음

 

문자열 검색(strstr)

char *p = strstr("dog", "og");

//str함수는 문자열 안에서 부분문자열을 검색하는 함수임 만약 부분 문자열이 발견되면 그 위치주소를 반환함.

문자열 찾지 못하면 NULL값 반환됨.

 

#include <string.h>
#include <stdio.h>
int main( void )
{	
	char s[] = "A joy that's shared is a joy made double";
	char sub[] = "joy";	
	char *p;
	int loc;
	p = strstr(s, sub);
	loc = (int)(p - s);
	if ( p != NULL )
		printf( "첫번째 %s가 %d에서 발견되었음\n", sub, loc );
	else
		printf( "%s가 발견되지 않았음\n", sub );
}

출력값: 

첫번째 joy가 2에서 발견되었음.

 

문자열 토큰 분리(strtok)

char s[] = "Hello World";

char delimit[] = " ";

char *p = strtok(s, delimit);

//문자열을 스페이스문자를 사용하여 단어들로 분리함

//strtok()함수는 찾는 문자열의 시작위치를 내부에서 static으로 관리하고 1번째 인자가 문자열이면 해당 문자열의

처음위치로 설정하고 NULL이면 찾은 delimiters의 다음 위치가 설정됨.

 

문자열 수치 변환

문자열 "36.5"와 수치값 36.5는 컴퓨터 안에서 상당히 다르게 저장된다.

sprintf()와 sscanf()

앞에 붙은 s는 string을 의미함

문자열을 수치로 바꿀땐 sscanf, 수치를 문자열로 바꿀땐 sprintf

 

#include <stdio.h>

int main( void )
{
	char s[] = "100";
	int value;

	sscanf(s, "%d", &value);
	printf("%d \n", value);
	value++;
	sprintf(s, "%d", value);
	printf("%s \n", s);
	return 0;
}

출력값: 

100

101

 

영상파일이름자동생성 프로그램

#include <stdio.h>
#include <string.h>
int main(void)
{
	char filename[100];
	char s[100];
	int i;
	for(i=0; i < 6; i++){
		strcpy(filename, "image");
		sprintf(s, "%d", i);
		strcat(filename, s);
		strcat(filename, ".jpg");
		printf("%s \n", filename);
	}
	return 0;
}

출력값:

image0.jpg

...

image6.jpg

 

문자열을 수치로 변환하는 전용함수 

#include <stdio.h>
#include <stdlib.h>
int main( void )
{
	char s1[] = "100";
	char s2[] = "12.93";
	char buffer[100];
	int i;
	double d, result;

	i = atoi(s1);
	d = atof(s2);
	result = i + d;

	sprintf(buffer, "%f", result);
	printf("연산 결과는 %s입니다.\n", buffer);
	return 0;
}

출력값: 연산 결과는 112.93000000입니다.

//int형이랑 더블형이랑 덧셈을 하고 이걸 sprintf를 사용하여서 문자열로 바꿔주고 이걸 다시 출력함.

 

문자열의 배열

문자열이 여러개 있는경우에는 어떤 구조를 사용하여 저장하면 제일 좋을까?

문자열의 배열: char[][]

문자 포인터 배열: char *[]

 

#include <stdio.h>

int main( void )
{
	int i;
	char menu[5][10] = { // char *menu[5]=  {
		"init",
		"open",
		"close",
		"read",
		"write"
	};

	for(i = 0; i < 5; i++)
		printf("%d 번째 메뉴: %s \n", i, menu[i]);

	return 0;
}

출력값:

0번째 메뉴: init

1번째 메뉴: open

2번째 메뉴: close

3번째 메뉴: read

4번째 메뉴: write

 

#include <stdio.h>
int main( void )
{
	int i;
	char fruits[3][20];
	for(i = 0; i < 3; i++) {
		printf("과일 이름을 입력하시오: ");
		scanf("%s", fruits[i]);
	}
	for(i = 0; i < 3; i++)
		printf("%d번째 과일: %s\n", i, fruits[i]);
	return 0;
}

char fruits[3][20]; -> char *fruits[3];

즉 위에 scanf에서 입력값을받을때 배열앞에 &붙히면안됌

 

출력값:

과일 이름을 입력하시오: 사과

과일 이름을 입력하시오: 배

과일 이름을 입력하시오: 포도

0번째 과일: 사과

1번째 과일: 배

2번째 과일: 포도

 

간단한 한영사전 구현

#define ENTRIES 5

int main( void )
{
	int i, index;
	char dic[ENTRIES][2][30] = {
		{"book", "책"},
		{"boy", "소년"},
		{"computer", "컴퓨터"},
		{"lanuguage", "언어"},
		{"rain", "비"},
	};
	char word[30];
    printf("단어를 입력하시오:");
	scanf("%s", word);

	for(i = 0; i < ENTRIES; i++)
	{
		if( strcmp(dic[i][0], word) == 0 )
		{
			printf("%s: %s\n", word, dic[i][1]);
			return 0;
		}
		
	}
	printf("사전에서 발견되지 않았습니다.\n");
}

출력값: 

단어를 입력하시오: book

book: 책

 

문자 암호화하기

문자 하나씩 앞당겨서 암호화

#include <stdio.h>

void encrypt(char cipher[], int shift);

int main (void) {
	char cipher[50];
	int shift=3;
	printf("문자열을 입력하시오: ");
	gets(cipher); // 한줄 전체 입력
	encrypt (cipher, shift);
	return 0;
}
void encrypt (char cipher[], int shift) {
	int i = 0;
	while (cipher[i] != '\0') {		
		if( cipher[i] >= ‘a' && cipher[i] <= 'z'){
			cipher[i] += shift;			
			if( cipher[i] > 'z' ) 
				cipher[i] -= 26;
		}
		i++;
	}
	printf("암호화된 문자열: %s", cipher);
}

이렇게 아스키코드값을 이용하여서 해버리면됌

 

복호화하는 함수는 decrypt()임

 

행맨게임 만들기

//빈칸으로 구성된 문자열이 주어지고 사용자는 문자열에 들어갈 글자들을 하나씩 추측해서 맞추는 게임

//사용자가 문자열에 들어 있는 글자를 정확하게 입력했으면 화면에 그 글자를 출력한다.

//일정한 횟수만 시도할 수 있게 하라.

 

#include <stdio.h>
int check(char s[], char a[], char ch);
int main (void) {
	char solution[100] = "meet at midnight“;
	char answer[100] = "____ __ ________“;
	char ch;
	while(1) {
		printf("문자열을 입력하시오: %s \n", answer);
		printf("글자를 추측하시오: ");
		ch = getchar();
		if( check(solution, answer, ch) == 1 )				break;
		getchar();//fflush(stdin); // 줄바꿈 문자 제거
	}
	return 0;
}

int check(char s[], char a[], char ch)
{
	int i;
	for(i=0; s[i] != NULL; i++) {
		if( s[i] == ch )
			a[i] = ch;
	}
	if( strcmp(s, a)==0 ) 
		return 1; // 정답과 일치하는지를 검사
	else return 0;
}

 

'C' 카테고리의 다른 글

c - pointer  (0) 2023.09.15

+ Recent posts