'분류 전체보기'에 해당되는 글 84건

  1. 2010.07.03 c++ 오버라이딩
  2. 2010.06.26 itoa 대신 sprintf
  3. 2010.06.26 라이브러리
  4. 2010.06.26 volatile
  5. 2010.06.24 포인터 상수
  6. 2010.06.22 [컴파일 강좌] 2강 - C++ 분할 컴파일
  7. 2010.06.21 [컴파일 강좌] 1강 - C 분할 컴파일
  8. 2010.06.19 __attribute__
  9. 2010.06.14 동적 할당 0614
  10. 2010.06.13 문자열

c++ 오버라이딩

|
자식 클래스가 부모 클래스의 오버로드된 함수들 중 하나만 오버라이딩 하면 에러.
모든 함수를 오버라이딩하면 성공.

상속과 무관...
디폴트 생성자 없이 생성자를 만든 후, 그 생성자를 호출하면 에러.
디폴트 생성자 만들고 다른 생성자 만든 후, 그 생성자를 호출하면 성공.

'C++' 카테고리의 다른 글

C++ 생성자  (0) 2010.08.13
생성자와 소멸자  (0) 2010.08.13
동적 할당 0614  (0) 2010.06.14
c++ 0610  (0) 2010.06.10
c++0608  (0) 2010.06.08
And

itoa 대신 sprintf

|
char* itoa(n)
{
static char buf[20];
sprintf(buf, "%s", n);
return buf;
}

char* buf[20]; 하면 리턴된 후 buf는 사라지므로 에러

static char* buf[20];
대신 동적 할당을 사용하면??

while(n) 100 10 1  0
{
cnt++;     1    2  3
n /= 10;  10   1 0
}
char* buf = (char*)malloc(sizeof(char)*cnt);

'C' 카테고리의 다른 글

volatile  (0) 2010.06.26
포인터 상수  (0) 2010.06.24
문자열  (0) 2010.06.13
구조체  (0) 2010.06.12
버블 정렬 c 0609  (0) 2010.06.09
And

라이브러리

|

입력을 받는 함수 3개( scanf(), getch(), gets() ),
출력을 하는 함수 3개( printf(), putch(), puts() )를 만들었다.
또한 수학 관련 함수 5개를 만들었다.
그리고 네트워크 관련 함수 4개도 만들었다.
이것을 각각 컴파일하여 15개의 오브젝트 파일을 만들어 놓았다.
그리고 헤더 파일도 15개 만들어 놓았다.

이제 main() 함수에서 #include로 필요한 함수의 헤더 파일을 포함시켜주고 그 함수를 사용하면 된다.
그리고 컴파일할 때 main() 함수에서 사용한 함수들의 오브젝트 파일을 포함시켜 컴파일해 주면 된다.
그런데 사용한 함수가 15개라면 아래와 같이 해야 할 것이다.
$ gcc -o main.c scanf.o getch.o gets.o printf.o putch.o puts.o ...
쓸 만하다. 
하지만 사용한 함수가 100개라면 어떻게 할 것인가?

그래서 라이브러리 개념을 도입한다.
잘 아는 단어이다. '도서관'이라는 뜻인데 이것이 컴파일과 무슨 관련이 있다는 것인가?
도서관에는 많은 책들이 있다. 그런데 이 많은 책들이 마구 섞여 있지 않고 잘 분류되어 있다는 것이다.
주제에 맞게 책을 분류하고 분류기호를 정해 놓았기 때문에 이 기호를 보고 원하는 책을 빨리 찾을 수 있다.

자, 다시 컴파일로 돌아오자.
15개의 함수를 입출력, 수학, 네트워크 이렇게 3개로 분류하면 좋을 것 같다.
그리고 각각을 libio, libmath, libnet라고 이름을 붙이자. (반드시 앞에 lib를 붙여주어야 함)
그래서 scanf() 함수를 찾으려면 libio를 찾아가면 되는 것이다.

이제 직접 라이브러리를 만들어보자.
GCC에서는 ar이라는 명령어로 라이브러리를 만들 수 있다.
우리는 3개로 분류하였으므로
libio.a
libmath.a
libnet.a
라는 3개의 파일이 만들어진다.
libio.a는 6개의 입출력 오브젝트 파일을 포함하고 있다.
나머지 파일도 해당 오브젝트 파일을 포함하고 있다.

