Lecture 3 : Array, Structure, and Pointer

이은상·2023년 9월 18일

Array

a set of pairs of <index, element>

같은 타입의 multiple variables 생성

iteration code에서 효율적 프로그래밍 가능
주어진 인덱스에 corresponding element 매치됨

Structure

group data of different types

Self-Referntial Structure

has one or more pointers to itself in the field
often used in linked lists or trees

Applications of Arrays : Polynomials

1. Store all terms of a polynomial in an array

Store coefficients for all orders as an array
Express one polynomial as one array

  • pros : simplified polynomial operations
  • cons : causes wasteful space, when most of the coefficients are zero

Example

  1. 기본
typedef struct{
	int degree;		//최대계수
    float coef[MAX_DEGREE];
}polynomial;

polynomial = {5, {10,0,0,0,6,3}};
  1. addition operation of polynomial
#include <stdio.h>
#define MAX(a,b) (((a)>(b)> (a): (b)))
#define MAX_DEGREE 101

typedef struct{
	int degree;		//최대계수
    float coef[MAX_DEGREE];
}polynomial;

polynomial poly_add1(polynomial A, polynomial B){
	polynomial C;
    int Apos=0, Bpos=0, Cpos=0;
    int degree_a = A.degree;
    int degree_b = B.degree;
    C.degree = MAX(A.degree, B.degree);
    
    while(Apos <= A.degree && Bpos <= B.degree){
    	if(degree_a > degree_b){	//A의 차수가 큰 경우
        	C.coef[Cpos++] = A.coef[Apos++];	
            //A 최고차항 계수를 바로 C에 저장 후 pos + 1
            degree_a--;		//다음 차수의 항 계산 위해 1 감소
        }
        else if(degree_a == degree_b){	
        	C.coef[Cpos++] = A.coef[Apos++] + B.pos[Bpos++];
            degree_a--;	degree_b--;   //다음 차수의 항 계산 위해 1 감소
        }
        else{
        	C.coef[Cpos++] = B.coef[Bpos++];
            degree_b--;
        }
    }
    return C;
}

void main(){
	polynomial a = {5, {3,6,0,0,0,10}};
    polynomial b = {4, {7,0,5,0,1}};
    polynomial c;
    c = poly_add1(a,b);
}

2. Store only non-zero terms of a polynomial in an array

Store (Coefficients, orders) in an array
Ex) 10x⁴ + 6x + 3 -> ((10, 4), (6, 1), (3, 0))

Multiple polynomials can be represented by an array

  • pros : efficient use of memory space (meaningless data 저장 안해도 되므로)
  • cons : polynomial operations are complex

Addition operation example code

#define MAX_TERMS 101
struct{
	float coef;
    int expon;
}terms[MAX_TERMS] = {{8,3}, {7,1}, {1,0}, {10,3}, {3,2}, {1,0}};
int avail = 6;

char compare(int a, int b){
	if(a>b) return '>';
    else if(a == b) return '=';
    else return '<';
}

void attach(float coef, int expon){
	if(avail > MAX_TERMS){
    	fprintf(stderr, " Too many terms \n");
        exit(1);
    }
    terms[avail].coef = coef;
    terms[avail++].expon = expon;
        //++avail하면 안 됨 큰일 남 진짜!
}

//As: starting point of A, As: end point of A....
poly_add2(int As, int Ae, int Bs, int Be, int *Cs, int *Ce){
	float tempcoef;
    *Cs = avail;
    while(As <= Ae && Bs <= Be){   //while 후 남은 부분은 비교 없이 바로 attach
    	switch(compare(terms[As].expon, terms[Bs].expon)){
        case '>':
        	attach(terms[As].coef, terms[As].expon); As++; break;
        case '=':
        	tempcoef = terms[As].coef + terms[Bs].coef;
            if(tempcoef)
            	attach(tempcoef, terms[As].expon);
                As++; Bs++; break;
        case '<':
        	attach(terms[Bs].coef, terms[Bs].expon);
            Bs++;
            break;
        }
    }
    
    for(; As<Ae; As++)
    	attach(terms[As].coef, terms[As].expon);
    for(; Bs<Be; Bs++)
    	attach(terms[Bs].coef, terms[Bs].expon);
    
    *Ce = avail - 1;
}

