Computer Science/C

C언어 6강 | Arrays, Pointers, and Strings | 중요!

토마토. 2021. 12. 16. 22:55

One dimensional arrays

 

array 배열

선언하는 법 : type array_name[size];

 

array initialization 배열 초기화

array는 storage class가 auto, external, static일 수 있다(register는 안됨)

auto인 경우에는 선언만 해주면, 쓰레기값이 들어있으므로꼭 초기화를 해주어야 한다. 

float f[5] = {0.0, 1.0, 2.0, 3.0, 4.0};
int a[100] = {0};
int a[] = {2,3,5,-7};

array subscripting 배열 첨자

a[i]로 배열 요소에 접근한다. 

 

sorting

배열을 이용하여, 4,-681,52,-23,15,9,13을 정렬하는 프로그램을 짜보자. 

1) bubble sort program

우선 가장 코드가 간단한 bubble sort부터!

int bubbleSort(int a[], int n){
    for (int i =0;i<n;i++){
        for (int j = i;j<n;j++){
            if (a[i]>a[j]){
                int tmp = a[i];
                a[i] = a[j];
                a[j] = tmp;
            }
        }
    }
    for (int i=0;i<n;i++){
        printf("%d\n", a[i]);
    }
    return 0;
}

2) selection sort algorithm

int selectionSort(int a[], int n){

    for (int i=0;i<n;i++){
        int tmp = a[i];
        for (int j=i;j<n;j++){
            if (a[j]<a[i]){
                tmp = a[j];
            }
        }
        a[i] = tmp;
    }

    for (int i=0;i<n;i++){
        printf("%d\n", a[i]);
    }
    return 0;
}

 

pointer

& : 주소를 나타내는 단항 연산자

포인터 변수는 값으로 주소를 가져간다.

포인터 변수의 선언은 int *p; 와 같은 형식으로 이루어진다. 

 

선언 : type *pointer_name;

할당 : pointer_name = &variable;

조작 : *pointer_name = value;

-> *pointer_name은 해당 메모리 주소에 저장된 값을 의미한다. 

 

특이한 사례)

포인터 변수에 직접 특정한 상수를 부여할 수도 있다. 

int *p;
p=0;
p=NULL; // == p=0;
p=&i;
p = (int*) 1776; // 메모리의 진짜 주소값 like 관악로1번지

용례)

 

int a =1, b=2, *p;
p = &a;
b = *p;

 

#include <stdio.h>

int main(void){
    int i=7, *p = &i;
    printf("Value of i:%d\nLocation of i:%p",*p, p);
    return 0;
}

 

허용되는 경우

포인터 타입이 서로 다른데 할당하려고 하는 경우

int *p = 0; // void인 경우와

int *p = (int*) 1; // constant인 경우와

void *v; int *p = v; // void 포인트인 경우 허용된다. 

 

 

call by reference

기본은 call by value인데,

포인터 변수를 이용하면 call by reference를 수행할 수 있다. 

 

 

arrays and pointers

#include <stdio.h>
void swap(int *, int*);
int main(void){
    int i=3, j=5;
    printf("%d %d\n", i, j);
    swap(&i, &j);
    printf("%d %d\n", i, j);
    return 0;
}

void swap(int *p, int *q){
    int tmp;
    tmp = *p;
    *p = *q;
    *q = tmp;
}

 

배열도 포인터와 같이 취급할 수 있다.

a[i] = *(a+i)

*(p+i) = p[i]

p = a; <=> p = &a[0];

p = a+1; <=> p = &a[1];

 

int a[N]과 같이 정의된 배열은 상수 포인터와 같다?

 

pointer arithmetic and element size

pointer arithmetic

포인터가 특정한 type으로 선언된 경우,

p+1, p+i, ++p, p+= i와 같은 산술적인 조작이 가능하다. 

double a[2], *p, *q;
p = a;
q = p+1;
printf("%d\n", q-p);
printf("%d\n", (int)q - (int)p); // 주소의 차이 == sizeof(double) == 8

 

q - p

array element의 개수를 보여준다.

 