이제 이 라이브러리 파일을 이용해서 컴파일을 하면 된다.
$ gcc -o main main.c -lio lmath lnet -L.
-l 뒤에 lib와 .a를 뺀 io, math, net를 써주면 된다.
-L 뒤에 라이브러리 파일이 포함된 경로를 포함시켜 준다. main.c와 같은 경로라면 .만 찍어 준다.



$ vi input.c
#include <stdio.h>

int input()
{
int in;
printf("What is your favorite number: ");
scanf("%d", &in);
return in;
}
:wq

$ vi output.c
#include <stdio.h>

void output(int out)
{
printf("Your favorite number is %d.\n", out);
}
:wq

$ vi main.c
#include <stdio.h>

int main()
{
int num;
num = input();
output(num);
return 0;
}
:wq

$ vi input.h
extern int input();
:wq

$vi output.h
extern void output(int)
:wq

$ ls
input.c  input.h  main.c  output.c  output.h
$ gcc -c input.c output.c
$ ls
input.c  input.h  input.o  main.c  output.c  output.h  output.o

$ ar r libmylib.a input.o output.o
ar: creating libmylib.a
$ ls
input.c  input.h  input.o  libmylib.a  main.c  output.c  output.h  output.o

$ ar s libmylib.a
$ ls
input.c  input.h  input.o  libmylib.a  main.c  output.c  output.h  output.o

$ ar t libmylib.a
input.o
output.o
$ ls
input.c  input.h  input.o  libmylib.a  main.c  output.c  output.h  output.o

$ gcc -o main main.c -lmylib -L.
$ ls
input.c  input.h  input.o  libmylib.a  main  main.c  output.c  output.h  output.o

$ ./main
What is your favorite number: 3
Your favorite number is 3.






'Compile 강좌' 카테고리의 다른 글

[컴파일 강좌] 2강 - C++ 분할 컴파일  (0) 2010.06.22
[컴파일 강좌] 1강 - C 분할 컴파일  (0) 2010.06.21
컴파일러 옵션  (0) 2010.05.30
And

volatile

|
C complier는 프로그래머가 작성한 소스코드를 최척화하여 컴파일한다.
int a;
a = 10;
a = 20;
a = 30;
이라고 작성하면 C Compiler는 가운데 2줄은 쓸데없는 것으로 인식하고 바로 a에 30의 값을 넣어버린다.

이를 Memory-mapped IO라고 해보자.

#define PORTF (*(unsigned char *)0x62) 
PORTF가 자주 쓰이므로 컴파일러는 0x62가 가리키는 메모리에 직접 값을 쓰거나 읽는 대신 cpu의 레지스터 하나에 값을 쓰거나 읽도록 최적화해버립니다.
그러면 여덟개의 스위치를 무작위로 누르면서 PINC에 값을 변화시키면 이 값은 실제 메모리인 0x62에 쓰여지지 않고 cpu의 레지스터에만 써버리기 때문에 PORTF에는 값이 쓰여지지 않게 된다. 그래서 스위치를 눌러도 LED에 불이 들어오지 않는다.

이를 막기 위해 volatile 키워드를 쓴다.
#define PORTF (*(volatile unsigned char *)0x62) 
이렇게 해주면 컴파일러는 최적화를 하지 않는다. 항상 0x62가 가리키는 메모리에 값을 쓰거나 읽는다.
따라서 스위치를 누를때마다 그 값이 실제 메모리 상에 쓰여지게 되므로 LED에 불이 들어온다.

while(1)
{
PORTF = PINC;
}

'C' 카테고리의 다른 글

itoa 대신 sprintf  (0) 2010.06.26
포인터 상수  (0) 2010.06.24
문자열  (0) 2010.06.13
구조체  (0) 2010.06.12
버블 정렬 c 0609  (0) 2010.06.09
And

포인터 상수

|






#include <stdio.h>


