문자와 스트링

차례:
  -char 형 변수
  -char 형 포인터 변수
  -string
  -char 배열
  -ponter 배열

0. 들어가기 전에

c 언어에서는 메모리의 주소 개념을 뺀 후 문자를 정확하게 이해하기가 힘들다.

(1) BYTE addressable

이 개념은 포인터 변수를 이해하는데 반드시 알아야 하는 내용이므로 확실히 이해하고 넘어가자.

바이트 단위로 주소가 부여(byte addressable)되어 있다는 개념이다.

현재 우리가 사용하고 있는 컴퓨터는 주 기억장치에 주소에 부여되어형태가 8 비트 즉 1 바이트 단위로 주소가 부여되어 있다.

주소를 기억하기 위해선 32 비트가 필요하다. 주소를 기억하는 변수를 포인터 변수라 하는데 포인터 변수의 크기는 4 바이트란 것을 기억하자.

(2) ASCII code

자판 A 를 누르면 이 A 는 컴퓨터 기억장치에 저장된후 화면에 표시된다. 그러면 이 A 는 컴퓨터 내부에 어떻게 기억될 까? 그냥 A 로 기억될까? 컴퓨터 내부적을 2 진수 체계를 사용한다. 당연히 이 A 는 2 진수 얼마로 약속이 되어 있을 것이다. 이 약속을 code 라한다.

이런한 약속방법에는 몇가지가 있는데 우리가 사용하고 있는 컴퓨터가 약속한 코드는 ascii code 이다. 이 코든 체계는 7 bit 로 문자 하나씩을 약속하여 사용한다.

문자 ASCII 코드 문자 ASCII 코드 문자 ASCII 코드
A 41a61030
B 42b62131
... ...............
(숫자는 16 진 표현)

(유제) 소문자 k 는 ascii 코드에서 어떻게 약속되어 있는가?

문자뭐가 2 진수 뭐로 약속이되어 있는지를 다 외울 필요는 없지만 기본적으로 몇가지는 외우는게 좋을 것 같다.

ascii 코드 00 에서 1F 까지는 대응되는 문자의 의미보다는 제어문자(줄바꿈, 벨,...) 로 사용된다. 그 중에서ascii 00 는 대응되는 문자는 없다. 이를 널 문자라 한다.

1) 숫자와 문자의 차이

차이를 이해하는 게 어렵다.

예를 들어,3 은 숫자이고 , '3' 은 문자 3 이다. 이는 완전히 다른 의미가 된다. 즉 3 은 2 진수 11 이고 문자 '3' 은 이진수 0011 0011(0x 33) 을 의미한다.

2) c 언어에서 표현 가능한 진법

c 언어로 표현 가능한 진법으로는 3 가지 진법이 있다.
  • 16 진법
    0x숫자 -- 이 숫자는 16 진법의 수
  • 8 진법
    0숫자 -- 이 숫자는 8 진법의 수
  • 10 진법
    숫자 -- 이 숫자는 10 진법의 수
예를 들어, c 언어에서는 다음은 같은 의미
      'A'= 0x41
         = 0101
         = 65
       

3) escape sequence

(보기) 아래 네모에 무엇을 넣어야 0 을 만들 수 있을까?

일반적인 방법으로는 네모속에 어떤 것을 넣더라도 0 을 만들 수 없다. 그래서 c 언어에서는 \숫자 형태를 사용하면 그대로 숫자로 나올수 있는 형태를 제공한다.

'\0' 을 널 문자라하고 , 이런 형태로 주로 사용하는 몇가지를 보면

1. char 형 변수

char 형 데이터 타입은 1 바이트를 의미하는 자료형이다.
#include < stdio.h >

int main()
{
   char a;  //  ---- 1

   a='A'; // --- 2 
   printf("%c",a); // --- 3
   return 0;
}
출력 결과가 A 가 출력된다면 이상 무. 문자를 다룰 때에 포인터 변수를 빼고는 문자를 정확하게 이해하기가 힘드니 이 부분 부터는 메모리상의 주소 개념을 가질 필요가 있다.
char a; // --- 1
변수 a 는 문자형 데이터 타입이다. 모든 변수는 주 기억장치에 자리를 차지한다.

