'프로그래밍'에 해당되는 글 8건

  1. 2013.02.25 사용자암호관련 by 초코송송이
  2. 2012.08.29 C#으로 오라클접속 by 초코송송이
  3. 2012.08.28 C#으로 MySql 접속 by 초코송송이
  4. 2012.08.28 C#으로 Active Directory 접근 by 초코송송이
  5. 2012.05.10 C#의 객체지향 by 초코송송이
  6. 2012.05.08 C#의 기본문법 by 초코송송이
  7. 2012.04.12 UDP서버-클라이언트 by 초코송송이
  8. 2012.04.02 [Delphi] String에 관하여.. by 초코송송이

사용자암호변경

ALTER USER user_name IDENTIFIED BY new_password;

user_name : 변경하고자 하는 사용자 명 
new_password : 새로운 암호

예) ALTER USER scott IDENTIFIED BY lion;

scott 사용자의 암호가 lion으로 바뀐다.


사용자 암호완료 확인

SQL>SELECT USERNAME, ACCOUNT_STATUS, LOCK_DATE, EXPIRY_DATE 

       FROM DBA_USERS 

       WHERE USERNAME='xxx';

ACCOUNT_STATUS에 EXPIRED이면 비밀번호 기간만료, OPEN이면 만료 해제, 숫자면 만료기간


사용자 비밀번호 만료 해제 SQL

sql>ALTER PROFILE DEFAULT PASSWORD_LIFE_TIME UNLIMITED;


Posted by 초코송송이
l

1.Oracle Data Access Components for Windows를 다운로드한다.

http://www.oracle.com/technetwork/indexes/downloads/index.html?ssSourceSiteId=ocomen

=>설치할 때, 필수항목으로 Instant Client가 설치되는데 이미 설치되어 있어도 다시 하도록 되어있었음..;;

2. using Oracle.DataAccess.Client;

string oradb = "Data Source=(DESCRIPTION="              
              + "(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.0.xxx)(PORT=1521)))"
                //+ "(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=orcl.xxxx.com)));"
                + "(CONNECT_DATA=(SERVER=DEDICATED)(SID=ORCL)));"
                + "User Id=user;Password=password;";//service_name은 전역데이터베이스이름
                       
using (OracleConnection conn = new OracleConnection(oradb))
{
    try
    {
        conn.Open();

        using (OracleCommand cmd = new OracleCommand())
        {
            cmd.Connection = conn;
            cmd.CommandText = "select * from " +  tablename;

            OracleDataReader dr = cmd.ExecuteReader();                                                             

            while(dr.Read())
            { 
                 MessageBox.Show(dr.GetString(1));

....

      }

      cmd.CommandText = "insert into " + tablename + " (column1, column2, column3)"
                                                       + " VALUES('" + val1 + "', '" + val3 + "', '" + val3 + "')";

      int Res = cmd.ExecuteNonQuery();
      if (Res == 1)
      {
          System.Diagnostics.Trace.WriteLine("insert 성공");
      }
      else
      {
          System.Diagnostics.Trace.WriteLine("insert 실패");
      }

        }

    }
    catch (OracleException ex) // catches only Oracle errors
    {
        switch (ex.Number)
        {
            case 1:
                MessageBox.Show("Error attempting to insert duplicate data.");
                break;

            case 12545:
                MessageBox.Show("The database is unavailable.");
                break;

            default:
                MessageBox.Show("Database error: " + ex.Message.ToString());
                break;
        }
    }

    catch (Exception ex) // catches any other error
    {
        MessageBox.Show(ex.Message.ToString());
    }
    finally
    {
         // Don't need conn.Close() because the using takes care of it.
    }
}

'프로그래밍 > C#' 카테고리의 다른 글

C#으로 MySql 접속  (0) 2012.08.28
C#으로 Active Directory 접근  (0) 2012.08.28
C#의 객체지향  (0) 2012.05.10
C#의 기본문법  (0) 2012.05.08
Posted by 초코송송이
l

1. mysql-connector-net 설치

http://www.mysql.com/downloads/connector/net/

2. Reference에 추가 - MySql.Data

3. 소스에 적용

using MySql.Data;
using MySql.Data.MySqlClient;

static string DBConnectString = "Data Source=192.168.0.xxx;Database=db_name;" + "User Id=userid;Password=password" + ";charset=utf8";

MySqlConnection Con = new MySqlConnection();
Con.ConnectionString = DBConnectString;
            
try
{
    Con.Open();
         
    MySqlCommand Command = new MySqlCommand("SELECT * FROM table_name", Con);
    MySqlDataReader DataReader;
    DataReader = Command.ExecuteReader();

    while (DataReader.Read())
    {
         MappingTable MappingT = new MappingTable();
         MappingT.MappingNumber = string.Format("{0}", DataReader["table_column1"]);
         MappingT.UserId = string.Format("{0}", DataReader["table_column2"]);
         MappingT.SIP = string.Format("{0}", DataReader["table_column3"]);
         MappingTList.Add(MappingT);
    }
    DataReader.Close();
    Con.Close();
}

catch (MySqlException ex)
{
    MessageBox.Show(ex.ToString());
}

'프로그래밍 > C#' 카테고리의 다른 글

C#으로 오라클접속  (0) 2012.08.29
C#으로 Active Directory 접근  (0) 2012.08.28
C#의 객체지향  (0) 2012.05.10
C#의 기본문법  (0) 2012.05.08
Posted by 초코송송이
l

1. Active Directory에 조직구성단위 추가

using System.DirectoryServices;

DirectorySearcher DSESearcher = new DirectorySearcher();
DirectoryEntries DEntries = DSESearcher.SearchRoot.Children;                
DirectoryEntry ou2 = DEntries.Add("OU=" + OrgName, "organizationalUnit");
ou2.CommitChanges();
ou2.Close();

2. 조직구성단위 열거

DirectorySearcher DSESearcher = new DirectorySearcher();
DirectoryEntries DEntries = DSESearcher.SearchRoot.Children;
List<string> listOU = new List<string>();

foreach (DirectoryEntry de in DEntries)
{
    if (de.Name.CompareTo("OU") > 0)
    {
        listOU.Add(de.Name.Remove(0, 3));//OU=구성단위이름

     }                
}    

3. 조직구성단위에서 사용자 추가

DirectorySearcher DSESearcher = new DirectorySearcher();
string RootDSE;
try
{
    RootDSE = DSESearcher.SearchRoot.Path;
}
catch
{
    System.Windows.MessageBox.Show("Active Directroy를 찾을수 없습니다.");
    return;
}

//조직구성단위 목록을 미리 가지고 와서 콤보박스에서 선택             
RootDSE = RootDSE.Insert(7, "OU="+ comboBoxOU.SelectedValue +",");
DirectoryEntry myDE = new DirectoryEntry(RootDSE);

//선택한 조직구성단위 아래에 사용자 추가

DirectoryEntry usr = myDE.Children.Add("CN=" + textBoxFName.Text + " " + textBoxGName.Text, "user");
 
//계정 - Windows 2000 이전버전 사용자 로그온 이름
usr.Properties["samAccountName"].Value = textBoxLogonName.Text;
//일반 - 표시이름
usr.Properties["displayName"].Value = textBoxFName.Text + " " + textBoxGName.Text;
//일반 - 이름
usr.Properties["givenname"].Value = textBoxGName.Text;
//일반 - 성
usr.Properties["sn"].Value = textBoxFName.Text; //family name
//계정 - 사용자 로그온 이름
usr.Properties["userPrincipalName"].Value = textBoxLogonName.Text + "@xxxxxx.com";
//조직 - 회사
if (textBoxCompany.Text.Length > 0)
    usr.Properties["company"].Value = textBoxCompany.Text;
//일반 - 전화 번호
if (textBoxTelNum.Text.Length > 0)
    usr.Properties["telephoneNumber"].Value = textBoxTelNum.Text;
//조직 - 직함
if (textBoxTitle.Text.Length > 0)
    usr.Properties["title"].Value = textBoxTitle.Text;
//조직 - 부서
if (textBoxDepartment.Text.Length > 0)
    usr.Properties["department"].Value = textBoxDepartment.Text;
//일반 - 전자 메일
if (textBoxMail.Text.Length > 0)
    usr.Properties["mail"].Value = textBoxMail.Text;

usr.CommitChanges();

usr.Invoke("SetPassword", textBoxPassword.Text);
//usr.Invoke("SetPassword", new object[] { textBoxPW.Text });

int val = (int)usr.Properties["userAccountControl"].Value;
usr.Properties["userAccountControl"].Value = val & ~0x2; //ADS_UF_NORMAL_ACCOUNT;
usr.CommitChanges();

myDE.Close();
usr.Close();

//(조직구성단위:OU, 사용자:CN)

'프로그래밍 > C#' 카테고리의 다른 글

C#으로 오라클접속  (0) 2012.08.29
C#으로 MySql 접속  (0) 2012.08.28
C#의 객체지향  (0) 2012.05.10
C#의 기본문법  (0) 2012.05.08
Posted by 초코송송이
l

1. 객체지향의 기본

 

1-1) 오버로딩(Overloading)

 

1) 오버로딩의 정의

- 하나의 이름으로 여러개의 함수를 만드는 기법

 

2) 오버로딩 함수를 만드는 규칙

- 반드시 매개변수의 개수와 매개변수의 형이 달라야한다.

 

3) 오버로딩 함수의 예

- public int plus(int a, int b) { ... }

- public float plus(float a, float b) { ... }

- public double plus(double a, double b) { ... }

 

1-2) 연산자 오버로딩(Operator Overloading)

 

1) 연산자 오버로딩(Operator Overloading)

- 연산자를 중복정의 하는 것: 일반적인 연산자를 생각하면 더하기 빼기 등 값에 대한 연산이다. 하지만 이러한 연산자를 객체들 사이의 연산으로 차원을 높여주는 것이 연산자 오버로딩이다.

 

2) 연산자 오버로딩의 예

 

class Top {

public int a = 0;

public int b = 0;

public Top(int a, int b) {

this.a = a;

this.b = b;

}

public static Top operator+(Top t1, Top t2) {

int m = t1.a + t2.a;

int n = t1.b + t2.b;

return new Top(m, n);

}

}

 

=> operator+ 함수의 개념적 호출

Top s1 = new Top(3, 5);

Top s2 = new Top(7, 16);

Top s3 = s1 + s2;

 

=> operator+ 함수의 실제 호출