int main(void) {

int arr[4] = {1, 2, 3, 4};


printf("&arr = %p\n", &arr);    // 0012FF54

printf("arr = %p\n", arr);       // 0012FF54

printf("&arr[0] = %p\n", &arr[0]);    // 0012FF54


return 0;

}


arr과 &arr 이 출력하는 메모리주소는 분명 같습니다만,


그 의미에는 미묘한 차이가 있습니다.


익히 아시고 계신 것처럼 arr은 &arr[0] 과 같은 의미이며, 그 data type은 배열의 data type입니다.


즉 arr + 1의 의미는 arr[0]에서 int형의 size만큼, 4byte 이동한 주소, &arr[1]을 의미할 수 있습니다


하지만 &arr은 그렇지 않습니다. &arr은 arr이라는 이름의 int형 4개짜리 일차원 배열의 시작주소입니다.


이것은 즉 (&arr) + 1 이 &arr[1] 을 의미하지 않음을 말합니다.


이러한 맥락에서 (&arr) + 1은 arr[0]에서 int형의 size*배열의 데이터 갯수(4) 를 곱한만큼 이동한 곳


의 주소입니다. 다음의 코드를 봐주세요.


int a[4] = {0, 1, 2, 3};


int (*pa)[4] = &a; /* 그냥 a를 쓰면 에러가 납니다 &a[0]과 &a는 의미하는 바가 달라서입니다


pa는 int형의 데이터 4개를 가지는 일차원배열을 가리키는 포인터입니다 */

pa++; /* 메모리 주소가 sizeof(int) * 4(배열데이터갯수) 만큼 이동합니다 */

printf("%d\n", (*pa)[-1]); /* pa가 가리키는 일차원 배열을 참조한 후 -1 인덱스,

즉 이는 a[3]을 의미합니다 */


출력되는 값은 물론 3입니다 :)


PS) int (*pa)[4]; /* 위에 나온대로 일차원배열을 가리키는 포인터를 선언합니다 */

int *pa[4]; /* int형의 포인터 4개를 가지는 일차원배열을 선언합니다 */











'C' 카테고리의 다른 글

itoa 대신 sprintf  (0) 2010.06.26
volatile  (0) 2010.06.26
문자열  (0) 2010.06.13
구조체  (0) 2010.06.12
버블 정렬 c 0609  (0) 2010.06.09
And

[컴파일 강좌] 2강 - C++ 분할 컴파일

|
클래스 헤더 파일
Point.h
클래스 구현 파일
Point.cpp
메인 함수 파일
main.cpp

클래스 구현 파일을 반드시 클래스 헤더 파일을 포함해야 한다.

클래스 헤더 파일에서 cout을 사용하였다면 반드시
#include <iostream>
using namespace std;
를 써 주어야 한다.

클래스 구현 파일에서 cout을 사용하였다면 반드시
#include <iostream>
using namespace std;
를 써 주어야 한다.

메인 함수 파일에서 cout을 사용하였다면 반드시
#include <iostream>
using namespace std;
를 써 주어야 한다.

클래스 헤더 파일과 구현 파일, 메인 함수 파일 모두에서 cout을 사용하였다면 클래스 헤더 파일에만
#include <iostream>
using namespace std;
를 써 주면 된다.
왜냐하면 클래스 구현 파일과 메인 함수 파일이 클래스 헤더 파일을 #include하고 있기 때문이다.

1) 오브젝트 파일 생성 (컴파일 - 어셈블리)
cl /c main.cpp Point.cpp
main.cpp와 Point.cpp 파일을 각각 컴파일한다. 즉,
cl /c main.cpp의 결과로 main.obj 생성
cl / Point.cpp의 결과로 Point.obj 생성

2) 실행 파일 생성 (링크)
cl main.obj Point.obj
이번에는 두 개의 obj 파일을 합쳐 하나의 실행 파일로 만든다.
제일 앞에 있는 obj 파일의 이름이 실행 파일의 이름이 된다.
따라서 main.exe 파일이 생성된다.

cl Point.obj main.obj
제일 앞에 있는 obj 파일의 이름이 실행 파일의 이름이 된다.
따라서 Point.exe 파일이 생성된다.

'-o test'을 쓰면 main.exe와 test.exe 2개의 실행 파일이 생성된다.