변수가 주 기억장치에 몇 평(?)을 차지하는지 자료형은 이 변수가 메모리에 명 평을 차지 하는지 가르켜 주는 역할을 한다. 즉 변수 a 는 1 바이트를 차지하는 변수를 의미한다. 즉 하나의 주소당 한 바이트

변수 a 가 100 번지에 자리를 차지 한다면

a='A' 나 a=0x41 혹은 a=65 나 같은 의미가 된다는 것에 주목하자.

a='A'; // --- 2
= 은 대입을 의미한다. 같다는 의미가 아니라 대입을 의미한다. 즉 'A' 즉 0x41 을 변수 a 위치에 넣어라는 의미이다.

printf("%c",a); // --- 3
printf 문(정확히 함수)은 화면에 뭔가를 출력하라는 의미이다. 변수 a 를 %뒤의 형식 즉 c 형식으 로 출력하라는 의미이다. c 는 변수 a 를 ascii 로 대응되는 문자형으로 출력하라는 의미이다.

즉 0100 0001 의 대응 문자 즉 16 진수 41 의 대응문자는 대문자 A 이므로 A 가 출력된다. 만약 변수 a 를


(유제1) 다음 프로그램의 실행 결과는? (가능하면 메모리 맵(그림)을 그리면서 생각하자)
#include < stdio.h >

int main()
{
   char a,b;

   a='A';
   b=a+0x20;
   printf("%c %c",a,b);
   return 0;
}

(유제2) 다음 프로그램의 실행 결과는? (가능하면 메모리 맵(그림)을 그리면서 생각하자)

#include < stdio.h >

int main()
{
   char a,b;

   a='a';
   b=a+10;
   printf("%c %x",a,b);
   return 0;
}

2. char 형 포인터 변수

(보기) 다음 프로그램을 입력후 출력결과를 살펴보자.
#include < stdio.h >

int main()
{
   char a,*p; //--1

   a='A'; //--2
   p=&a;  //--3
   printf("%c %c",a,*p); //--4
   return 0;
}
  1. char a,*p;
    a, *p 는 문자형 즉 한 바이트를 변수이다. 뭔지는 모르겠지만 *p 는 한 바이트인데 변수 p 는 뭔가?

    변수 앞에 * 가 붙은 변수는 주소를 저장하는 변수이다. 이를 포인터 변수라 한다. 1 장에서 주소를 저장하기 위해서는 4 바이트를 필요로 하므로 모든 포인터 변수는 4 바이트 변수이다.

    정리하면

    1. a , *p 는 한 바이트
    2. p 는 네 바이트 포인터 변수

    a 가 100 번지에 , p 가 200 번지에 위치한다면

  2. a='A'
    변수 a 에 0100 0001 을 대입

  3. p=&a;
    &(앰퍼샌드, ampersand) 가 무엇인가????

    & 는 변수의 주소를 의미한다. 즉 a 는 0100 0001 을 &a 는 100 을 의미한다. 즉 변수 a 의 주소를 p 에 대입하라는 의미이다.

  4. printf("%c %c",a,*p);
    이제 *p 의 의미를 알아보면 , p 는 100 이고 ,&p 는 200 이고 , *p 는 무엇을 의미 하는가?

    *p 는 변수 p 가 가르키는 내용을 의미한다. 즉 100 번지가 가르키는 내용을 의미한다.

    그러면 *p 가 한 바이트라는 의미는 p 에 주소값 100 이 들어 가 있을 때 , 100 번지가 가르키는 내용 한 바이트를 의미한다. 만약에 *p 가 두 바이트 크기라면 100 번지 , 101 번지 의 내용을 의미할 것이다.

(유제1) 다음 프로그램의 실행 결과는?
#include < stdio.h >

