목록전체 글 (109)
나의 지식 보관소
const는 Constant를 줄인 말로서 상수라는 의미이다. 즉 변수 선언 앞에 붙여 주면 해당 변수는 상수가되어 값의 변경이 금지 된다. 그런데 포인터 변수에서는 아래의 코드와 같이const를 붙이는 위치에 따라 그 역할이 조금 차이가 있다. const int* ptr1 = #//ptr1을 이용해 ptr1이 가리키는 변수에 저장된 값을 변경하는 것을 금지한다. int* const ptr2 = #//ptr2를 상수로 만들어 ptr2의 값을 변경하는 것을 금지한다. //1번줄과 2번줄을 합친형태도 가능하다. const int* const ptr3 = #//num에 저장된 값의 변경과 ptr3의 값을 변경하는것을 금지한다.
문자열을 선언하는 데에는 배열을 이용한 방법과 포인터를 이용한 방법 두 가지가 있다. char str1[] = "Hello World"; char* str2 = "Hello C"; 배열을 이용한 방법은 변수 형태의 문자열이다. 즉 문자열을 일부를 변경할수있다. 하지만 포인터를 이용한 문자열 선언은 상수 형태의 문자열이다. 즉 문자열의 내용의 변경이 불가능하다. 하지만 포인터를 이용한 문자열의 선언에서는 포인터가 다른 곳을 가리 킬 수 있다. 하지만 배열의 이름은 상수 형태의 포인터 변수이기 때문에 다른 곳을 가리키지는 못한다.
포인터 변수를 대상으로 +연산과 -연산을 할 수 있다. 중요한 것은 +1을 한다고 해서 간단히 1만 더해지지는 않는다는 것이다. int형 포인터에 1을 더하면 4가 증가하고 double형 포인터에 1을 더하면 8이 증가한다. 즉 간단히 말해서 type 형 포인터 변수에서의 n을 더한다는 것의 의미는 n*sizeof(type)의 크기만큼 더하겠다는 것과 같다. int arr[3] = { 1, 2, 3 }; printf("%d",*(arr+1));// printf("&d",arr[1]); 과 같은 결과가 나온다. 즉 int형 포인터의 경우 +1을 하면 4파이트 떨어진 메모리를 가리키게 되므로 배열에서 다음 요소를 가리키게 된다. 따라서 arr[i]과 *(arr+i)는 같다!
우리가 흔히 사용하는 배열의 이름은 포인터 상수이다. 말 그대로 값을 바꿀 수 없는 포인터이다. 그렇다면 배열의 이름은 어디를 가리킬까? 배열의 이름이 가리키는것은 배열의 시작 주소 값이며, 이는 배열의 첫 번째 요소의 주소 값과도 같다. 앞서 말한 내용을 종합해보면 포인터와 배열은 상수이냐 변수이냐의 차이외에는 같다는 것을 알 수 있다. 따라서 배열에 *연산을 할 수 있고 반대로 포인터에 [ ] 연산을 할 수 있다. int arr[3] = { 1, 2, 3 }; int* ptr = arr; print("%d",ptr[1]); 따라서 이러한 활용도 가능하다.
포인터란 포인터는 메모리의 주소를 저장하는 변수이다. type형 변수의 주소 값을 저장하는 포인터 변수의 선언 방법은 아래와 같다. type* ptr 여기서 의문이 들수 있다. 주소 값을 저장하는 변수라면 그 크기가 모두 같을 텐데 왜 굳이 서로 다른 포인터형을 쓸까? 대답은 참조를 위해서이다. 만일 주소값만 주어졌다면 우리는 그 값을 어떻게 읽어 들여야 할까? 주어진 주소 값을 시작으로 4바이트를 읽어야 할까? 8바이트를 읽어야 할까? 이렇듯 포인터형은 메모리 공간을 참조하는 기준이 된다. 널포인터 포인터를 사용할 때 선언만 해두고 초기화를 하지 않는다면 포인터에는 쓰레기 값이 존재하게 된다. 이 상태에서 역참조 연산자를 통해 값을 변경하게 되면 어딘지도 모르는 메모리 공간을 변경해버리게 된다. 이러한..
추상클래스는 인터페이스와 클래스 사이쯤 되는 친구다. 클래스처럼 구현을 가질수있지만 인터페이스처럼 인스턴스를 가질수는 없다. 추상클래스의 특징은 추상 메서드를 가질수 있다는 점이다. 추상메서드는 구현을 갖지는 않지만 자식 클래스에서 구현하도록 강제한다. 다만 추상클래스를 다른 추상클래스가 상속 하는 경우에는 부모 추상클래스의 추상메서드를 구현하지 않아도 된다. 추상 클래스와 추상 메서드는 다음과 같이 abstract 수식을 붙여 선언한다 1 2 3 4 5 6 7 8 9 10 11 12 abstract class Parent { public abstract void Something(); } class Son : Parent { public override void Something() { //구현부 } }

죽음의 다이아몬드란 하나의 부모 클래스를 두 개의 자식 클래스가 상속받고, 이 두 개의 자식 클래스를 다시 하나의 자식 클래스가 상속하는 것을 말한다. 다음 그림에서 혼종은 "멍!"하고 짖을까? "냥!"하고 짖을(?)까? 이러한 모호성이 죽음의 다이아몬드 문제의 핵심이다. 때문에 c#은 클래스의 다중상속을 허용하지 않는다. 하지만 인터페이스는 구현을 생략한 선언만 물려주기 때문에 이러한 문제가 발생되지 않아 인터페이스는 다중 상속이 가능하다.
인터페이스는 클래스가 따라야 하는 약속이다. 마치 플러그를 통해 전원을 공급받기 위해 따라야만 하는 전기 플러그 규격처럼 인터페이스를 상속받기위해 클래스들은 인터페이스에 선언된 모든 메서드를 구현해야 한다. 인터페이스는 메서드, 이벤트, 인덱서, 프로퍼티 등을 선언만 할 수 있고 구현부를 가질 수 없다. 또한 인스턴스를 가질 수도 없다. 하지만 자식 클래스의 인스턴스를 참조할 수 있다. 자식 클래스의 인스턴스를 참조하게 되면 인터페이스는 자신에게 선언되어있는 메서드를 호출할 수 있고 작동은 참조한 자식 클래스에 구현되어있는 대로 작동한다. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 interface Animal { void Breath(); } class Dog : Animal..