'Compile 강좌' 카테고리의 다른 글

라이브러리  (0) 2010.06.26
[컴파일 강좌] 1강 - C 분할 컴파일  (0) 2010.06.21
컴파일러 옵션  (0) 2010.05.30
And

[컴파일 강좌] 1강 - C 분할 컴파일

|
컴파일 강좌
1강 - C 분할 컴파일
2강 - C++ 분할 컴파일
3강 - 정적 라이브러리
4강 - 동적 라이브러리

하나의 파일에 모든 소스 코드를 넣으면 그 파일의 크기는 얼마나 될까? 그리고 여러 사람이 동시에 작업하는 것이 가능할까?
몇 백명이 투입되는 대형 프로젝트의 경우 모든 작업이 분업화되어 작은 부분을 하나하나 합쳐서 커다란 하나의 프로그램을 만든다.

그러면 각각의 파일을 컴파일하여 오브젝트 파일을 생성하고 이 오브젝트 파일을 하나로 합치는 방법에 대해서 알아보자.

예제1) main.c 파일에 모든 함수를 작성한 경우

main1.c
#include <stdio.h>

void swap(int* a, int* b)
{
    int temp;

    temp = *a;
    *a = *b;
    *b = temp;
}

void disp(int a, int b)
{
    printf("a = %d, b = %d\n", a, b);
}

int main()
{
    int a = 10;
    int b = 30;

    disp(a, b);

    swap(&a, &b);

    disp(a, b);

    return 0;
}
$ gcc -o main1 main1.c


예제2) 3개의 파일로 분할한 경우

swap.c
void swap(int* a, int* b)
{
    int temp;

    temp = *a;
    *a = *b;
    *b = temp;
}

disp.c
void disp(int a, int b)
{
    printf("a = %d, b = %d\n", a, b);
}

main2.c
#include <stdio.h>
#include "swap.c"
#include "disp.c"

int main()
{
    int a = 10;
    int b = 30;

    disp(a, b);

    swap(&a, &b);

    disp(a, b);

    return 0;
}
$ gcc -o main2 main2.c

main2.c는 #include문에 의해 swap.c의 소스와 disp.c의 소스가 그대로 옮겨지기 때문에 전처리가 완료된 후의 소스코드는 main1.c와 같아진다.
소스파일만 분할되어 있고 main2.c만 컴파일한 것이므로 분할 컴파일이라 할 수 없다.


예제3) 3개의 파일을 각각 컴파일하여 오브젝트 파일을 생성한 후, 하나의 실행파일을 만드는 경우

swap.c
void swap(int* a, int* b)
{
    int temp;

    temp = *a;
    *a = *b;
    *b = temp;
}
$ gcc -c swap.c

disp.c
#include <stdio.h>

void disp(int a, int b)
{
    printf("a = %d, b = %d\n", a, b);
}
$ gcc -c disp.c

main3.c
void swap(int*, int*);
void disp(int, int);

int main()
{
    int a = 10;
    int b = 30;

    disp(a, b);

    swap(&a, &b);

    disp(a, b);

    return 0;
}
$ gcc -c main3 main3.c

main() 함수에 있던 #include문이 다 없어졌다.
일단 아래의 두 줄은 각각의 파일을 따로 컴파일하였으므로 당연히 필요가 없다.
대신 main() 함수 안에서 쓰이는 두 함수의 원형을 선언해 주었다.

다음으로 #include <stdio.h>에 대해 살펴 보자.
main1.c나 main2.c에서는 disp() 함수에서 printf() 함수가 쓰이고 있다.
그래서 #include <stdio.h>를 써주었으나 이번에는 각각의 파일을 따로 컴파일하는 것이므로 printf() 함수가 쓰이고 있는 disp.c에 #include <stdio.h>를 써주어야 한다.
그리고 main3.c에서는 #include <stdio.h>를 지웠다.

각각의 파일을 컴파일하면 3개의 오브젝트 파일이 생성된다.
그러면 최종적으로 3개의 파일을 합쳐 보자.
$ gcc -o main3 swap.o disp.o main3.o