Top s1 = new Top(3, 5);

Top s2 = new Top(7, 16);

Top s3 = s1 + s2;

 

◈ 오버로딩이 불가능한 연산자

- ?    .    =     is    new   sizeof    typeof    ||    []    ()    &&    ->    +=    -=    ?: 등

 

 

1-3) 상속(Inheritance) 

1) 상속(Inheritance) 

- 만들어둔 클래스를 다시 사용할수 있는 방법을 말한다.

 

2) 기본적인 상속 방법

public class HelloForm: Form {

..

}

 => HelloForm 클래스에서 Form 클래스를 상속받음

 

- 상위 클래스를 기본 클래스(Base Class)라고 하며, 하위 클래스(Sub Class)를 파생 클래스(Derived Class)라고 한다.

- 상속을 하면 상위 클래스의 능력을 하위 클래스에서 이용할 수 있다.

 

3) 상속 금지 키워드 sealed

public sealed class Father {

public void SayFather() {

..

}

}

 

=> Father 클래스는 상속을 할 수 없다.

 

1-4) 생성자

 

1) 생성자 함수

- 생성자 함수는 메모리가 생성된 직후 가장 먼저 호출되는 함수이다.

 

2) 생성자의 특징

- 생성자는 리턴타입이 없다.

- 생성자의 이름은 클래스의 이름과 동일하다.

- new 연산자가 힙 영역에 해당 클래스의 메모리를 생성한 직후 호출된다.

 

public class ConstructTest{

public ConstructTest() { // 생성자

     ..

}

}

 

1-5) 소멸자

 

1) 소멸자

- 객체의 메모리가 제거될 때 호출되는 함수

 

2) 소멸자의 특징

- 객체가 소멸되기 직전에 호출되어 객체에 부여된 메모리의 회수를 담당

- 실제적으로 메모리의 회수여부는 가비지 콜렉터가 담당한다.

 

public class DestructorTest {

public DestructorTest() {}

~DestructorTest() { //소멸자

Console.WriteLine("나는 소멸자");

}

public static void Main() {

DestructorTest a = new DestructorTest();

}

}

 

1-5) internal과 protected internal 접근자

 

1) 논리적 접근 제한자

- 기준: 클래스 계층 구조에서 접근을 제한

- public, private, protected

 

2) 물리적 접근 제한자

- 기준: 어셈블리 단위로 접근을 제한

- internal

 

3) internal 접근자

- 하나의 Assembly 내에서만 접근을 허락하는 접근자

- 같은 Assembly 안에 있어야 접근이 가능하다.

 

4) protected internal

- 어셈블리가 다르더라도 상속을 받은 하위 클래스에서 상위 클래스의 internal에 접근 가능하게 하기 위해서 protected internal을 제공한다.

 

1-6) 오버라이딩(Overriding) 

 

1) 오버라이딩의 종류

- new 키워드를 이용한 재정의

- virtual, override키워드를 이용한 재정의

 

2) virtual, override, new의 관계

- virtual : 하위 클래스에서 재정의해서 사용할 것을 표시

- override : 상위 클래스에서 virtual로 표시된 함수를 재정의 할 때 사용

- new : 상위 클래스의 함수를 완전히 무시할 때 사용

 

3) new 키워드를 이용하여 함수를 재정의한 후 업캐스팅 했을 때

- 업캐스팅 되었을 때 상위 클래스는 상위 클래스 내의 함수만을 호출한다.

 

class Base {

public void MethodA() {

Console.WriteLine("Base Method()");

}

}

class SimpleNew: Base {

new public void MethodA() {

Consol.WriteLine("SimpleNew Method()");

}

public static void Main() {

SimpleNew m = new SimpleNew();

m.MethodA(); // SimpleNew 메서드 호출

 

Base b = m;

b.MethodA(); // Base 메서드 호출

}

}

 

4) virtual 키워드를 이용하여 함수를 재정의 한 후 업캐스팅 했을 때

- 상위 클래스의 이름으로 하위 클래스의 함수를 호출하는 것이 된다.

 

class Base {

virtual public void MethodA() {

Console.WriteLine("Base Method()");

}

}

class SimpleNew: Base {

override public void MethodA() {

Consol.WriteLine("SimpleNew Method()");

}

public static void Main() {

SimpleNew m = new SimpleNew();

m.MethodA(); // SimpleNew 메서드 호출

 

Base b = m;

b.MethodA(); // SimpleNew 메서드 호출

}

}

 

1-7) this, 생성자를 호출하는 this()

 

1) this

- this란 자기자신을 참조하는 가상의 참조 변수이다. (this.멤버이름)

 

2) 생성자를 호출하는 this()

- 자신의 생성자를 호출할 때도 사용

- 자신의 생성자를 재사용하기 위해서 생성자를 호출하는 방법을 제공

 

3) this()를 사용하는 이유

- 생성자의 중복을 피하기 위해서

 

4) this() 생성자의 특징

- 생성자 내에서 this()를 호출한다면 this()를 이용한 생성자가 먼저 처리된 후 현재의 생성자가 처리된다.

 

using System;

public class ThisSelf {

private string name;

private int age;

public ThisSelf() : this("이름없음") {

Console.WriteLine("매개변수가 없는 생성자");

}

public ThisSelf(string name) : this(name, -1) {

Console.WriteLine("매개변수가 1개 있는 생성자");

}

public ThisSelf(string name, int age) {

this.name = name;

this.age = age;

Console.WriteLine("name: " + name + " number: " + age);

Console.WriteLine("매개변수가 2개 있는 생성자");

}

public static void Main() {

ThisSelf ts1 = new ThisSelf();

ThisSelf ts2 = new ThisSelf("홍길동");

ThisSelf ts3 = new ThisSelf("김삿갓", 50);

}

}

출력:

name: 이름없음 number: -1

매개변수가 2개 있는 생성자

매개변수가 1개 있는 생성자 

매개변수가 없는 생성자

name: 홍길동 number: -1

매개변수가 2개 있는 생성자

매개변수가 1개 있는 생성자

name: 김삿갓 number: 50

매개변수가 2개 있는 생성자

 

1-8) base 키워드, base()

 

1) base 키워드

- 재정의한 상위 클래스의 함수를 호출할 때 사용

 

using System;

public class NewFather {

public virtual void OverrideFunc() {

Console.WriteLine("아버지의 함수");

}

}

public class NewSon : NewFather {

public override void OverrideFunc() {

Console.WriteLine("아들의 재정의 함수");

}

public void TestFunc() {

base.OverrideFunc(); //NewFather의 메서드 호출

}

public static void Main() {

NewSon ns = NewSon();

ns.OverrideFunc(); // NewSon의 함수 호출

ns.TestFunc(); // TestFunc() 내에서 NewFather의 함수 호출

}

}

 

2) 상위 클래스의 생성자를 호출하는 base()

- 생성자 내에서 상위 클래스의 생성자를 호출하기 위해 사용한다.

- 상위 클래스 생성자에 매개변수가 존재하면 생성자의 매개변수의 형과 개수를 맞추어 주어야만 호출 가능하다.

 

using System;

public class BaseFather {

private stsring name;

public BaseFather(string name) {

this.name = name;

Console.WriteLine("BaseSon : {0}", name);

}

}

public class BaseSon : BaseFather {

public BaseSon(string str) : base(str) {

}

public static void Main() {

BaseSon s = new BaseSon("Base Test Problem");

}

}

출력:

BaseSon : Base Test Problem

 

 

2. 다형성

 

2-1) 다형성(Polymorphism)

1) 다형성(Polymorphism)이란?

- 다형성이란 하나로 여러가지의 일을 하는 것을 말한다.

 

2) 다형성이 적용되는 곳

- 오버로딩(Overloading)

- 오버라이딩(Overriding)

- 업캐스팅(Upcasting)

- 다운캐스팅(Downcasting)

- 추상 클래스(Abstract Class)의 상속 관계

- 인터페이스(Interface)의 상속 관계

 

3) 다형성을 지원하기 위한 도구

- 추상 클래스(Abstract Class)

- 인터페이스(Interface)

- 업캐스팅(Upcasting)과 다운캐스팅(Downcasting)

- 박싱(Boxing), 언박싱(UnBoxing)

- 대리자(Delegate)

 

2-2) Boxing & UnBoxing

1) Boxing과 UnBoxing

- Boxing : 값 타입을 참조타입으로 변환하는 기법

- UnBoxing : 참조타입을 값 타입으로 변환하는 기법

 

2) 박싱(Boxing)

- Boxing이란 값타입을 참조타입으로 변환하는 것을 말한다.

 

◈ Boxing의 예

int p = 123;

object o = p; //Boxing이 발생

 

 Boxing이 이루어지는 순서

- 값 타입 변수를 객체화하기 위한 메모리를 힙 역역에 생성한다. 이 공간을 Box라고 한다.

- p에 있는 값을 힙에 생성한 Box로 복사

- 참조타입 변수 o에 Box의 참조값을 할당

 

=> 값타입의 값을 object o에 담기 위해서 힙영역에 박스(Box)라는 것을 만들고, 이 박스에 값을 복사해서 넣어주면 모든 변환이 끝난다. 새로 생성된 참조값은 객체 o가 가지게 된다.

 

3) 언박싱(UnBoxing)

- UnBoxing은 Boxing된 참조타입을 다시 값 타입으로 변환하는 것을 말한다.

 

◈ UnBoxing의 예

int p = 123;

object o = p; //Boxing이 묵시적으로 이루어진다.

int j = (int)o; //UnBoxing 명시적으로 캐스팅해주어야 함

Console.WriteLine("j = " + j);

Console.WriteLine("p = " + p);

Console.WriteLine("o = " + o);

 

출력:

j = 123

p = 123

o = 123

 

◈ UnBoxing의 순서

- 해당 객체가 지정한 값타입을 Boxing 한 값인지 확인

- Boxing 된 객체라면 객체의 값을 값타입 변수에 복사한다.

- Boxing 한 메모리와 UnBoxing한 메모리 두개가 존재한다.

2-3) 구조체의 Boxing과 클래스의 형변환

 

1) 구조체의 Boxing

 

using System;

struct Point {

public int x, y;

public Point(int x, int y) {

this.x = x;

this.y = y;

}

}

class BoxingTest2 {

public static void Main() {

Point p = new Point(10, 10); // 스택 메모리 생성 - 값타입

object box = p;                  // 힙 메모리 생성 - Boxing 발생

p.x = 20;

Console.Write((Point)box).x);

//Boxing될 때 스택의 메모리가 힙영역으로 메모리가 복사되고 스택 메모리는 변환없음

}

}

