Programming Language/C
[C] Call By Value
[C] Call By Value
2022.04.13#include #include int main() { int a = 10; int b = 20; int c = add(a,b); } int add(int n1, int n2){ int n3; n3 = n1 + n2; return n3; } 위의 코드처럼 함수를 호출하면, a와b에 대한 값은 변화 없이 지역변수에 복사만 하고 로직을 수행한다. 이를 Call By Value라고 한다. CBV를 사용해 코드를 작성하면, 값들을 재사용하기 좋고, 디버깅 할 때도 집중해서 관찰해야 하는 값이 한정되기 때문에 편하다. Java로 함수를 작성할 때도 CBV형식으로 작성하는 경우가 많았을 것이다. 함수에서 값 대신 포인터를 입력받도록 하면, main함수의 변수에 대해서도 값을 바꿀 수 있다. C에서 입력은 scanf..
[C] 포인터와 함수
[C] 포인터와 함수
2022.04.13배열을 할당받은 변수명은 포인터 타입이다. 함수를 통해 배열을 전달하면, 배열 자체가 전달될까 아니면 포인터가 전달될까? 함수의 인자로 배열을 입력하면 주소 즉, 포인터가 복사돼 전달된다. 지난번에 스택프레임에 대해 배울 때 메모리 스택에서 활성화 된 값들만 조작할 수 있다고 배웠는데, 포인터를 사용하면 비활성화된 스택 프레임에도 접근할 수 있다. 그런데, 배열을 포인터로만 전달하게 되면 배열의 길이에 대한 정보는 전달하지 못한다. 배열에 있는 요소들의 합을 구하는 함수를 작성하고 싶은데, 이런 경우는 어떻게 해야 할까? 함수로 배열을 넘겨줄 때 배열의 길이를 함께 지정해 줄 수 있다. 일단 배열을 받는 함수는 정수 포인터 타입의 변수로 받아오고, 배열의 길이에 대한 정보인 len을 추가로 받는다. 물론..
[C] 문자열과 포인터
[C] 문자열과 포인터
2022.04.01지난번에 배웠듯 C에서 문자열을 표현할 때는 char타입의 배열을 사용해 마지막에 null (\0)문자를 붙이는 것으로 표현할 수 있다. 그런데 포인터 변수로도 배열을 표현할 수 있다면, 문자열도 포인터 변수로 표현할 수 있지 않을까? 표현할 수 있다. 하지만 첫 번째 방법과 두 번째 방법은 차이점이 있다. char * s1 = "your team"; 처럼 포인터 변수를 사용해 문자열을 표현하면, 문자열을 새로 할당하는 것도 가능하고, 문자열이 아니라 char의 주소를 할당하는 것도 가능하지만, 문자열의 일부분을 인덱스를 통해 변경하려고 하면 오류가 발생한다. 이는 자동 할당된 문자열에 대한 규칙으로 받아들여야 한다. char타입의 배열로 문자열을 선언하게 되면 아까와는 반대되는 상황이 발생한다. 배열 ..
[C] 포인터 연산
[C] 포인터 연산
2022.04.01포인터도 값이니 사칙연산이나 증감 연산자를 적용할 수 있지 않을까? #include #include int main() { int num = 1234; int *p = # printf("%p\n", p); printf("%p", p+1); } 포인터p의 값이 1000이라고 했을 때 p+1은 1001이 될까? 포인터 변수에 대해서도 덧셈과 뺄셈은 수행할 수 있지만, 정수 변수의 덧셈과는 다른 양상을 보인다. 변수의 타입에 따라 연산의 단위가 달라진다. 주소를 값으로 바꿔주는 * 연산자에 대해서 덧셈과 뺄셈을 수행해도 직관적인 결과를 얻을 수 있다. 배열과 같은 양상으로 연산을 수행한다. 당연하겠지만, *(ptr) +1을 출력하면 12가 출력되고, 배열 범위를 벗어나면 오류 대신 쓰레기값이 출력된다...
[C] 배열과 포인터
[C] 배열과 포인터
2022.04.01자바에서의 배열과 C에서의 배열은 성격이 좀 다르다. 일단, 배열을 선언한 변수 이름은 배열의 시작 주소를 의미하는 포인터이다. ( arr = &arr[0] ) 자바에서는 arr을 객체로 저장해 arr자체로는 뭐 할 수 있는게 많이 없지만, C에서는 그 자체로도 의미하는 바가 있다. 그러면, 변수명이 배열의 시작 주소를 의미한다고 했으니.. 특정 주소를 할당할 수도 있지 않을까? 라고 생각할 수도 있지만, 할당은 불가능하다. 포인터변수와 배열의 변수명은 굉장히 유사하지만, 배열의 변수명에다가 값을 새롭게 할당할 수는 없다. (읽기는 가능) int main() { int arr[3] = {0, 1, 2}; arr[0]++; printf("%d \n", *arr); // 1 printf("%d \n", ar..
[C] 포인터
[C] 포인터
2022.03.28포인터는 메모리 공간의 주소를 의미한다. 포인터만 따로 처리하는 타입으로 포인터 타입이 있다. ( * 로 표시하고, "%p"를 사용 ) 변수를 저장할 때 처음부터 포인터와 값을 함께 기록하면 좋지만, 저장할 때 메모리 공간이 따로 들어가고 매번 값을 확인해야 하기 때문에 잘 사용하지 않는다. 대신 크기별로 전담 변수를 둬 데이터의 타입으로 덩어리의 크기를 표현하는 방법을 사용한다. (int타입이면 int포인터, char타입이면 char포인터) int형 포인터 ptr1에 대해서 주소로 접근해 데이터를 읽을 때는 4byte를 한 덩어리로 보고 읽어온 값은 int형으로 처리한다. char형 포인터 ptr2에 대해서 주소로 접근해 데이터를 읽을 때는 1byte를 한 덩어리로 보고 읽어온 값은 char형으로 처리..
[C] 문자열
[C] 문자열
2022.03.28C에서는 boolean타입도 없지만 String타입도 없다. boolean대신 int타입을 대신해서 사용했고, String타입 대신 char타입의 배열로 String을 표현한다. char배열과 문자열의 구분은 문자열의 마지막에 '\0'의 유무로 판단한다. char배열의 끝에 모두 0으로 채우는 '\0'이 있으면 문자열로 인식하고, 그렇지 않으면 char배열로 인식한다. '\0'은 숫자로 해석 시 0이고, char로 해석 시 null로 해석하면 된다. char배열에서 '\0'을 만나면 뒤에 남아있는 요소에 상관없이 해석을 끝마친다. 즉, 널문자를 이용하면 문자열을 파싱할 수 있다. char str[10]; scanf("%s", str); printf("%s", str); scanf를 사용해 입력받을 때 문..
[C] 배열
[C] 배열
2022.03.28C에서는 자바와 배열의 선언 방식이 다르다. int arr[100]; 자바에서는 타입 뒤에 []가 와도 괜찮았지만, C에서는 변수명 뒤에 []를 붙여야 한다. 또, C에서는 객체지향 개념이 적용되지 않아서 저렇게 선언 후 바로 배열의 요소에 접근할 수 있다. int arr[100]; arr[1] = 12; 자바에서는 배열을 객체로 다루기 때문에 메모리 공간에 대해서 쉽게 이해할 수 있었다. 반면 C에서는 배열에 대해 생각해야 할 부분이 좀 있다. &는 주소를 의미한다고 보면 된다. rabbit[2] = 3; 같은 코드를 실행할 때, 배열의 시작인 rabbit[0]의 시작 부분(주소로는 100)에서 int사이즈의 크기인 4와 2를 곱한 값을 더해서 주소를 찾고, 값을 넣어준다. C에서 배열은 연속된 메모리..
[C] 함수 간 소통 / 재귀
[C] 함수 간 소통 / 재귀
2022.03.22소통 전역변수를 이용하면 함수들 간에 정보를 교환할 수 있다. 하지만... 전역변수와 지역변수의 이름이 같을 때는 전역변수에 접근할 수 없고, 어떤 함수가 전역변수에 접근했는지 판단하기 어렵기 때문에 디버깅도 어렵다. 그러므로 두 함수 사이에서 소통하는 방법이 합리적이다. 지역 변수를 복사해서 건네주고, 또 리턴 값을 돌려받는 방식으로 함수들 간의 소통을 구현할 수 있다. 이 때 리턴할 수 있는 인자는 하나뿐이기에 이 부분에 주의해서 코드를 작성하자. 재귀 알고리즘 문제를 풀 때 분할 정복 방법으로 푸는 경우가 많다. 이 때 자신을 호출하는 재귀함수를 많이 사용한다. 속도와 성능 측면에서는 불리하지만, 코드를 간결하게 작성할 수 있을 경우 사용한다. 재귀함수는 base case부분과 재귀 호출 부분으로 ..
[C] Register / Volatile
[C] Register / Volatile
2022.03.22Register CPU가 자신과 가장 가깝게 가지고 있는 기억장치를 Register라고 한다. 메모리에 저장된 변수보다 레지스터에 저장된 변수를 읽어오는 것이 훨씬 빠르다. 빠르니까 다 레지스터에 넣으면 좋겠지만... 레지스터에 넣을 수 있는 변수는 한정되어있다. 레지스터에 어떤 변수를 넣을지는 컴파일러가 결정하고, 컴파일러에게 특정 변수를 레지스터에 넣어놓으라고 말할 때 register키워드를 사용한다. (모든 레지스터가 사용중이면 할당받지 못할 수 있다.) 레지스터에 있으면 좋은 변수로는 for문 안에서만 사용되는, 증감만 반복하는 변수들이 있다. i, j, count 등등.. Volatile int ready = 0; while(!ready){ printf("asdf\n"); } 이런 프로그램은 그..
[C] Scope
[C] Scope
2022.03.22함수와 여러 문법을 중첩해서 사용할 때, 변수의 이름이 겹치는 경우가 많아 내가 어떤 변수를 사용하게 될 지 헷갈리는 경우가 많이 발생한다. void func1(){ int i =0; while(1){ int k = 100; int i =1; while(1){ int i = 2; while(1) { printf("%d %d", k, i); // 어떤 k 이고 어떤 i 일까? } } } } 바로 위와 같은 경우인데, 이 경우 가장 가까운 Scope부터 변수를 탐색한다. 위에서는 k에는 100, i에는 2가 해당된다. 이를 Scoping 규칙이라고 한다. 변수의 Scope와 Lifetime을 헷갈리면 안된다. 개념을 명확히 하고 가자. Lifetime은 시간적인 개념이고, Scope는 공간적인 개념이라고 이..
[C] 스택 프레임
[C] 스택 프레임
2022.03.22스택 자료구조는 나중에 들어간 요소가 먼저 나오는 LIFO형태를 갖춘 자료구조이다. 함수가 동작할 때 스택 구조로 동작하게 된다. 메모리구조와 함수에 대해 알아보자. 사실 Data Segment와 Code Segment부분은 함께 붙어서 하드디스크에 존재한다고 생각해도 된다. 전역변수와 정적변수는 서로 비슷한 성질을 가져 혼용되어 사용하기도 하지만, 정적변수는 전역변수와는 달리 초기화가 한 번만 발생한다. 즉, static키워드가 붙은 정적변수는 프로그램이 시작될 때 한 번만 초기화되며, 함수의 매개변수로 사용할 수 없다. 프로그램을 실행할 때 데이터는 Data Segment / Stack Memory / Heap Memory 세 구간으로 나뉘어 위치하게 된다. 여기서 Data Segment부분의 전역변..