void main(){
	int Cs, Ce;
    poly_add2(0,2,3,5,&Cs,&Ce);
}
for(; As<Ae; As++)
    attach(terms[As].coef, terms[As].expon);
for(; Bs<Be; Bs++)
    attach(terms[Bs].coef, terms[Bs].expon);

왜 1과 달리 이 부분이 추가되었을까 생각해보자.
1은 모든 항이 표현되어 있어 끝나는 지점이 같도록 설정되어 있었다. 따라서 계산 후 각각 다항식에 더 계산을 해야 할 항이 남아있지 않아서 이 부분이 필요 없었다.

Sparse Matrix

array를 사용하여 matrix를 나타내기 위한 두 가지 방법

  1. How to store all elements in a 2D array
  2. How to store only non-zero elements

Sparse matrix

대부분의 terms가 0인 행렬

Sparse Matrix Representation

1. How to store all elements in a 2D array

  • pros : matrix operations can be implemented simply
  • cons : memory is wasted when most terms are zero

-> good for the dense matrix

Example code of addition

#include <stdio.h>
#define ROWS 3
#define COLS 3

//addition
void sparse_matrix_add1(int A[ROWS][COLS], int B[ROWS][COLS], int C[ROWS][COLS]){
	int r, c;
    for(r = 0; r<ROWS; r++){
    	for(c = 9; c<COLS; c++){
        	C[r][c] = A[r][c] + B[r][c];
        }
    }
}

main(){
	int array1[ROWS][COLS] = {{2,3,0}, {8,9,1}, {7,0,5}};
    int array2[ROWS][COLS] = {{1,0,0}, {1,0,0}, {1,0,0}};
    int array3[ROWS][COLS];
    
    sparse_matrix_add1(array1, array2, array3);
}

2. How to store only non-zero elements

  • pros : memory is saved for sparse matrix
  • cons: complex implementation of matrix operations

-> good for the sparse matrix

zigzag scan을 통해 값을 저장

Example code of addition

#define ROWS 3
#define COLS 3
#define MAX_TERMS 10

typedef struct{
	int row;	//location
    int col;	//location
    int value;
}element;

typedef struct SparseMatrix{
	element data[MAX_TERMS];
    int rows;	//row size
    int cols;	//column size
    int terms;	//the number of element
}SparseMatrix;

//addition
SparseMatrix sparse_matrix_add2(SparseMatrix a, SparseMatrix b){
	SparseMatrix c;
    int ca=0, cb=0, cc=0;  //index indication terms in each array
    if(a.rows != b.rows || a.cols != b.cols){
    	fprintf(stderr, "Size error in Sparse matrix\n");
        exit(1);
    }
    c.rows = a.rows;
    c.cols = a.cols;
    c.terms = 0;
    
    while(ca < a.terms && cb < b.terms){
    	int inda = a.data[ca].row * a.cols + a.data[ca].col;
        //index of a
        int indb = b.data[cb].row * b.cols + b.data[cb].col;
        //밑에 사진 보고 이해하기
        
        if(inda < indb){
        	c.data[cc++] = a.data[ca++];
        }
        else if(inda == indb){
        	if((a.data[ca].value + b.data[cb].value) != 0){
            	c.data[cc].row = a.data[ca].row;
                c.data[cc].col = a.data[ca].col;
                c.data[cc++].value = a.data[ca++].value + b.data[cb++].value;
            }
            else {
            	ca++;
                cb++;
            }
        }
        else
        	c.data[cc++] = b.data[cb++];
    }
    
    //copy and paste the remaining terms
    for (; ca<a.terms;)
    	c.data[cc++] = a.data[ca++];
    for(; cb<b.terms;)
    	c.data[cc++] = b.data[cb++];
    
    c.terms = cc;
    return;
}