출력:

10

2) 클래스의 형변환(업캐스팅)

 

using System;

class Point {

public int x, y;

public Point(int x, int y) {

this.x = x;

this.y = y;

}

}

class BoxingTest3 {

public static void Main() {

Point p = new Point(10, 10); // 힙 메모리 생성

object box = p;               // 최상위형으로 Upcasting된다. Boxing과 UnBoxing과 무관

p.x = 20;

Console.Write((Point)box).x);

}

}

출력:

20

 

=> 클래스를 이용한 객체는 참조변수이므로 당연히 p의 값은 참조값 자체이다. 그리고 object box에 이 참조값을 할당하면 box는 p의 참조값을 할당받는다. 즉, 객체의 참조할당의 법칙이 적용되는 것이지, Boxing과 UnBoxing의 변환이 아니다.

 

2-4) 인덱서 (Indexer)

 

1) 인덱서 (Indexer)

- 클래스의 객체나 구조체의 변수를 배열형식으로 이용할 수 있게 해주는 기법이다.

 

2) 인덱서를 구현하는 예제

- 문자열을 얻어내고 지정하기 위해 인덱서 사용

 

using System;

using System.Collections;

class SimpleIndexer {

ArrayList lname = new ArrayList();

public object this[int index] {

get {

if (index > -1 & index < lname.Count) { //범위내에 있는것

return lname[index];

} else {

return null;

}

}

set {

if (index > -1 & index < lname.Count) { // 범위내에 있는 것은 변경됨

lname[index] = value;

} else if (index == lname.Count) { //순서대로 하나씩 추가하면 됨

lname.Add(value);

} else {

Console.WriteLine("sid[" + index + "] : 입력범위 초과 에러!!");

}

}

}

}

class IndexerTest {

public static void Main() {

SimpleIndexer sid = new SimpleIndexer();

sid[0] = "hong";

sid[1] = "kim";

sid[2] = "sung";

 

Console.WriteLine(sid[0]);

Console.WriteLIne(sid[1]);

Console.WriteLine(sid[2]);

sid[10] = "park"; // 범위초과

}

}

출력;

hong

kim

sung

sid[10] : 입력범위 초과 에러!!

 

2-5) 추상 클래스 (Abstract Class)

 

1) 추상 클래스가 되는 방법

- 몸체없는 함수를 하나라도 포함하고 있는 클래스

abstract class Sample1{

public abstract void F();

}
- 몸체없는 함수를 포함하고 있지 않더라도 클래스를 선언할 때 abstract 키워드를 포함하고 있는 경우

abstract class Sample2{

public void G() {}

}

 

2) 추상함수의 선언 형태

- public abstract void F();

 

3) 추상 클래스의 특징

- 추상 클래스는 객체(인스턴스)를 생성할 수 없다.

- 추상 함수는 자동으로 가상함수가 된다. (추상 함수는 virtual 키워드를 사용하지 않지만 자동으로 virtual이 된다.)

 

 

2-6) 추상 함수 (Abstract Method)

 

1) 추상함수(Abstract Method)

- 몸체가 없는 함수

- 프로토타입만 가지고 있음

 

2) 추상 클래스를 상속받으면 추상클래스에서 선언한 추상함수를 모두 재정의 해야한다.

 

2-7) 인터페이스

 

1) 인터페이스의 구성요소를 보여주는 예

public delegate void StringListEvent(IStringList sender);

public interface IStringList {

void Add(String s); //함수

int Count {get; set; } //속성

event StringListEvent Changed; //이벤트

string this[int index] { get; set; } //인덱서

}

 

2) 인터페이스의 특징1

- 인터페이스 내의 멤버는 모두 몸체가 없다.

- 인터페이스의 멤버는 디폴트로 전부 public이다.

- 내부에 필드를 가질 수 없다.

- 멤버에 어떠한 접근자, 한정자도 붙이지 않는다.

 

3) 인터페이스의 특징2

- 인터페이스끼리 상속하면 더 큰 인터페이스가 된다.

- 인터페이스는 구현을 목적으로 한다.

 

◈ 클래스의 상속과 인터페이스를 동시에 상속하는 예

 

interface IA {

void SayA();

}

interface IB {

void SayB();

}

abstract class Top{

public abstract void SayTop();

}

class TopTest : Top, IA, IB {

public void SayA(){}

public void SayB() [}

public override void Saytop() {}

}

 

4) 인터페이스에서의 오버라이딩

 

using System;

interface IMyInterface {

void IMethod();

}

class MyClass : IMyInterface {

public void IMethod() {

Console.WriteLine("인터페이스의 함수를 구현");

}

public static void Main() {

MyClass mc = new MyClass();

mc.IMethod();

IMyInterface imc = mc;

imc.IMethod();

}

}

출력:

인터페이스의 함수를 구현

인터페이스의 함수를 구현

 

=> 추상클래스와 인터페이스의 추상 함수는 모두 가상(virtual) 함수의 성질을 가지고 있다. 추상 클래스 내의 추상 함수를 재정의 할 때에는 override 키워드를 사용해서 함수를 재정의하지만, 인터페이스 내의 추상 함수를 재정의 할때는 override 키워드를 사용하지 않는다.

 

 

5) 인터페이스 내의 속성 구현

 

interface IMyInterface {

int IProperty {

get;

get;

}

}

class ImplementsClass : IMyInterface {

private int iProperty;

public int IProperty {

get {

return iProperty;

}

set {

iProperty = value;

}

}

}

ImplementsClass ic = new ImplementsClass();

IMyInterface imi = ic;

imi.iProperty = 1000; //set

Colsole.WriteLine("Interface의 Property : " + imi.IProperty); //get

 

출력:

Interface의 Property : 1000

 

6) 인터페이스 내의 인덱서 구현

 

using System;

using System.Collections;

public interface SimpleInterface{

object this[int index] { get; set; } // 인덱서

}

public class SimpleIndexer : SimpleInterface {

ArrayList lname = new ArrayList();

public object this[int index] {

get {

if (index > -1 & index < lname.Count) { //범위내에 있는것

return lname[index];

} else {

return null;

}

}

set {

if (index > -1 & index < lname.Count) { // 범위내에 있는 것은 변경됨

lname[index] = value;

} else if (index == lname.Count) { //순서대로 하나씩 추가하면 됨

lname.Add(value);

} else {

Console.WriteLine("sid[" + index + "] : 입력범위 초과 에러!!");

}

}

}

}

class InterfaceIndexerTest{

public static void Main() {

SimpleIndexer sid = new SimpleIndexer();

sid[0] = "hong";

sid[1] = "kim";

sid[2] = "sung";

 

Console.WriteLine(sid[0]);

Console.WriteLIne(sid[1]);

Console.WriteLine(sid[2]);

sid[10] = "park"; // 범위초과

}

}

출력:

hong

kim

sung

sid[0] : 입력범위 초과 에러!!

 

7) 인터페이스의 명시적 구현

 

interface InterA {

void a();

}

interface InterB {

void a();

void b();

}

class Test : InterA, InterB {

public InterA.a(){

Console.WriteLine("InterA_a");

}

public InterB.a() {

Console.WriteLine("InterB_a"); 

}

public void b() {

       Console.WriteLine("InterB_b");

}

}

 

2-8) Delegate

 

1) Delegate

- 함수의 대리자

- 함수보다 효율적으로 사용하기 위하여 특정 함수 자체를 캡슐화할 수 있게 만들어주는 방법

 

2) Delegate 구현 예

 

using System;

//Delegate 선언

delegate void SimpleDelegate1();

delegate void SimpleDelegate2(int i);

class AType {

public void F1() {

System.Console.WriteLine("AType.F1");

}

public void F2(int x) {

System.Console.WriteLine("AType.F2 x=" + x);

}

}

class DeleTest {

public static void Main() {

AType atype = new AType();

//Delegate 객체 선언

SimpleDelegate1 s1 = new SimpleDelegate1(atype.F1);

SimpleDelegate2 s2 = new SimpleDelegate(atype.f2);

s1();

s2(1000);

}

}

출력:

AType.F1

AType.F2 x=1000

 

=> 하나의 클래스에서 다른 것은 필요없고 단지 특정 객체의 함수에만 관심이 있다고 해도, 해당 객체를 생성하여 그 객체의 모든 자원을 사용해야한다. 하지만 Delegate을 쓰면 하나의 함수를 빼올 수 있다.

 

=> C#에서 스레드를 사용하는 것이 위의 예와 비슷하다. C#에서 사용되는 모든 스레드는 함수를 이용해서 스레드를 시작하면 작업이 수행된다. 10개의 함수가 있다고 가정하자. 그리고 10개의 스레드를 만들고 함수를 스레드에 하나씩 넣어주고 실행하라고 명령을 내리면 10개의 스레드가 동시에 작업이 시작될 것이다. 스레드를 만들 때 함수를 넣어주어야 하는데 이 때 Delegate를 사용한다.

 

※ 스레드에서 Delegate를 사용하는 예

Top t = new Top(); // Top 클래스에 say()함수가 있다고 가정

Thread thread = new Thread(new ThreadStart(t.say));

thread.Start();

-> 여기서 ThreadStart가 Delegate이다.

 

 

'프로그래밍 > C#' 카테고리의 다른 글

C#으로 오라클접속  (0) 2012.08.29
C#으로 MySql 접속  (0) 2012.08.28
C#으로 Active Directory 접근  (0) 2012.08.28
C#의 기본문법  (0) 2012.05.08
Posted by 초코송송이
l

1. C#의 기본문법

1). 데이터 타입

C#의 데이터 타입들은 System.Object에서 파생된 System.Type 에서부터 상속된다. System.Object는 모든 데이터 타입의 부모이고, 어떠한 데이터 타입으로도 변환이 가능하다.

타 입

실제이름

범위

sbyte

System.SByte

-128 ~ 127

byte

System.Byte

0 ~ 255

char

System.Char

하나의 유니코드 문자. U+0000 ~ U+FFFF

short

System.Int16

-32,768 ~ 32,767

ushort

System.UInt16

0 ~ 65,535

int

System.Int32

-2,147,483,648 ~ 2,147,483,647

uint

System.UInt32

0 ~ 4,294,967,295

long

System.Int64

-9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807

ulong

System.UInt64

0 ~ 18,446,744,073,709,551,615