int main()
{
   char *p,a;

   a='A';
   p=&a;
   *p='a';
   printf("%c %c",a,*p);
   return 0;
}
(유제2) 다음 프로그램의 실행 결과는?
#include < stdio.h >

int main()
{
   char *p,*q,a;

   a='A';
   p=&a;
   q=p;
   *q='B';
   printf("%c %c %c",*p,*q,a);
}

3. string

스트링이란 0 개 이상의 문자를 모아 놓은 것을 스트링이라 한다.

c 언어에서 string 은 겹 따옴표를 사용하여 표시한다.

"스트링"
'a' 는 문자 a , "a" 는 스트링 a
'ab' 는 문자 ab , "ab"는 스트링 ab
문자는 두 개이상의 문자 표현은 스트링이기에 'ab'라는 표기법은 말이 않된다.

(보기) 아래 프로그램을 그대로 입력 후 실행 결과를 확인해 보자.

#include 

int main()
{
   char *p;

   p="tur";
   printf("%s",p);
}
#include <stdio.h>

int main()
{
   char *p;   -- 1

   p="AB";   -- 2
   printf("%s",p); -- 3
}
1.char *p;

p 는 4 바이트 차지하는 포인터 변수

2.p="AB";

p="tur" 은 두가지 기능을 수행한다.

  1. "AB" 는 A,B,\0 을 차례대로 가용공간에 연속적으로 잡은 후의 첫 문자의 주소 를 의미
  2. p="AB" 는 문자 A 가 잡힌 기억장치의 주소를 포인터 변수 p 에 대입하라는 의미이다.
3.printf("%s",p);

%출력형식에서 s 는 스트링을 출력하는 형식인데, %s 는 반드시 변수의 주소와 대응한다.

이 경우 포인터 변수 p 에는 주소값 200 이 들어있으므로 200 번지의 가르키는 곳에서 한 바이트씩 문자형으로 출력한다. 단, \0 즉 널 문자를 만날 때 까지 한 바이트씩 문자형으로 출력하라는 의미이다.

(유제) 다음 프로그램의 실행 결과는?
#include < stdio.h >

int main()
{
   char a,*p,*q;

   p="AB";
   q=p;
   a=*p;
   *q=*q+1;
   printf("%s %c %c",p,*q,a);
}

4. char 배열

(보기) 다음을 그대로 입력 후 실행 해보자.
#include < stdio.h >

int main()
{
    char ia[]={'A','B','\0'};

    printf("%s\n",&ia[0]);
    return 0;
}
분석---

배열 ia 가 100 번지부터 자리를 잡는다면 , ia[0] 에는 'A' , ia[1] 에 'B' , ia[2] 에 '\0' 가 들어 간다. 즉 ia[0] 는 'A' 이고 , ia[0] 의 주소 &ia[0] 는 100 이 된다.

ia[]={'A','B','\0'} 와 ia[]="AB" 와는 동일한 표기이다.

(유제) 다음 프로그램의 실행 결과는?

#include < stdio.h >

int main()
{
   char str[]="turbo";
 
   printf("%s\n",&str[2]);
   return 0;
}

5. 배열과 포인터

c 언어에서 배열과 포인터는 밀접한 관계가 있다.
1) 주소(포인터) 연산
프로그래밍시 포인터의 연산을 하기 되는데 이 부분이 포인터를 이해하는데 아주 중요한 개념이니 반드시 숙지하도록 하자. 포인터 변수 p , q 의 크기는 둘 다 2 바이트이다. 하지만 *p (char 형)의 크기는 1 , *q 의 크기(int 형)는 2 이다.

만약 포인터 변수 p 에는 주소 100 이 q 에는 주소 200 이 들어있다고 할 때 p , q 를 1 증가 하면 얼마일 까? 101 , 201 이 될 까 ?