하나의 프로그램을 만드는데 100명이 투입되었다고 해 보자.
1명당 함수를 10개씩 만들어야 한다.
그러면 main.c 파일에 선언되어야 하는 함수의 원형은 몇 개일까? 1000개이다.
main.c를 만드는 사람은 1000개의 함수 원형을 선언해야 한다.
그런데 #include문을 생각해 보자.
#include문은 해당 파일의 내용을 그대로 붙여 넣어 준다.
그러면 각각의 개발자가 자신이 만든 함수의 원형 10개만 따로 파일로 작성해서 main.c를 만드는 사람에게 넘겨주면 되는 것 아니겠는가?
main.c를 만드는 사람은 100개의 파일을 받아서 그 파일들만 #include문으로 추가해 주면 된다.
그러면 나머지 900라인을 쓸 필요가 없어지는 것이다. 이 파일이 바로 .h로 끝나는 헤더파일이다.
물론 헤더 파일이 필요한 이유가 이것 때문만은 아니다.

예제4) 헤더 파일을 활용하여 각각의 파일을 컴파일하는 경우

swap.c와 disp.c는 예제3과 동일하다.

swap.h
void swap(int* a, int* b);

disp.h
void disp(int a, int b);

main4.c
#include "swap.h"
#include "disp.h"

int main()
{
    int a = 10;
    int b = 30;

    disp(a, b);

    swap(&a, &b);

    disp(a, b);

    return 0;
}

swap.h와 disp.h에서 각각의 함수의 원형을 선언해 주고, main4.c에서 선언되어 있던 함수의 원형을 지우고 두 개의 헤더파일을 포함시켰다.
결국 전처리가 끝나면 main3.c와 같아진다.
하지만 각각의 헤더 파일에 함수가 10개씩 들어 있다면 main3.c의 경우는 20줄을 적어야 했으나 main4.c는 2줄만 적어주면 된다.

'Compile 강좌' 카테고리의 다른 글

라이브러리  (0) 2010.06.26
[컴파일 강좌] 2강 - C++ 분할 컴파일  (0) 2010.06.22
컴파일러 옵션  (0) 2010.05.30
And

__attribute__

|



The '__attribute__ ( ( signal ) )' directive on the function prototype informs the compiler that the function is an ISR and results in two important changes in the compiler output.
  1. The 'signal' attribute ensures that every processor register that gets modified during the ISR is restored to its original value when the ISR exits. This is required as the compiler cannot make any assumptions as to when the interrupt will execute, and therefore cannot optimize which processor registers require saving and which don't.
  2. The 'signal' attribute also forces a 'return from interrupt' instruction (RETI) to be used in place of the 'return' instruction (RET) that would otherwise be used. The AVR microcontroller disables interrupts upon entering an ISR and the RETI instruction is required to re-enable them on exiting.



Avr-gcc defines an interrupt vector as a name that the linker will use to overide the weak reset addresses in the AVR interrupt vector table. For example, the USART0 receiver interrupt on an ATmega128 is defined as:

#define USART0_RX_vect       _VECTOR(18)

The _VECTOR(18) macro is expanded to:

#define USART0_RX_vect     __vector_18

and so the ISR macro used to define the corresponding interrupt handler in code expands to:

void __vector_18(void) __attribute__ ((signal, __INTR_ATTRS));

and this wll compile into the assembler output as the two lines:

.global  __vector_18
__vector_18:

The linker picks up the name as matching the corresponding interrupt vector table entry and makes the replacement into the vector table. Thus an interrupt with this number will arrive at the corresponding interrupt handler.

However in C++ the name is mangled early in the compile process and prevents the linking from occurring. Patch #6805 allows an interrupt handler name to be represented by a number, and while the name will be mangled as usual, the number survives for later linking. The patch provides for an optional numeric argument to the signal function. An interrupt function prototype using this system for the same USART0 receiver interrupt looks like:

void IntName(void) __attribute__ ((signal(18), __INTR_ATTRS)); 

The numeric signal argument is translated by this Patch into the same two assembler lines as above. The given name is still processed according to the language rules. The name is thus independent of the vector number, but the number is attached to the name. Note that for C++, by the time the signal argument is being processed the given name is mangled.