타 입

실제이름

정밀도

범 위

float

System.Single

7개의 자릿수

±1.5 ×10~45 ~ ±3.4 × 1038

Double

System.Double

15~16개의 자릿수

±5.0 ×10~324 ~ ±1.7 × 10308

Decimal

System.Decimal

28~29개의 자릿수

1.0 ×10~28 ~ 7.9× 1028

타 입

실제이름

특 징

Object

System.Object

모든 타입의 최상위 부모 클래스. C#에서 모든 객체들은 이 Object 클래스로부터 상속받는다.

String

System.String

문자열을 나타내는 타입이다.

Bool

System.Boolean

Boolean 값이며 참(true) 또는 거짓(false)를 나타낸다.

 

2). 선언의 간략 예

1) 변수선언의 간단 예

int x = 10;

System.Int32 x = 10;

int x, y, z;

string str = "문자열";

※ Delphi와 다른 변수이름 규칙: 대소문자를 가림

2) 구조체 선언

struct Person{

int num;

int age;

double height;

float weight;

}

구조체 변수선언과 값 할당

Person sister;

sister.num = 1000;

sister.age = 15;

sister.height = 165.3D;

sister.weight = 51.0F;

3) 열거형 선언

enum SignFlag {black, yello, green, blue, red};

=> black, yello, green, blue, red는 상수이며, 자동으로 0, 1, 2, 3, 4의 값이 할당된다.

SignFlag s1;

s1 = SignFlag.black;

Console.WriteLine("{0}={1}", s1, (int)s1);

s1 = SignFlag.blue;

Console.WriteLine("{0}={1}", s1, (int)s1);

==> 결과

black=0

blue=3

값을 지정 시: enum SignFlag {black, yello=3, green, blue=7, red};

4) 클래스 선언

class Top{

...

※ 타입캐스팅

class Test
{
    static void Main()
    {
        double x = 1234.7;
        int a;
        a = (int)x;  // cast double to int
        System.Console.WriteLine(a);
    }
}

출력: 1234

 

3). 연산자의 종류

1) 산술연산자

기본 산술연산자: +, -, *, /, %

산술복합연산자: +=, -=, *=, /=

2) 증감/감소 연산자

++x, x++, --y, y--

4) 관계 연산자

==, >, <, <=, >=, !=

5) 논리 연산자

&(AND), |(OR), ^(Exclusive OR), ~(NOT)

&&(Short-Circuit ADN 연산자): 두개의 피연산자 중 첫번째 값이 거짓이면 무조건 거짓이 된다.

||(Short-Circuit OR 연산자): 두개의 피연산자 중 첫번째 값이 참이면 무조건 참이다.

예)

if(a>10 & b>10) {

Consol.WriteLine("AND연산자");

}

if(a>10 && b>10) {

Consol.WriteLine("Short Circuit AND연산자");

}

6) 비트 연산자

&(비트 논리곱 AND), |(비트 논리합 OR), ^(배타적 논리합 XOR), ~ (1의 보수NOT), >>(우측 쉬프트), <<(좌측 쉬프트)

 

4). 제어문 

제어문들은 기본적으로 C문법과 동일

1) if-else문, if-else if, else문

if(불형식의 표현){

참이면 실행할 문장;

} else {

거짓이면 실행할 문장;

}

2) switch case문

int btn;

btn = 2;

switch(btn) {

case 1;

btn이 1일 경우;

break;

case 2:

..

break;

case 3:

...

break;

default:

break;

}

--------------------------------------

int n = 1;
switch (n) {
    case 1:
    case 2:
    case 3:
        Console.WriteLine("1,2,3입니다");
        break;
    default:
        break;
}

3) while문, do-while문

while(조건식) {
      ...
}

--------------------------------

int i = 0;

do{

     Console.WriteLine(i);

     i++;

} while (i > 2);

4) for문

for (int i = 1; i <= 5; i++){

   Console.WriteLine(i);

}

5) foreach 문

int[] numbers = { 4, 5, 6, 1, 2, 3, -2, -1, 0 };

foreach (int i in numbers){

   System.Console.WriteLine(i);

}

//다차원 배열 예 

int[,] numbers2D = new int[3, 2] { { 9, 99 }, { 3, 33 }, { 5, 55 } };
foreach (int i in numbers2D) {
    System.Console.WriteLine("{0} ", i);
}

 

5). 분기문

break, continue

 

 

2. C#의 클래스

 

1). 클래스

 

1) 클래스 만들기와 사용의 간단한 예

using System;

 

public class MethodTest {

public int a;

public int b;

public int Sum(int x, int y) {

return x + y;

}

}

public class MethodMain() {

public static void Main() {

int s;

MethodTest test = new MethodTest();

test.a = 1000;

test.b = 2000;

s = test.Sum(3, 5);

Console.WriteLine("변수 a는 " + test.a);

     Console.WriteLine("변수 b는 " + test.b);

     Console.WriteLine("Sum메서드 결과는 " + test.Sum(3, 5));

     Console.WriteLine("또다른 결과는  " + s);

}

}  

출력:

변수 a는 1000

변수 b는 2000

Sum메서드 결과는 8

또다른 결과는 8

 

2) 접근 지정자

private, protected, public

 

3) 속성(Property)

- 멤버 필드에 값을 할당하는 방법

- Set과 Get 형식의 함수를 일반화한 형태

- 스마트 필드(Smart Field)라고도 함

 

using System; 

public class Tes{

private string name; //소문자

public string Name { //대문자

get {

return name;

}

set {

name = value + " 님 반갑습니다.";

}

}

}

 

2). C#에서 구조체와 클래스의 차이점 

 

1) 구조체와 클래스의 유사점

- 클래스와 구조체는 데이터 타입 생성기이다.

 

2) 구조체와 클래스의 차이점

- 구조체는 값(Value Type)이며 클래스는 참조타입(Reference Type)이다.

 

◈ 구조체를 사용하는 이유

- 구조체는 직접적으로 메모리에 접근하기 때문에 참조의 낭비를 막을 수 있다.

 

◈ 구조체의 특징

- 구조체는 상속할 수 없으며 또한 상속해 줄 수 도 없다.

- 생성자를 중복해서 만들수 있지만 디폴트 생성자만큼은 재정의 해서 사용할 수 없다.

- (이유)값타입의 변수를 선언함과 동시에 메모리에 생성된다.

 

※ 구조체 테스트

using System;

struct Person {
    public int age;
    public long height;
    public float weight;
    public Person(int a, long h, float w) {
        age = a;
        height = h;
        weight = w;
    }
}

 

public class StructTest {
    public static void Main() {

        Person sister;
        sister.age = 13;
        sister.height = 170L;
        sister.weight = 55F;
        Console.WriteLine("sister : {0}-{1}-{2}", sister.age, sister.height, sister.weight);

 

        Person brother = new Person();//디폴트 생성자
        Console.WriteLine("brother : {0}-{1}-{2}", brother.age, brother.height, brother.weight);

       

        Person mother = new Person(40, 160L, 60F); //초기값을 할당할 수 있는 값타입의 구조체
        Console.WriteLine("mother : {0}-{1}-{2}", mother.age, mother.height, mother.weight);

       

        Person twins = sister; // 값 복사
        Console.WriteLine("twins : {0}-{1}-{2}", twins.age, twins.height, twins.weight);

}

}

 

출력:

sister : 13-170-55

brother : 0-0-0

mother : 40-160-60

twins : 13-170-55

 

==> sister와 twins은 sister를 twins에 통째로 복사된 것

그리고, brother와 mother은 new를 사용하였지만, new를 썼다고 무조건 참조타입은 아니며 값타입으로 생성된 것이다. 구조체의 초기값을 할당할 방법을 제공하기 위해 new 키워드를 사용하였지만, 값타입인 것은 변함없다.

 

 

3). Call By xxx

 

1) 값에 의한 전달 (Call By Value)

-------------------------

int a = 3;

int b = 5;

int c;

c = Sum(a, b);

-------------------------

int Sum(int x, int y) {

return x + y;

}

-------------------------

 

2) ref에 의한 참조 (Call By Reference)

 

using System;

class TestRef {

static void CallByRef(ref int x) {

x = 10000;

}

public static void Main() {

int x1 = 10; //초기화 하지 않으면 에러발생

CallByRef(ref x1);

Console.WriteLine("Call-By-Reference: {0}", x1);

}

}

출력:

Call-By-Reference: 10000

 

◈ ref 키워드

- 참조에 의한 전달을 할 때 사용하는 키워드

- 참조로 값을 넘길 때 참조할 대상은 반드시 초기화 되어야 한다.

 

3) out에 의한 참조(Call By Reference)

 

using System;

class TestRef {

static void CallByRef(out int x) {

x = 10000;

}

public static void Main() {

int x1; //초기화 하지 않음

CallByRef(out x1);

Console.WriteLine("Call-By-Out: {0}", x1);

}

}

출력:

Call-By-Out: 10000

 

◈ out 키워드

- 참조에 의한 전달을 할 때 사용하는 키워드

- 참조로 값을 넘길때 참조할 대상을 초기화 할 필요는 없다.

 

◈ out 키워드를 사용하는 경우

- 함수로부터 값을 얻어낼 때 주로 사용한다. 그렇기 때문에 초기화를 할 필요가 없는 것이다.

 

 

3. C#프로그램 시작하기

 

3-1) C# Hello World 분석

 

1) Hello World 프로그램 구성

 

using System;

namespace org.jabook {

public class HelloWorld {

public static void Main() { // 프로그램 실행은 하나의 Main()에 포함해야한다.

Console.WriteLine("Hello World!");

}

}

}

=> HelloWorld.cs로 저장

 

==> C# 프로그램에서 컴파일을 통해서 만들 수 있는 파일은 exe나 dll 형태의 파일이다. exe 파일은 프로그램의 진입점을 포함하고 있는 형태이며, dll 파일은 진입점을 포함하지 않는 순수한 라이브러리 형태이다. 진입점이 있다는 것은 Main()함수를 포함하고 있다는 의미이다.

 

※ C# 컴파일러로 컴파일하기

Visual Studio 명령프롬프트에서 (VS설치 시)

c:\>csc HelloWorld.cs

HelloWorld.exe가 생성됨 

 

2) 네임스페이스와 어셈블리

 

◈ 어셈브리(Assembly)

: 컴파일한 결과 파일(중간언어 형태로 되어 있음)

 

◈ 네임스페이스