* 를 붙인 크기 만큼 증가한다. 즉 *p 의 크기가 1 이라면 p 를 1 증가하면 1 증가하고 , *q 의 크기가 2 라면 q 를 1 증가하면 2 만큼 증가한다. *q 의 크기가 4 라면 q 를 1 를 1 증가하면 4 만큼 증가한다. 위 예에서는 101 , 202 가 될 것이다.

예를 들어 , char ***p; 가 있다면

(유제1) 다음 프로그램의 실행 결과는?
#include < stdio.h >

main(){
   int ia[]={2,4,6,8,10};
   int *p;

   p=&ia[0];
   p=p+2;
   *p=*p+3;
   printf("%d\n",ia[2]);
}
(유제2) 다음 프로그램의 실행 결과는?
#include <stdio.h>

int main()
{
   char *str="turbo";

   str+=2;
   printf("%s\n",str);
}
(유제3) 다음 프로그램의 실행 결과는?
#include <stdio.h>

int main()
{
   char **p,*q,r;

   r='A';
   q=&r;
   p=&q;
   printf("%c\n",**p);
}
2) 배열과 포인터
배열 ia[] 가 있을 때 , 이 배열의 첫 위치는 &ia[0] 이다. 이를 나타내기가 너무 구질구질하여 배열명은 이 배열의 처음 주소로 약속한다. 즉
배열 명 ia 는 &ia[0] 이다.
이는 ia+n 은 &ia[n] 과 동일한 표현이다.

이를 주소가 아닌 내용으로 확장하면

*(ia + n) 은 ia[n] 과 동일한 표현이다.
(보기) 배열 ia[] 가 있을 때 , *ia+n , *(ia+n)의 차이는 무엇일 까?

*ia+n 은 ia[0]+n 과 같고 , *(ia+n) 은 ia[n] 과 동일한 표현이다.
(유제1) 다음 프로그램의 실행 결과는?
#incldue < stdio.h >

int main()
{
   int ia[]={2,4,6,8,10};

   printf("%d %d %d\n",*(ia+3),*ia+3,ia[3]);
}
(유제2) 다음 프로그램의 실행 결과는?
#include < stdio.h >

int main()
{
   int ia[]={2,4,6,8,10};
   int *p;

   p=ia;
   p+=2;
   printf("%d %d %d %d\n",p[0],*p,*p+1,*(p+1));
}

6. 포인터 배열

char *p;
p 는 주소를 가지는 포인터 변수이다.
char *p[3];
p 는 주소를 가지는 포인터 배열이다. 즉 이 배열원소에는 주소값이 들어간다.

(보기) 다음 프로그램을 그대로 실행 후 결과를 확인 해보자.

#include < stdio.h >

int main()
{
   char *p[3];

   p[0]="aa";
   p[1]="bb";
   p[2]="c";

   for(i=0;i<3;i++)
      printf("%s\n",p[i]);
}

포인터와 배열에서 주의 사항

  1. 아래 두 가지 프로그램 중에 하나는 오류가 있는 프로그램이다. 어떤 프로그램이 오류가 있는가? 이유가 무엇인가?
    //1 번 프로그램
    
    #include <stdio.h>
    
    int main()
    {
       char *p;
    
       p="turbo";
       printf("%s\n",p);
    }
    
    //2 번 프로그램 
    
    #include <stdio.h>
    
    int main()
    {
       char p[10];
    
       p="turbo";
       printf("%s\n",p);
    }
    

  2. 아래 두 가지 프로그램 중에 하나는 오류가 있는 프로그램이다. 어떤 프로그램이 오류가 있는가? 이유가 무엇인가?
    //1 번 프로그램
    
    #include <stdio.h>
    
    int main()
    {
       char *p;
    
       scanf("%s",p);
       printf("%s\n",p);
    }
    
    //2 번 프로그램 
    
    #include < stdio.h >
    
    int main()
    {
       char p[10];
    
       scanf("%s",p);
       printf("%s\n",p);
    }
    
출처:dovelet

[질/답]
[홈으로]  [뒤 로]
[푼 후(0)]