Once implemented the Patch can be used, but to be versatile it will require an additional definition for each interrupt in each processor. The current proposal is to add the new definition along with those existing. For example, the USART interrupt above for the '128 in file iom128.h will now have two lines.

#define USART0_RX_vect         _VECTOR(18)
#define USART0_RX_vect_num     18

The corresponding new interrupt macro for C++ interrupt declarations is defined in Interrupts.h as:

#define ISRN(name, number) void name(void) __attribute__ ((signal(number), __INTR_ATTRS));







And

동적 할당 0614

|
C++

2차원 배열 동적 할당

int * ptr = new int; == int * ptr = new int [1];

*ptr == ptr[0] == *(ptr + 0)
ptr == &ptr[0] == &*(ptr + 0) == (ptr + 0)

int arr[2][3] = {1, 2, 3, 4, 5, 6};
2차원 배열이지만 실제 메모리 상에서는 1차원 배열이 연속적으로 나열되어 있다.
즉, 3칸짜리 배열 2개가 이어져 있는 경우이다.

머릿속 그림)
┏━┯━┯━┓
┃1│2│3┃
┣━┿━┿━┫
┃4│5│ 6┃
┗━┷━┷━┛

┏━┯━┯━┳━┯━┯━┓
┃1│2│3┃4│5│6┃
┗━┷━┷━┻━┷━┷━┛

실제 메모리)
┏━┯━┯━┳━┯━┯━┓
┃ 1│ 2│ 3┃ 4│ 5│ 6┃
┗━┷━┷━┻━┷━┷━┛

│1│2│3│4│5│6


┌─┬─┬─┬─┬─┬─┐
│1│2│3│4│5│6│
└─┴─┴─┴─┴─┴─┘


arr[0]과 arr[1]은 3칸짜리 배열의 이름이며, 시작주소가 된다.
따라서 타입은 int*가 된다.



arr은 3칸짜리 배열묶음이 2칸인 배열의 이름이며, 시작주소가 된다.
따라서 타입은 int(*)[3]이 된다.






int * ptr = new int[3];
int * * dptr = new int[3];


'C++' 카테고리의 다른 글

C++ 생성자  (0) 2010.08.13
생성자와 소멸자  (0) 2010.08.13
c++ 오버라이딩  (0) 2010.07.03
c++ 0610  (0) 2010.06.10
c++0608  (0) 2010.06.08
And

문자열

|
#문자열
1) 문자 : 끝에 NULL('\0') 문자가 없다.
2) 문자 배열 : 끝에 NULL('\0') 문자가 없다.
2) 문자열 : 문자 배열이면서 끝에 NULL('\0') 문자가 있다.



#문자열 상수
printf("%p\n", 1)
printf("%p\n", '1')
printf("%p\n", "1")
모두 상수이므로 코드 영역에 할당된다.

1) 문자 상수 : 작은 따옴표(')를 이용하여 표시
ex) 'a', '3', '%', '+'
2) 문자열 상수 : 큰 따옴표(")를 이용하여 표시
ex) "This is a string."

#상수 테이블
1) 심볼 테이블 : 프로그램 내의 모든 변수의 정보가 기록되어 있다.
2) 상수 테이블 : 프로그램 내의 모든 상수의 정보가 기록되어 있다.
문자열 "string"
%d로 출력 -> "string"의 시작주소
%p로 출력 -> "string"

#문자열 선언
char carray[] = {'a', 'b', 'c'};
연속된 3개의 공간에 문자 'a', 'b', 'c'가 값으로 들어가 있는 문자 배열이다.
char string1[] = {'a', 'b', 'c', '\0'};
연속된 4개의 공간에 문자 'a', 'b', 'c'와 NULL('\0') 문자가 들어가 있는 문자열이다.
각각의 문자가 코드 영역에 순서대로 할당되고, 'a'의 시작주소가 string1에 대입된다.
메모리 할당은 코드 영역에 될까? 스택 영역에 될까?
char string2[] = "abc";
"abc"는 코드 영역에 'a', 'b', 'c', '\0'으로 할당되어 있고, 스택 영역에 배열로 할당된다???
string2에는 다른 값을 대입할 수 없다. 주소이기 때문이다. 각각의 문자변수([0], [1], [2], [3])에 값을 넣어야 바꿀 수 있다.
char *string3 = "abc";
"abc"는 코드 영역에 'a', 'b', 'c', '\0'으로 할당되어 있고, 스택 영역에 할당된 string3에 "abc"의 시작 주소가 들어가 있다.
string3에는 다른 값을 대입할 수 있다. 포인터 변수이기 때문이다.