: 어셈블리 내에서 각각의 클래스들을 구분하는 기준이 네임스페이스(Namespace)이다. 다시 말해, 어셈블리 내에는 많은 클래스들이 존재할 수 있으며, 이들 클래스를 구분하는 기준이다.

 

namespace aaa{

namespace bbb {

namespace ccc {

public class Person {

public int age;

public long height;

public float weight;

}

}

}

}

 

※ C# 컴파일러로 컴파일하기

csc /target:library /out:Person.dll Person.cs

 

3) 네임스페이스 사용하기

 

◈ 라이브러리 형태의 어셈블리(Assembly)를 사용하기 위한 절차

- Person.dll의 물리적인 위치를 명시한다. (콘솔창에서 컴파일할 때)

- 코드 내에서 Person.dll 내부에서 사용하고자 하는 네임스페이스를 using한다. (코드 내에서 작업)

 

◈ 라이브러리 생성(C#컴파일러)

- csc /target:library /out:Person.dll Person.cs

 

◈ 라이브러리 물리적 사용

- csc /reference:Person.dll /out:PersonFinal.exe PersonTest.cs

- Person.dll이 현재 디렉터리에 있기 때문에 /reference:Person.dll을 사용

- Person.dll이 다른 위치에 있다면 전체 경로 명시

 

◈ 라이브러리의 논리적 사용

  using System;

  using aaa.bbb.ccc; //사용하고자하는 네이스페이스 명시

  public class personTest {

      ...

  }

 

4) Main() 함수

 

C#에서 실행파일이면서 Main() 함수가 없다면 프로그램은 컴파일 되지 않고, 프로그램의 진입점이 없다는 에러가 발생한다.

만약, Main() 함수가 없는 라이브러리 형태의 어셈블리를 만든다면 반드시 컴파일 옵션인 /target:library를 명시해야한다.

 

◈ C#에서 Main() 함수의 형식

1. public static void Main() { .. }

2. public static void Main(string[] args) { .. }

3. public static int Main() { ... return 0; }

4. public static int Main(string[] args) { ... return 0; }

 

◈ Main() 함수의 특징

- 프로그램의 시작점이다.

- Main() 함수가 끝나면 프로그램은 종료한다.

- Main() 함수는 반드시 static으로 선언해야 한다.

- Main() 함수에서 객체를 생성하거나 다른 함수를 호출할 수 있다.

- 명령 프롬프트 상의 매개변수를 읽기 위해서 string 배열을 사용한다.

 

5) static 키워드

모든 클래스에서 공유하기 위한 멤버를 선언하기 위해 사용한다. 객체가 아무리 많이 생성되더라도 스태틱 멤버로 선언되어 있으면 프로그램 내에서 단하나의 메모리만 생성된다. 모든 객체에서 공통으로 사용하는 전역변수의 개념으로 사용할 때 이 스태틱을 사용한다.

 

6) Console.WriteLine() 함수

- System 네임스페이스의 클래스

- WriteLine() 함수는 Colsole 클래스의 스태틱 멤버함수이며 화면에 표준출력을 처리하는 함수

 

예)

Console.WriteLine("a: {0}, b: {1}, a-b: {2}", a, b, a-b);

 

 

3-2) 스태틱 메모리, 함수, 생성자

 

1) 스태틱 메모리

 

◈ 스태틱 사용의 예

public class StaticAccess {

public static in sint = 0;

public int nint = 0;

public static void Main() {

StaticAccess.sint = 3333;

StaticAccess sa = new StaticAccess();

sa.nint = 1000;

Console.WriteLine("static직접접근: " + StaticAccess.sint);

Console.WriteLine("일반멤버 필드접근: " + sa.nint);

}

}

 

◈ 스태틱 메모리에 접근하는 방법

- 클래스의 이름으로 접근한다. (StaticAccess.sint)

 

◈ 스태틱 메모리 생성시기

- 객체를 생성하기 이전에 스태틱의 메모리는 생성된다.

 

2) 스태틱 함수

 

◈ 스태틱 멤버함수

- 스태틱 멤버함수는 클래스의 이름으로 접근 가능하다.

- public static 함수만 접근 가능하다.

 

◈ 스태틱 멤버 함수에서 주의할 점

- 스태틱 함수를 이용하여 일반 멤버 필드에 접근 불가

- 일반 멤버 필드는 객체 생성 후에 존재하기 때문에 스태틱 함수에서 접근 불가

 

◈ 스태틱 함수의 사용

 

public class StatiMethodAccess {

private static int sint = 100;

public int nint = 0;

public static void SetStaticInt(int x) {

sint = x;

}

public static int GetStaticInt() {

return sint;

}

public static void Main() {

StaticMethodAccess.SetStaticInt(33333);

int s = StaticMethodAccess.GetStaticInt();

Console.WriteLine("static값은 : " + s);

}

}

 

3) 스태틱 생성자

 

◈ 스태틱 생성자

- 스태틱 생성자는 스태틱 멤버 필드의 메모리가 생성된 직후 호출되는 스태틱 전용의 생성자 함수이다.

 

◈ 스태틱 생성자의 특징

- 접근제어를 사용할 수 없음

- 매개변수를 가질 수 없음 : 스태틱에 접근하는 순간 스태틱 생성자는 자동으로 호출되므로 매개변수를 넣어줄 방법이 없다.

 

 

◈ 스태틱 생성자의 사용

 

class StaticConst {

public static int sInt = 0;

static StaticConst() { //스태틱 생성자

sInt = 10;

Console.Write("sInt = " + sInt + " : static 생성자!!");

}

public StaticConst() { //디폴트 생성자

//

}

public static void InitSint(int a) {

sInt = a;

}

}

class StaticConstTest {

public static void Main() {

int a = StaticConst.sInt;

}

}

 

==> 스태틱 멤버 필드들은 클래스가 생성되기 전에 메모리가 생성된다. 그렇다고 한다면 스태틱의 멤버의 값을 초기화할 수 있는 곳은 선언하는 순간에 할당할 수 밖에 없다. 딱히 다른곳에서 할데가 없다 이 방법 이외에 스태틱 생성자라는 것을 이용할 수도 있다.

 

※ Main() 함수가 static 함수인 이유

- 실행 클래스의 객체가 생성되기 이전에 CLR이 Main() 함수에 접근해야 하기 때문이다.

 

 

3-3) const, unsafe

 

1) const 상수 

 

◈ const 키워드

- 상수를 선언하는 키워드

 

◈ const 상수 선언하기의 예

- public const int SALLERY = 7070;

 

◈ const 상수의 특징

- const는 자동으로 static이 된다.

- const로 선언한 변수는 반드시 초기화 되어야 한다.

 

2) readonly 상수

 

 ◈ readonly 상수의 특징

- 반드시 초기화할 필요없다.

- 생성자에서 딱 한번 값을 할당할 수 있다.

- static 키워드를 사용하면 스태틱 상수가 되고 사용하지 않으면 일반 상수가 된다.

 

◈ static readonly

- static readonly 일 경우 스태틱 생성자에서 초기화할 수 있다.

- static readonly 일 경우 클래스의 이름으로 접근가능

 

◈ 일반 readonly

- 일반 readonly 일 경우 생성자에서 초기화 할 수 있다.

- 일반 readonly 일 경우 객체의 이름으로 접근가능

 

◈ readonly의 사용

using System;

public class ReadonlyTest {    

public readonly static int STATIC_READONLY = 1;

public readonly int NORMAL_READONLY= 1;

 

static ReadonlyTest() { //static 생성자

STATIC_READONLY = 100;

}

public ReadonlyTest() {//일반 생성자

NORMAL_READONLY = 100000;

}

public static void Main() { //

Console.Write("STATIC_READONLY = " + ReadonlyTest.STATIC_READON

ReadonlyTest rt1 = new ReadonlyTest();

Conesole.Write("NORMAL_READONLY" + rt1.NORMAL_READNLY);

}

}

 

3) 가비지 콜렉터와 메모리

 

◈ c++에서 메모리 관리

- new 를 사용해서 메모리르 생성했으면 반드시 사용자가 직접 delete를 사용해서 메모리를 제거해 주어야한다.

 

◈ 가비지 콜렉터

- C#의 메모리 관리자 역할을 한다.

 

◈ 가비지 콜렉터가 하는 일

- 더이상 사용하지 않는 메모리나 불필요한 메모리를 제거한다.

- 메모리가 부족하면 메모리의 조각 모음을 한다.

 

◈ 가비지 콜렉터의 관리 대상

- 힙에 생성되는 객체의 메모리

 

=> C#에서는 new는 사용하지만 delete는 사용하지 않는다. 가비지 콜렉터를 지원해주기 때문이다. 가비지 콜렉터는 CLR 내에서 동작하면서 메모리의 청소가 필요하다고 생각될 때 시스템에 의해서 구동되는 메모리 청소부와 같은 역할을 담당한다.

 

4) unsafe 코드

 

◈ unsafe code

 - C#에서 포인터를 사용하면 unsafe code가 된다.

- CLR이 메모리를 전자동으로 관리해 주는데 사용자가 직접 메모리를 건드는 것은 안전하지 못하기 때문에 unsafe code라고 한다.

 

◈ unsafe code 컴파일 옵션

- unsafe code를 컴파일할 때 컴파일 옵션으로 /unsafe를 사용해서 컴파일해야 한다. 

 

◈ unsafe code 사용

- C#에서 포인터(*)를 사용하기 위해서 unsafe 키워드를 이용해서 사용할 수 있다.

 

using System;

class UnsafeTest {

unsafe static void CallByPoint(int* x) {

*x = 1000;

}

public static void Main() {

int x1 = 10;

unsafe {

CallByPoint(&x1);

}

Console.WriteLine("Call-By-Point: {0}", x1);

}

}

 

4) fixed 키워드를 이용한 메모리 고정

 

◈ fixed 키워드

- CLR에 의해 해당 메모리의 포인터를 이동시키지 못하도록 하는 키워드

- unsafe code 내에서만 사용할 수 있다.

- CLR이 메모리를 이동하지 않는다는 것이 보장된다.

 

◈ fixed의 사용 예

 

using System;

class FixedTest

{

unsafe static void ArrayCopy(byte[] source, byte[] target) {

fixed(byte* s = source, t = target) {

for( int  i = 0 ; i < source.Lengh ; i++ )

                        *(t+i) = *(s+i);

              }

        }

public static void Main() {

byte[] sample = {5, 6, 4, 6, 3, 4, 100};

byte[] copyed = new byte[7];

ArrayCopy(sample, copyed);

foreach(byte temp in copyed)

Console.Write(temp + "\t");

}

}