arrays as function arguments

이거 선언하는 방법이 어렵다. 

그 다음에는 똑같음

기본적으로 call by value

double sum(double a[], int n)
{
	int i;
    double sum = 0.0;
    for (i=0;i<n;++i)
    	sum += a[i];
    return sum;
}
void main(void)
{
	double v[100];
    sum(v,100);
	sum(&v[50], 50);
    sum(v+7, 3);
}

pointer 변수를 인자로 넣으면, call by reference가 된다. 

double sum(double *a, int n)
{
	// ...
}

 

bubble sort

using pointer

#include <stdio.h>
#include <stdlib.h>

void bubbleSort(int *, int);
void swap(int *, int *);
void print(int *, int);

int main(void){
    int *a, n;
    n = 12;
    int list[] = {10,20,30,1,2,3,4,5,6,2,1,13};
    a = list;

    bubbleSort(a, n);
    print(a, n);

    return 0;
}

void bubbleSort(int *a, int n){
    for (int i=0;i<n;i++){
        for (int j =i;j<n;j++){
            if (*(a+i)>*(a+j)){
                swap((a+i), (a+j));
            }
        }
    }
}
void swap(int *a, int *b){
    int *tmp = (int*)malloc(sizeof(int));
    *tmp = *a;
    *a = *b;
    *b = *tmp;
}

void print(int *a, int n){
    for (int i=0;i<n;i++){
        printf("%d ", *a);
        a++;
    }
    printf("\n");
}

dynamic memory allocation

stdlib.h 헤더 파일에 calloc(), malloc(), free() 함수가 있다.

보통 malloc을 많이 쓴다. free는 자꾸 까먹는데, 잘 쓰도록 하자. 

calloc()

ptr = calloc(n, sizeof(int);

연속적인 할당? (초기화 해줌)

char *p = (char*)malloc(sizeof(char)*10));
char *p = (char*)calloc(10,sizeof(char));

malloc()

ptr = malloc(n*sizeof(int));

메모리 할당 (초기화 안해줌)

free(ptr);

할당해준 메모리 공간을 해제한다. 

 

merge sort

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>

void merge(int a[], int b[], int c[], int m, int n);
void mergeSort(int key[], int n);
void wrt(int key[], int sz);

void merge(int a[], int b[], int c[], int m, int n){
    int i=0, j=0, k=0;
    while (i<m&&j<n){
        if (a[i]<b[j]){
            c[k++]=a[i++];
        }
        else{
            c[k++]=b[j++];
        }
    }
    while (i<m) c[k++]=a[i++];
    while (j<n) c[k++]=b[j++];
}

void mergeSort(int key[], int n){
    int j, k, m, *w;
    for (m=1;m<n;m*=2);
    if (n<m) exit(1);
    w=calloc(n, sizeof(int));
    assert(w!=NULL);
    for (k=1;k<n;k*=2){
        for (j=0;j<n-k;j+=2*k){
            merge(key+j, key+j+k, w+j, k, k);
        }
        for (j=0;j<n;++j) key[j] = w[j];
    }
    free(w);
}

void main(void){
    int sz;
    int key[] = {4,3,1,67,55,8,0,4,-5,37, 7,42,9,1,-1};
    sz=sizeof(key)/sizeof(int);
    printf("\nBefore : ");
    wrt(key, sz);
    mergeSort(key, sz);
    printf("\nAfter");
    wrt(key, sz);
}

void wrt(int key[], int sz){
    for (int i=0;i<sz;i++){
        printf("%d ", *(key+i));
    }
    printf("\n");
}

 

strings

문자열은 char type의 1차원 배열과 같다. 

문자열의 끝을 표기하기 위해서 '\0'가 추가된다(Null character)

string 문자열 상수는 포인터처럼 취급할 수 있다. 

char *p = "abcde";

 

 

count the number of words in a string

scanf 대신 fgets를 사용하자. 

fgets(저장할 배열, 배열의 최대 크기, stdin);

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