void main(){
	SparseMatrix m1 = {{{1,1,5}, {2,2,9}}, 3,3,2};
    SparseMatrix m2 = {{{0,0,5}, {2,2,9}}, 3,3,2};
    SparseMatrix m3;
    m3 = sparse_matrix_add2(m1, m2);
}


이 공식? 원리를 이용해서 인덱스 번호 생성!

Pointer

다른 변수의 주소를 나타내는 변수

char a = 'A';
char *p;
p = &a;     

위의 코드에서 p는 a의 주소이고, *p는 데이터(a = 'A')를 나타냄
역으로 a는 데이터이고, &a는 a의 주소를 나타냄

'&' : return variable address
'*' : return the contents of the pointer

포인터는 다른 변수의 주소값을 저장?하는 변수. 따라서 포인터 또한 그의 주소를 가지고 있음. 이러한 이유로 포인터의 주소값을 저장하는 포인터 변수를 또 지정할 수 있음.
ex)

int a;		//정수 변수 선언
int *p;		//정수 포인터 선언
int **pp;	//정수 포인터의 포인터 선언
p = &a;		//포인터 p와 변수a 연결
pp = &p;	//포인터 p와 포인터의 포인터 pp 연결

Pointer type

void *p;	//pointer to point null
int *pi;
float *pf;
char *pc;
int **pp;	//pointer to a pointer
struct test *ps;  //pointer to a structure of test type
void (*f)(int);	 //pointer to a function f with 'int' parameter

pointer casting

possible to cast whenever necessary

  • void pointer can be changed to different type of pointer
    -> 포인터를 어떤 타입에 사용할지 모를 때 사용하면 좋음

ex)

void *p;
pi = (int *)p;

Pointer as Fuction Parameter

It is possible to change the value of external variable by using pointer passed as parameter in function

code

void swap(int *px, int *py){
	int tmp;
    tmp = *px;
    *px = *py;
    *py = tmp;
}

main(){
	int a=1, b=2;
    printf("Before swap: a=%d, b=%d\n", a,b);
    swap(&a, &b);
    printf("After swap: a=%d, b=%d\n", a,b);
}

메모리에 a=1, b=2, px = address of a, py = address of b 존재함
주소를 전달하여 주소에 있는 값들을 swapping하도록 함
(복사해서 붙이기? 뭔 소리지..)

pointer를 사용하지 않는 경우 dummy value가 발생함
ex)

void swap(int a, int b){
	int x = a;
    int y = b;
    int tmp = a;
    x = y;
    y = tmp;
}

시작할 때 a=1 b=2 x=1 y=2
종료 후 결과 a=1 b=2 x=2 y=1
→ (a,b) and (x,y) are not sharing, just different value

Array and Pointer

Array name = pointer
the compiler replaces the array name with the first address in the array

Example code

#include <stdio.h>
#define ROWS 3

void vec_add1(int A[ROWS], int B[ROWS], int C[ROWS]){
	int r,c;
    for(r=0; r<ROWS; r++){
    	C[r] = A[r] + B[r];
    }
}

void main(){
	int array1[ROWS] = {2,3,0};
    int array2[ROWS] = {1,0,0};
    int array3[ROWS];
    vec_add1(array1, array2, array3);
}

2.

#include <stdio.h>
#define ROWS 3

void vec_add1(int *A, int *B, int *C){
	int r,c;
    for(r=0; r<ROWS; r++){
    	C[r] = A[r] + B[r];
    }
}

void main(){
	int array1[ROWS] = {2,3,0};
    int array2[ROWS] = {1,0,0};
    int array3[ROWS];
    vec_add1(array1, array2, array3); //address of first element
}

Structure Pointer

'->' : accesses elements of a structure

ps -> i = 2;     // == (*ps).i = 2;

Pointer of Pointer