#잘못된 문자열 선언
char string[3] = "abc";


#gets()
원형 : char *gets(char *str);
읽은 문자열을 가리키는 포인터를 반환하며, 오류가 발생하면 NULL을 반환한다.
개행문자가 나올 때까지 읽어서, 개행문자 이전까지의 문자열에 NULL 문자를 붙인 문자열을 만든 후, 매개변수가 가리키는 메모리 공간에 저장하고, 그 시작 주소를 반환한다.
1)scanf() : 단어 입력
2)gets() : 문장 입력

#puts()
원형 : int puts(char *str)
매개변수가 가리키는 값을 출력하고 마지막에 개행문자를 자동으로 출력한다.
정상적으로 실행한 후 마지막 출력문자를 반환한다. 대부분의 경우 '\n'을 출력한다.
오류가 발생하면 EOF를 반환한다.

#포인터 변수의 초기화
포인터 변수를 초기화하지 않으면 경고 메시지를 보여준다.

#문자열 함수
1) int strlen(char *s)
2) int strcmp(char *s1, char *s2)
3) int strncmp(char *s1, char *s2, int n)
4) char *strcpy(char *s1, char *s2)
5) char *strncpy(char *s1, char *s2, int n)
6) char *strcat(char *s1, char *s2)
7) char *strchr(const char *string, int c)
8) char *strrchr(const char *string, int c)
9) int atoi(const char *s)
10)int itoa(int value, char *string, int radix)


#문자열 배열
char strings[N][M] = {"string1", "string2", "string3", ... "stringN" };
최대 M의 길이를 가지는 문자열 N개를 작성할 수 있다.
코드 영역에 N개의 문자열 공간이 할당되고, 스택에도 N*M만큼의 공간이 할당된다.

#문자열 포인터 배열
char *strings[N] = {"string1", "string2", "string3", ... "stringN" };
길이 제한 없는 문자열 N개를 작성할 수 있다.
코드 영역에 N개의 문자열 공간이 할당되고, 스택에는 그 문자열의 시작주소를 가리키는 4*N만큼의 공간이 할당된다.
메모리 공간이 절약되고 문자열을 편리하게 다룰 수 있다.

원본 문자열 배열의 시작 주소만 가지는 포인터 배열은 원본의 변경없이 포인터만 바꿔주면 정렬 등의 기능을 수행할 수 있다.


#main() 함수
int main(int argc, char *argv[]);
argc, argv는 관례적으로 사용하는 것이며, 사용자가 바꿀 수 있다.
공백을 포함한 문자열을 큰따옴표로 묶으면 하나의 인수로 인식한다.

#문자 함수
1) int isalnum(int c)
2) int isalpha(int c)
3) int iscntrl(int c)
4) int isdigit(int c)
5) int isgraph(int c)
6) int islower(int c)
7) int is print(int c)
8) int ispunct(int c)
9) int isspace(int c)
10) int isupper(int c)
11) int isxdigit(int c)
12) tolower(int ch)
13) toupper(int ch)






#문자열

char string = "test";

string = string+0 = &string[0]
*string = *(string+0) = string[0]
string + 1 = &string[1]
*(string + 1) = *&string[1] = string[1]
*(1+string) = *&1[string] = 1[string]
printf("%p\n", "abc");


배열크기는 변수로 지정할 수 없다??? 디파인된 상수로는 가능??






'C' 카테고리의 다른 글

volatile  (0) 2010.06.26
포인터 상수  (0) 2010.06.24
구조체  (0) 2010.06.12
버블 정렬 c 0609  (0) 2010.06.09
다중포인터  (0) 2010.06.08
And
prev | 1 | ··· | 3 | 4 | 5 | 6 | 7 | 8 | 9 | next