int main(void){
    char s[100];
    fgets(s, 100, stdin);
    int cnt = 0;
    char *str = s;

    while (*str!='\0'){
        while (isspace(*str)){
            str++;
        }
        if (*str!='\0'){
            cnt++;
        while (!isspace(*str)&&*str!='\0'){
            str++;
        }
        }
    }
    printf("%d\n", cnt);
    return 0;
}

string handling functions in standard library

string.h에 정의되어 있다.

- strcat -

원형 : char *strcat(char *s1, const char *s2);

s1 뒤에 s2를 붙인다. concat

- strcpy -

원형 : char *strcpy(char *s1, const char *s2);

s1을 s2에 복사 붙여넣기 한다('\0'까지 함께!)

- strlen -

원형 : size_t strlen(const char *s); // 형변환 필수**

number of characters before '\0'(띄어쓰기 있으면 못 세주니까 주의 - fgets를 이용하자!)fgets(저장할 배열, 배열 크기, stdin)이면 끄떡없다!

 

size_t strlen(const char *s)
{
	size_t n;
    for (n=0;*s!='\0'; ++s) ++n;
    return n;
}

char *strcpy(char *s1, const char *s2)
{
	char *p = s1;
    while (*p++=*s2++);
    return s1;
}

char *strcat(char *s1, const char *s2)
{
	char *p = s1;
    while (*p) ++p;
    while (*p++ = *s2++);
    return s1;
}

 

multidimentional arrays

기본적으로

int a[2][7];

double c[5][3][2];

와 같은 방식으로 다차원 배열을 선언할 수 있다. 

a[i][j]로 접근 가능함

 

storage mapping function

그냥.. 명시적으로 다 쓰자 ^^

 

typedef 

#include <stdio.h>
#define N 3
typedef double scalar;
typedef scalar vector[N];
typedef scalar matrix [N][N];

void add(vector x, vector y, vector z);
scalar dot_product(vector x, vector y);
scalar multiply(matrix a, matrix b, matrix c);

int main(void){
    vector x = {1,2,3};
    vector y = {2,3,4};
    vector z = {0};

    add(x,y,z);
    printf("%f %f %f\n", z[0], z[1], z[2]);
    printf("%f\n", dot_product(x, y));


    return 0;
}

void add(vector x, vector y, vector z){
    z[0] = x[0] + y[0];
    z[1] = x[1] + y[1];
    z[2] = x[2] + y[2];
    
}
scalar dot_product(vector x, vector y){
    scalar tmp =0;
    tmp += x[0] * y[0];
    tmp += x[1] * y[1];
    tmp += x[2] * y[2];
    return tmp;
}
scalar multiply(matrix a, matrix b, matrix c){
    /* a = b * c */
    int i, j, k;
    for (i=0;i<N;++i){
        for (j=0;j<N;++j){
            a[i][j] = 0.0;
            for (k=0;k<N;++k){
                a[i][j] += b[i][k] * c[k][j];
            }
        }
    }
}

 

arrays of pointers

좀 긴 부분임

 

arguments to main()

argc, argv를 받음

#include <stdio.h>
int main(int argc, char *argv[]){
    int i;
    printf("argc  = %d\n", argc);
    for (i=0;i<argc;++i)
    printf("argv[%d]=%s\n", i, argv[i]);
    return 0;
}
$ ./e my_echo a is for apple
argc  = 6
argv[0]=./e
argv[1]=my_echo
argv[2]=a
argv[3]=is
argv[4]=for
argv[5]=apple

int main(int argc, char * argv[])를 가져가자..

 

ragged arrays

크기가 다른 배열들을 수용하는 포인터의 배열 (2차원 배열)

#include <stdio.h>

int main(void){
    char a[2][15] = {"abc:", "a is for apple"};
    char *p[2] = {"abc:", "a is for apple"};

    printf("%s%s\n", a[0], a[1]);
    printf("%s%s\n", *p, *(p+1));
    return 0;
}

 

function as arguments

이걸.. 꼭.. 지금 봐야할까?

 

function pointers

이걸.. 꼭.. 지금 봐야할까?