포인터 또한 메모리 어딘가에 저장되므로 해당 주소를 저장하는 포인터를 또다시 선언할 수 있음
위에 이미 언급했었으니까 더 자세히는 위에 보면 됨

Pointer Operation

  • pointer variable + 1 은 다음 변수의 주소를 나타냄 (따라서 결과에서 표현하는 주소값은 변수의 타입의 바이트 크기를 더한 것)
*pi = A[3] // then,  *(pi + 1) == A[4]; *(pi - 1) == A[2]

*p++   //다음 변수의 주소를 가리킴
(*p)++  //변수의 값에 1을 더함

Example code

int a1 = 10;
int *p;
p = &a1;

printf("%d\n", p);		//5896696

int b = (*p)++;   //==10++

printf("%d\n", p);	   //5896696
printf("%d\n", *p);	   //11
printf("%d\n", b);	  //10
int a1 = 10;
int *p;
p = &a1;

printf("%d\n", p);		//5896696

int b = *p++;   //5896696 + 4(int byte)

printf("%d\n", p);	   //5896700
printf("%d\n", *p);	   //-868996460 == dummy value
printf("%d\n", b);	  //10
  • set to NULL when the pointer is pointing to nothing
int *pi = NULL;
  • do not use it when it is not initialized
main(){
	char *pc;	//pointer pc is not initialized
    *pc = 'E';	//not recommended
}
  • use explicit type conversion when coverting between pointer types
int *pi;
float *pf;
pf = (float *)pi;

Memory Allocation

How a program allocates memory

1. Static memory allocation
2. Dynamic memory allocation

Static memory allocation

  • 프로그램 시작 전 메모리의 크기가 고정되고, 실행동안 변경될 수 없음
  • an input, larger than the size initially determined, will not be processed
  • a smaller input will waste the remaining memory

Dynamic memory allocation

  • 프로그램 실행하는 동안 메모리 할당
  • allocate, use, and return as much as you need
  • very efficient use of memory

Example code)

main()
{
	int *pi;
    pi = (int *)malloc(sizeof(int)); // dynamic memory allocation
    .
    .           //use dynamic memory
    .
  free(pi);  //release dynamic memory
  
  // pi[10] == pi = (int *)malloc(sizeof(int)*10)
  • Related library functions
malloc(size)       // memory allocation
free(ptr)        // deallocate memory  
sizeof(var)    // return the size of the variable or type (in bytes)

Example Code

struct Example{
	int number;
    int name[10];
};

void main(){
	struct Example *p;
    
    p = (struct Example *)malloc(2*sizeof(struct Example));
    							//define example which contains two
    if(p == NULL){
    	fpintf(stderr, "can't allocate memory\n");
        exit(1);
    }
    
    p->number = 1;
    strcpy_s(p->name, "Park");	//==strcpy_s(p[0].name, "Park");
    (p+1)->number = 2;
    strcpy_s((p+1)->name, "PKim");
    
    printf_s ......
    printf_S ......
    free(p);
}

Memory Allocation of 2D Array

allocation

void main(){
	int row = 3;
    int col = 3;
    
    int **m2 = (int **)malloc(sizeof(int *)*row);
    for (int i = 0; i<row; i++){
    	m2[i] = (int *)malloc(sizeof(int)*col);
    }
    
    int count = 0;
    for(int i = 0; i<row; i++){
    	for(int j = 0; j<col; j++){
        	m2[i][j] = ++count;
            printf_s("%d\n", m2[i][j]);
        }
    }
}

same as

int count = 0;
for (int i = 0; i<row; i++){
	int *tmp = *(m2+i);   //row i's first element address
    for(int j =0; j <col; j++){
    	tmp[j] = ++count;
        printf_s("%d\n", tmp[j]);
    }
}

(m2+i) : row i's first element address
(m2+i) + j : row i, column j's first address

disallocation

if(m2 != NULL){
	for(int i = 0; i<row; i++){
    	free(m2[i]);
    }
    free(m2);
    m2 = NULL;
}

0개의 댓글