결과:

5    6    4    6    3    4    100

 

'프로그래밍 > C#' 카테고리의 다른 글

C#으로 오라클접속  (0) 2012.08.29
C#으로 MySql 접속  (0) 2012.08.28
C#으로 Active Directory 접근  (0) 2012.08.28
C#의 객체지향  (0) 2012.05.10
Posted by 초코송송이
l

1.UDP 프로토콜개요

TCP와 UDP의 공통점: 전송 계층 프로토콜이라는 점에서 다음과 같은 공통점이 있다.

- 포트번호를 이용해 주소를 지정한다.

- 데이터 오류를 체크한다. : TCP와 UDP는 IP의 패킷 전송 기능을 기반으로 동작한다. 전송 중 여러 원인으로 오류가 발생할 수 있는데, IP는 프로토콜 동작에 필수적인 IP 헤더에 대해서만 오류를 체크하고 데이터는 체크하지 않는다. 반면 TCP와 UDP는 헤더는 물론이고 데이터에 대한 오류도 체크한다.

TCP와 UDP의 차이점 

항목 

TCP 

UDP 

 1)

연결형(connection-oriented) 프로토콜

- 연결설정 후 통신 가능

비연결형(connectionless) 프로토콜

- 연결설정 없이 통신 가능 

 2)

신뢰성 있는 데이터 전송  신뢰성 없는 데이터 전송 
 3) 일대일 통신(unicast) 

일대일 통신(unicast), 일대다 통신(broadcast, multicast) 

 4)

데이터 경계 구분 안함

- 바이트 스트림(byte-stream) 서비스

데이터 경계 구분함

- 데이터그램(datagram) 서비스 

항목을 UDP 위주로 설명하면,

1) 연결 설정을 하지 않았으므로 connect() 함수를 사용하지 않는다. (몇가지 이유로 connect() 함수를 사용하는 경우가 있지만, UDP 프로토콜에는 연결 설정 개념이 없으므로 connect() 함수를 호출하더라도 특별한 패킷 교환이 일어나지 않는다.

2) 프로토콜 수준에서 신뢰성 있는 데이터 전송을 보장하지 않으므로, 필요하다면 응용 프로그램 수준에서 신뢰성 있는 데이터 전송 기능을 구현해야 한다.

3) 간단한 소켓 함수 호출 절차만 따르면 다자 간 통신을 쉽게 구현할 수 있다.

4) TCP와 달리 응용 프로그램이 데이터 경계 구분을 위한 작업을 별도로 할 필요가 없다.

 

※ UDP에 대한 오해

UDP는 신뢰성 없는 데이터 전송을 하므로 데이터 오류를 체크하지 않을 것이라고 생각하면 안된다. UDP는 TCP처럼 체크섬(checksum)을 이용해 데이터 오류를 체크한다. 그렇다면 TCP는 신뢰성 있는 데이터 전송을 하는데 왜 UDP는 그렇지 못 할까? 답은 UDP에서 데이터 재전송과 데이터 순서 유지 작업을 하지 않기 때문이다. UDP는 도착한 데이터에 오류가 있다고 판단하면 이 데이터를 응용 프로그램에 전달하지 않고 그대로 삭제해 버린다. 따라서 응용 프로그램은 데이터에 오류가 있어 버려졌다는 사실을 알지 못한다. 또한 TCP는 데이터 순서 유지를 위해 각 바이트마다 번호를 부여하지만 UDP는 비슷한 기능을 제공하지 않는다.

 

2. UDP 서버-클라이언트 동작 원리

UDP서버는 TCP 서버와 달리 멀티스레드 등의 프로그래밍 기법을 사용하지 않고도 한 소켓으로 여러 클라이언트를 처리할 수있다.

 (a) 서버는 소켓을 생성하고 클라이언트가 데이터를 보내기를 기다린다. 이 때 서버가 사용하는 소켓은 특정 포트 번호와 결합되어 이 포트번호로 도착한 데이터만 수신한다.

(b) 첫 번째 클라이언트는 연결 설정 없이 서버와 데이터를 바로 주고 받는다.

(c) 두 번째 클라이언트도 연결 설정 없이 서버와 데이터를 바로 주고 받는다. (TCP 서버와 달리 UDP서버는 소켓을 한개만 사용한다.)

(d) UDP 서버-클라이언트가 통신하는 일반적인 상황이다. 'UDP 클라이언트 #n' 처럼 한 클라이언트가 소켓을 두개이상 사용해 서버와 통신할 수 도 있다.

 

※ UDP서버-클라이언트모델(1)

 

주의 사항

- 블로킹 소켓을 사용할 경우, 송수신 함수의 호출 순서가 맞지 않으면 교착상태가 발생할 수 있다.

- 클라이언트는 데이터를 받은 후 송신자의 주소(IP주소,포트번호)를 확인해야 한다. recvfrom() 함수는 UDP 서버가 보낸 데이터는 물론, 전혀 다른 UDP 응용 프로그램이 보낸 데이터도 수신할 수 있기 때문이다.

 

※ UDP 서버-클라이언트모델(2)

=> UDP 클라이언트를 위와 같이 작성할 수도 있다. UDP 소켓에 대해 connect() 함수를 호출하면 통신할 상대의 주소 정보가 내부적으로 기억되므로 sendto()/recvfrom() 함수 대신 send()/recv() 함수를 사용해 특정 UDP 서버와 통신할 수있다.

장점

- sendto() 함수를 사용한 경우보다 효율적이다. connect() 함수로 서버주소를 한 번만 설정해 두면, send() 함수가 이 정보를 재사용하기 때문이다.

- 데이터를 받은 후 송신자의 주소(IP, 포트)를 확인하지 않아도 된다. recvfrom() 함수와 달리 recv() 함수는 connect() 함수로 설정한 대상을 제외한 다른 UDP 응용 프로그램이 보낸 데이터는 수신하지 않기 때문인다.

주의사항

- 블로킹 소켓을 사용할 경우, 송수신 함수의 호출 순서가 맞지 않으면 교착 상태가 발생할 수 있다.

 

※ UDP 데이터 전송 함수

1. sendto() 함수

1) 함수 원형

int sendto(

 SOCKET s, //통신에 사용할 소켓

 const char *buf, //보낼 데이터를 담고 있는 버퍼의 주소

 int len, // 보낼 데이터의 크기

 int flags, // sendto()함수의 옵션. 대부분 0을 사용. MSG_DONTROUTE(윈속에서는 사용하더라도 무시), MSG_OOB(UDP에서는 의미가 없음)가 있음

 const struct sockaddr *to, // 목적지 주소를 담고 있는 소켓 주소 구조체(UDP의 경우, 특정 호스트나 라우터 주소, 브로드캐스트나 멀티캐스트 주소를 사용할 수 있음)

 int tolen // 목적지 주소를 담고 있는 소켓 주소 구조체의 크기

);

2) sendto() 함수의 특징

sendto() 함수는 UDP 소켓은 물론, TCP 소켓에도 사용가능 하다. TCP 일때는 to와 tolen인자는 무시된다. 그리고 TCP 소켓에 사용할 때만, flags 인자에 MSG_OOB를 사용할 수 있다.

sendto() 함수로 보낸 데이터는 독립적인 UDP 데이터그램(패킷)으로 만들어져 전송되며, 수신측에서는 recvfrom() 함수 호출 한번으로 이 데이터를 읽을 수 있다. 따라서 UDP를 사용할 경우에는 TCP와 달리 응용 프로그램 수준에서 메시지 경계를 구분하는 작업을 할 필요가 없다.

UDP 소켓에 대해 sendto()함수를 호출할 경우, 한번에 보낼수 있는 데이터의 크기에 제한이 있다. 최소 0~최대 65507(65535-20(IP헤더크기)-8(UDP헤더크기)) 바이트이다. 실제로는 최대값보다 훨씬 작은 크기를 사용하는것이 바람직하다. 특히 UDP를 이용해 브로드캐스트 패킷을 보낼 경우, 512바이트보다 작은 크기를 사용할 것을 권고한다.(비주얼스트디오 설명서)

sendto()함수로 보낸 응용 프로그램 데이터는 커널(운영체제)영역에 복사되어 전송된 후 곧바로 버려진다. sendto() 함수가 리턴됐다고 실제 데이터 전송이 완료된 것은 아니며, 데이터 전송이 끝났어도 상대방이 받았는지 확인할 수 없다.

블로킹 소켓을 사용할 경우, 커널 영역에 데이터를 복사할 공간이 부족하면, sendto()함수는 호출 시 블록된다.

2. recvfrom() 함수

1) 함수 원형

int recvfrom(

 SOCKET s, // 통신에 사용할 소켓. sendto()함수에 사용하는 소켓과 달리, 이 소켓은 반드시 지역주소(IP, 포트)가 미리 결정되어 있어야 한다.

 char *buf, // 받은 데이터를 저장할 버퍼의 주소

 int len, // 버퍼의 크기. 도착한 UDP 패킷 데이터가 len보다 크면 len 만큼만 복사하고 나머지는 버린다. 이때 SOCKET_ERROR를 리턴한다.

 int flags, // 대부분 0을 사용. 사용가능한 값으로 MSG_PEEK, MSG_OOB(UDP에서는 의미가 없음)가 있다. recvfrom() 함수의 기본 동작은 수신버퍼의 데이터를 응용프로그램 버퍼에 복사한 후 해당 데이터를 수신버퍼에서 삭제하는 것인데, MSG_PEEK 옵션을 사용하면 수신 버퍼에 데이터가 계속 남는다.

 struct sockaddr *from, // 소켓 주소 구조체를 전달하면 송신자의 주소 정보로 채워진다.

 int *fromlen // 함수가 채워넣은 주소정보 구조체의 크기를 갖게된다.

);

2) recvfrom() 함수의 특징

recvfrom() 함수는 UDP 소켓은 물론이고, TCP 소켓에도 사용할 수 있으며, 이 경우, from과 fromlen 인자는 무시된다. TCP 소켓에 사용할 때만 flags 인자에 MSG_OOB를 사용할 수 있다.

sendto() 함수로 보낸 데이터는 독립적인 UDP 데이터그램(패킷)으로 만들어져 전송되며, 송신측에서는 recvfrom() 함수 호출 한번으로 이 데이터를 읽을 수 있다. 따라서 응용프로그램 수준에서 메시지 경계를 구분하지 않아도 된다.

UDP 소켓에 대해 recvfrom() 함수를 호출할 경우 리턴 값이 0이 될 수 있는데, 이는 상대방이 sendto() 함수 호출시 데이터 크기를 최소값 0으로 설정했다는 뜻이다. UDP 프로토콜에는 연결 설정과 종료 개념이 없으므로 recvfrom() 함수의 리턴 값이 0이라고 해서 특별한 의미가 있는 것은 아니다. 반면, TCP 소켓에서 recvfrom() 함수를 호출할 경우 리턴 값이 0이면, 정상종료를 의미한다.

블로킹 소켓을 사용할 경우, 소켓 수신 버퍼에 도착한 데이터가 없으면 recvfrom()함수는 호출 시 블록된다.

 

3. 브로드캐스팅

 

- 유니캐스팅(unicasting: one-to-one): 한 개체가 다른 한 개체에 데이터를 보내는 모델. IPv4, IPv6에서 지원

- 브로드캐스팅(broadcasting: one-to-many): 한 개체가 특정 네트워크(자신과 동일 네트워크도 가능)에 속한 모든 개체에 데이터를 보내는 모델. IPv4에서만 지원

- 멀티캐스팅(multicasting: ont-to-many): 동일 그룹에 가입한 모든 개체(물리적으로는 서로 다른 네트워크에 속할 수 있음)에 데이터를 보내는 모델. 개념적으로 브로드캐스팅은 멀티캐스팅의 특수한 경우로 볼수 있다. IPv4, IPv6에서 지원)

- 애니캐스팅(anycasting: one-to-one-of-many): 한 개체가 동일 그룹에 가입한 개체 중 가장 가까운 하나에만 데이터를 보내면, 데이터를 받은 개체가 그룹에 속한 나머지 개체에 데이터를 보내는 모델. IPv6에서만 지원)

 

※ 소켓응용 프로그램에서 브로드캐스트 데이터를 보내는 방법

① 브로드캐스팅을 활성화 한다.

BOOL bEnable = TRUE;

retval = setsocketopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&bEnable, sizeof(bEnable));

=> socket() 함수로 생성한 소켓은 기본적으로 유니캐스팅만 지원하지만, setsocketopt() 함수의 첫번째 인자로 해당 소켓, 두번째 SOL_SOCKET과 세번째 SO_BROADCAST 옵션, 네번째 TRUE 값을 사용하면 브로드캐스팅이 활성화된다.

② 브로드캐스트 주소를 목적지로 해서 데이터를 보낸다.

SOCKETADDR_IN remoteaddr;

ZeroMemory(&remoteaddr, sizeof(remoteaddr));

remoteaddr.sin_family = AF_INET;

remoteaddr.sin_addr.s_addr = inet_addr("255.255.255.255");

remoteaddr.sin_port = htons(9000);

char buf[BUFSIZE]; //송신용 버퍼를 선언하고 테이터를 넣는다.

...

retval = sendto(sock, buf, strlen(buf), 0, (SOCKET *) &remoteaddr, sizeof(remoteaddr));

if(retval == SOCKET_ERROR) 오류처리;

printf("%d바이트를 보냈습니다.\n", retval);

=>브로드캐스트 데이터를 보내는 코드는 IP 주소 부분을 제외하면 유니캐스트 데이터를 보내는것과 동일

 

※ 브로드캐스트 주소의 종류

IPv4 주소는 크게 네트워크 ID와 호스트 ID로 나눌수 있다. 서브넷을 사용하는 경우, 호스트 ID의 일부는 서브넷 ID로 사용된다. 브로드캐스트용으로 예약되어 있는 IPv4 주소는 아래와 같다. 

 

- 네트워크 브로드캐스트(net-directed broadcast)

호스트 ID 비트가 모두 1인 경우로 특정 네트워크에 대한 브로드캐스트를 의미한다. 브로드캐스트 데이터가 라우터를 거쳐야 하므로 라우터 설정에 따라 브로드캐스팅이 불가능할 수도 있다. 따라서 실용적으로 브로드캐스팅 주소로 사용하기는 어렵다.

 

- 서브넷 브로드캐스트(subnet-directed broadcast)

서브넷 ID를 제외한 호스트 ID비트가 모두 1인 경우로, 특정 서브넷에 대한 브로드캐스트를 의미한다. 서브넷 브로드캐스트도 라우터를 통과하지 못할 수 있으므로, 일반적으로 외부 서브넷에 대한 브로드캐스팅을 목적으로 사용하기는 어렵다. 하지만 송신자 자신이 속한 서브넷에 대한 브로드캐스팅은 항상 가능하다.

 

- 지역 브로드캐스트(local broadcast 또는 limited broadcast)

송신자 자신이 속한 네트워크에 대한 브로드캐스트를 의미한다. 항상 브로드캐스팅이 가능하며, 브로드캐스트 데이터가 라우터 경계를 넘어가지 않는다. 지역 브로드캐스트 주소는  ws2def.h파일에 INADDR_BROADCAST(0xffffffff; 255.255.255.255) 값으로 정의되어 있다.

 

"TCP/IP 윈도우 소켓 프로그래밍"책 에서 발췌

Posted by 초코송송이
l

Delphi String에 관하여..

 

Delphi 2007이하의 String Types 기준
 
3가지 타입이 있음.
  • Short strings.
  • Long strings.
  • Wide (Unicode) strings. 
 Type Maximum Length  Memory required  Used for 
 ShortString  255 characters  2 to 256 bytes  이전 버전과의 호환을 위해 존재함.
 AnsiString  ~ 2^31 characters   4 bytes to 2GB  8-bit (ANSI) characters, DBCS ANSI, MBCS ANSI, etc.
 WideString  ~ 2^30 characters  4 bytes to 2GB   유니코드. 다중사용자 서버나 다중언어 지원용 어플리케이션에서 사용.
(AnsiString: 때때로 long string 이라 불리며, 많은 목적으로 대부분 이걸 사용함.
WideString: .Net 플랫폼에서 주로 사용함.)  

Short String

델파이의 조상격인 Turbo-Pascal에서는 단지 한종류의 String 타입밖에 없었다. 바로 ShortString 타입이다. 터보파스칼 시절 ShortString은 디폴트 String 타입이었고, 우리가 단순하게 String으로 선언을 하면 기본적으로 ShortString이 선언이 되었다. ShortString은 기본적으로 힙에 생성이 되지 않고, 스택에 생성이 된다. 메모리 할당 관점에서 보면 ShortString은 다른 Integer, Boolean, Record, Enum타입등과 달리 정적할당 타입이다. 정적할당이다 보니 ShortString은 당연히 런타임시 사이즈가 변할수 없다. 그래서 ConCat(문자열 붙이는 함수), Insert(문자열에 추가하는 함수)등의 함수를 쓸 때 문제를 야기시켰다. 이 문제를 회피하기 위해, 터보파스칼(델파이)은 모든 String 인스턴스에 256바이트의 최대 사이즈를 미리 할당하였다. ShortString의 첫번째 요소는 String의 실제 길이를 나타내므로 ShortString은 최대 255 글자를 가질수 있다.

var S: ShortString;

 실제로 위와 같이 선언하면 스택에는 256 바이트가 사용된다. ShortString 변수가 지역변수이거나, 전역변수이거나 레코드나 클래스에 사용된다 해도 똑같이 256바이트가 사용된다. 

 ShortString의 메모리 구조는 아래와 같다. n은 문자가 문자열에 저장된 수이다.

 

원문: http://blog.naver.com/g2000240023?Redirect=Log&logNo=50021477287

http://www.codexterity.com/delphistrings.htm

 

Long Strings (AnsiString)

long string 이라고도 부르는 AnsiString은 동적으로 할당되는 문자열이다. 최대길이는 메모리가 허락하는 한도까지이다.
 
long string 변수는 4 바이트의 메모리 공간을 차지하는 포인터다.
문자열이 비어있을 때 (=길이가 0 일 때), 그 포인터는 nil 을 가리키며, 추가적으로 사용하는 공간은 없다. 문자열이 비어 있지 않을 때, 그 포인터는 문자열이 들어있는 동적할당된 메모리의 블럭을 가리킨다. 문자열 위치 이전의 8 바이트는 32-bit 길이의 지시자와 32-bit 길이의 레퍼런스 카운트로 이루어져 있다.
이 메모리는 힙 영역에 할당되지만, 이 메모리는 프로그래머가 관리하지 않고 전부 자동적으로 관리된다.
 
왜냐하면 long-string 변수들은 포인터이며, 둘 이상의 이 변수들은 추가적인 메모리의 소비없이 같은 값을 가리킬 수도 있기 때문이다.
컴파일러는 이 메모리를 이용해 자원을 절약하고, 실행속도를 향상시킨다.
long-string 변수가 해제되거나 생성될 때, 레퍼런스 카운트는 감소되거나 증가한다. string 을 가리키고 있던 레퍼런스 카운트가 0 이 되면 그 string 의 메모리는 해제된다.
이 프로세스를 레퍼런스 카운팅이라고 부른다.
인덱싱 과정에서 string 의 문자 하나를 바꿀 경우, 만약 그 레퍼런스 카운트가 1 보다 크다면 그 string 의 복사본이 생성된다.
이것을 가리켜 copy-on-write 라 한다.

원문: http://bloodguy.tistory.com/96 

아래는 델파당에서 검색한 내용을 옮겨보았습니다.

VCL의 String은 reference가 count됩니다. String은 변수는 그냥 포인터 4Byte입니다.
그런데 4Byte에 적힌 address위치에 가 보면 실제data가 있죠.

procedure TForm1.Button1Click(Sender: TObject);
var
    s: AnsiString;
begin

  s:='abcd';

  ShowMessage(IntToStr(sizeof(s)));      //'4'
  ShowMessage(IntToStr(sizeof(AnsiString))); //'4'
end;

위 코드에서 s의 size나  AnsiString의 크기는 4입니다.
즉 String변수는 포인터입니다.
실제 Data가 기록된 위치의 메모리 주소(Address)가 적힌 것입니다.

[AnsiStirng의 실제 기록 구조]

AllocSize =   ( StrRec+ 실제 Data ) 까지의 크기가 기록되어있습니다.
RefCount =  말그대로 reference-Count 로 AnsiString변수가 참조하고 있는 숫자입니다.
Length     =  실제Data의 길이입니다.

String의 길이를 구할때..
Delphi(VCL)의 AnsiString이   Lenth함수가  c에서 strlen보다 속도가 빠른데..
AnsiStirng의 Length를 구하면 위에 StrRec구조체에서  Length값을 그냥 return하지만
c에서 strlen은   $0 즉  NULL문자가 나타날때까지 처음부터 계속 search합니다.
그래서 AnsiString의 Length가 c의 strlen보다 속도를 비교할수 없을 정도로 빠르죠
참조  http://cafe.naver.com/bcbmaster/850

var
  s1,12: Stirng;
begin                                             // 1)

  s1:='abcd';                                  // 2)
  s2:=s1;                                       // 3)

  s2[1]='k';                                   // 4) 

end;                                             // 5)
  
위 소스에서 1) ~ 4) 과정을 설명해드리면...

1) AnsiString을 선언하면 스택에 4Byte 씩 2개 변수가 할당됩니다.
    문론 내용은 nil 아무것도 없습니다.

2) heap(힙)에   strrec(12Byte)포함하여 실제 data 4Byte까지 총 16Byte가 할당됩니다.

      AllocSize = 16
      RefCnt    =  1
      Length     = 4
      data = 'abcd'

      위와같이 메모리에 기록되고 stack에 s1위치에는 Data위치의 address가 기록됩니다. 

3)  s2:=s1 하면
      RefCnt = 2 로 바뀌고
      S2변수가 있는 stack위치에 Data가 s1과 똑같은 값 Data위치의 addr이 기록됩니다.

4)  s2의 값의 일부를 변경하면
       *  s1의 가리키던   StrRec의 RefCnt =1 로 바뀝니다.
       *  그리고 다시 또다른  힙에 14+8=22Byte의 메모리가 확보되고 다음과 같이 내용이 기록됩니다.
      AllocSize = 22
      RefCnt    =  1
      Length     = 4
      data = 'kbcd'            //실데이타  5Byte인데..  4의 배수로  할당되어서 8Byte가 할당됨
       * 그리고 s2의 stack위치값은  새로할당된 data의 힙address가 기록됩니다.

    음.. 그리고 AllocSize가 왜 12+data_length가 아니라 14+data_length인지는 저도 정확한 이유를 잘 모르겠네요
   
...
5) 함수를 빠져나가면 stack에   할당된 s1 , s2 가 삭제되겠죠

    string변수가 삭제 될때는 RefCnt가 하나씩 줄어듭니다.
    만약 RefCnt가 0이 될때면 메모리에 할당된 StrRec부터 Data까지 모두 free됩니다.


만약 전역변수에 문자열을 할당하면 전역변수의 내용을 바꾸지 않은한 RefCnt는 프로그램이 종료할때까지 항상  1보다 크겠죠

원문: http://www.delmadang.com/community/bbs_view.asp?bbsNo=17&bbsCat=41&indx=417354

 

WideString

WideString 타입은 동적으로 할당되는 16-bit 유니코드 문자로 이루어진 문자열을 의미하며, Win32 에서, WideString 은 COM BSTR 타입과 호환된다.
 Note: Win32 에서, WideString 은 레퍼런스 카운트를 이용하지 않는다.

 Win32 플랫폼은 싱글바이트, 멀티바이트뿐만 아니라 유니코드를 지원한다.

SBCS(single-byte character set) 에서 각 바이트는 문자열의 한 character이며,                     MBCS(multibyte character set)에서는 일부 character 는 1바이트 또는 그 이상의 바이트를 통해 표현된다.
MBCS에서 가장 첫번째 바이트는 lead byte 라고 불리며,
일반적으로, MBCS에서 하위 128 characters 들은 7-bit ASCII characters 이며, 127 이상의 character 를 결정하는 건 MBCS의 lead byte 이다.
null 값은(#0) 언제나 싱글바이트 문자이다.
 
유니코드 character set 에서 각 문자는 2바이트를 의미하며 유니코드 문자열은 1바이트의 나열이 아니라 2바이트의 나열이다.
Unicode characters 와 strings 는 또한 wide characters 와 wide character strings 로 불린다.
Unicode characters map 의 처음 245은 ANSI character set 이다.
윈도우즈 OS는 유니코드를 지원한다. (UCS-2) 
 
델파이는 Char, PChar, AnsiChar, PAnsiChar, AnsiString 타입을 통해 싱글바이트와 멀티바이트 문자(열)을 지원한다.
(멀티바이트 함수들은 보통 Ansi- 로 시작하는 이름을 가진다. 예를 들어, StrPos 의 멀티바이트 버전 함수는 AnsiStrPos 임)
멀티바이트 문자의 지원은 로케일 별 OS 에 의존적이며,  델파이는 WideChar, PWideChar, WideString 을 통해 유니코드를 지원한다.

원문: http://bloodguy.tistory.com/96 

 

Delphi 2009 이상

 상당히 오랫동안 Delphi에는 문자를 나타내기 위해 다음의 두 가지 개별적인 문자 타입들을 가지고 있었습니다.
AnsiChar: 8비트 표현을 사용 (256개의 기호들을 표시), 코드 페이지에 따라 다르게 해석됨
WideChar: 16비트 표현을 사용 (64K의 기호 표시)
 이 측면에서 Delphi 2009에 변화된 점은 없습니다. 달라진 점은, AnsiChar의 별칭이었던 Char 타입이 이제 WideChar의 별칭이 되었다는 것입니다. 컴파일러가 코드에서 Char를 발견할 때마다 WideChar로 해석합니다. 이런 새 컴파일러 디폴트를 변경할 수 있는 방법은 없습니다.이는 많은 소스 코드에 영향을 미치고 많은 결과를 양산하는 상당히 큰 변화입니다. 예를 들어 PChar 포인터는 이제 이전의 PAnsiChar가 아닌 PWideChar의 별칭입니다.

문자열과 UNICODESTRING

 Char 타입 정의에서의 변경 사항은 문자열 타입 정의에서의 변경 사항과 밀접한 관련이 있기 때문에 매우 중요합니다. 문자와는 다르게 문자열은 이전에는 없었던 새로운 데이터 타입(UnicodeString이라고 함)에 매핑됩니다. 내부 구조도 AnsiString 타입의 표현과는 상당히 다릅니다.
WideChar 타입에 기반한 문자열을 나타내는 WideString 타입이 언어에 이미 있는데, 왜 힘들게 새 데이터 타입을 정의해야 할까요? WideString은 참조가 카운트되지 않으며 성능과 유연성의 측면에서 매우 취약합니다. 예를 들어 WideString은 네이티브 FastMM4가 아닌 Windows 글로벌 메모리 할당자(allocator)를 사용합니다.
AnsiString과 마찬가지로 UnicodeString은 참조가 카운트되며 copy-on-write 의미를 사용하고 성능이 우수합니다. AnsiString과 달리 UnicodeString은 문자별로 2바이트를 사용하며 UTF-16을 기반으로 합니다. 사실상 UTF-16은 변수 길이 인코딩이며, 때때로 UnicodeString은 두 개의 WideChar 서로게이트 요소(즉, 4바이트)를 사용하여 단일 유니코드 코드 포인트를 나타냅니다.
문자열 타입은 이제 Char 타입에서와 마찬가지로 하드 코딩된 방식으로 UnicodeString에 매핑됩니다. 이를 변경할 수 있는 컴파일러 지시어나 다른 방법은 없습니다. 계속해서 AnsiString 문자열 타입을 사용해야 하는 코드가 있으면 해당 코드를 AnsiString 타입의 명확한 선언으로 교체해야 합니다.

문자열의 내부 구조

새 UnicodeString 타입과 관련된 주요 변경 사항 중 하나는 내부 구조입니다. 새로운 구조는 참조 카운트되는 문자열 타입 모두(UnicodeString 및 AnsiString)에 해당되며, 참조 카운트되지 않는 문자열 타입들(ShortString 및 WideString)에는 해당되지 않습니다.
기존의 AnsiString 타입의 구조는 다음과 같았습니다.

-8 -4  문자열 참조 주소 
 참조 카운트  길이 문자열의 첫 번째 문자 

첫 번째 요소(문자열 자체의 시작 부분부터 역으로 카운트)는 Pascal 문자열 길이이며 두 번째 요소는 참조 카운트입니다.

Delphi 2009에서는, 참조 카운트된 문자열의 구조는 다음과 같습니다.

-12

-10

 -8

-4

문자열 참조 주소

코드 페이지

요소 크기

참조 카운트

길이

문자열의 첫 번째 문자


길이와 참조 카운트 외에, 새로운 필드는 요소 크기와 코드 페이지를 나타냅니다. 요소 크기는 AnsiString과 UnicodeString 사이에 식별하는 데 사용되며, 코드 페이지의 경우는 UnicodeString 타입이 고정 코드 페이지 1200을 가지므로 AnsiString 타입에서만 의미를 갖습니다.

해당 지원 데이터 구조는 다음과 같은 System 유닛의 임플먼테이션 섹션에서 선언되어 있습니다.
type
    PStrRec = ^StrRec;
    StrRec = packed record
        codePage: Word;
        elemSize: Word;
        refCnt: Longint;
        length: Longint;
    end;
UnicodeString에 의해 리턴되는 코드 페이지는 1200이며, 이것은 글로벌 변수인 DefaultUnicodeCodePage에 저장되어 있는 숫자 값을 리턴한 것입니다. 위의 코드와 결과에서 Length는 문자 수를 리턴하므로 문자열 길이를 바이트 단위로 확인할 수 있는 직접적인 호출은 없습니다.
물론 다음 표현식을 사용하여, 길이 값에 각 문자의 바이트 크기를 곱하여 문자열 길이의 바이트 값을 알 수 있습니다.
Length(str1) * StringElementSize(str1)

(자세한 사항은 문서 참조)

참조문서: http://www.google.co.kr/url?sa=t&rct=j&q=delphi%20%EB%AC%B8%EC%9E%90%EC%97%B4%20%EA%B5%AC%EC%A1%B0&source=web&cd=1&ved=0CC8QFjAA&url=http%3A%2F%2Fwww.devgear.co.kr%2Fpdf%2Fmarco_cantu_delphi_and_unicode_kor.pdf&ei=Bc17T-6_I8ORiQfGnaCiCQ&usg=AFQjCNE0oq_js4cy2BaiEJ0aVFNAe4BUPg&cad=rjt 

 

Posted by 초코송송이
l