"What is A?"
라는 문장을 변수 a에 넣고 싶다.

그럼 
char* s=malloc(sizeof(char)*10);
scanf("%s",s);
printf("s:%s\n",s);

이런식으로 하면 될까?
위의 결과는
s: What
이다.



"what is A"라는 문장을 사용자가 입력하면 버퍼에 그대로 저장이된다.
그런 뒤 scanf를 사용하면 버퍼에서 공백이 있는 곳까지 잘라 변수에 저장하게 된다. 

프로그램 코드와 실행 결과











이런식으로 코딩하게 되면 "What(space)is(space)A(Enter)"를 입력했을 때
s=What
s1=is
s2=A
가 된다. 

What is A 를 입력한 뒤 다른 입력 없이도 scanf함수를 사용할 때마다 buffer에서 읽어와서 변수에 저장하는 것이당


실행결과




그럼 공백도 포함해서 s에 넣고 싶으면 어쩌나..

fgets를 사용하면 된다. 
fgets(char* s, int size, File* stream);
이런 형태인데 stream으로부터 한줄씩(개행문자가 오기 전까지) 읽어서 s에 넣는 함수이다.
fgets함수 정리는 다음에... 





http://kaspyx.kr/2

int index=0;

printf("%02x",index);


//%(02)x=%(출력하고자하는 자리수)x


printf('%02x",index)의 결과=00

printf('%05x",index)의 결과=00000


char buff[100]="note";
char* pstr="book";
char* p;
strcat(buff, pstr);
puts(buff);
strncat(buff, "12345",3);
puts(buff);
strcat(p,buff);        //오류: 쓰레기 주소에 값을 넣으려고 시도

strcat(pstr, buff);    //오류: 상수값에 값을 집어넣으려고 시도


alloca => stack 세그먼트 영역에 동적메모리 할당

장점: 함수 호출, 반환시 스택 영역을 사용하는데 스택영역에 동적메모리를 할당하면 함수 반환시 메모리가 자동 해제되기때문에 

free할 필요없음

2차원 배열을 선언 후 함 수 이자로 넘겨줄 필요가 있는 경우




이케 받으면 오류가 난다.

그 이유는?
이중배열은 실제로 메모리에 저장될 때는 일차원으로 저장된다. 

int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};


이렇게 선언한 배열은 


int a[12]={1,2,3,4,5,6,7,8,9,10,11,12};


라기 보다는 


int a[3]={int[4], int[4], int[4]}


이것과 같다고 볼 수 있다.


무슨말이냐






이중 배열의 주소체계는 이러하다. 

a자체는 총 3개의 int[4]원소를 가진 배열의 시작주소를 나타내고

a의 첫번째 원소 a[0]( *(a+0)과 같음 )=int[4]={1,2,3,4}

      두번째 원소 a[1]=int[4]={5,6,7,8}

      세번째 원소 a[2]=int[4]={9,10,11,12}가 된다. 

따라서 a[0]의 주소와 a[1]의 주소는( 4byte(int 크기)*4=)16byte 차이가 난다. 


a[0]은 총 4개의 int원소를 가진 배열의 시작주소를 나타내므로 

a[0]의 첫번째 원소 a[0][0]( *(*(a+0)+0)과 같음 )=int={0}

         두번째 원소 a[0][1]=int={1}

                        ...

과 같다. 

따라서 a[0][0]의 주소와 a[0][1]의 주소는 4byte차이가 난다. 


이를 코드로 확인해보면 



>>결과


이케 된다. 

a의 주소와 그 다음 주소 값을 확인해보면 16진수로 48byte(3*(int(4byte)*4))=(0x30) 차이가 나는 것을 알 수 있다

a의 첫번째 원소(*a=a[0])의 주소값과 두번째 원소(*(a+1)=a[1])의 주소값은 16byte(int*4)=(0x10) 차이가 나고

a[0]의 첫번째 원소(*(*(a+0)+0)=a[0][0])의 주소값과 두번째 원소(*(*(a+0)+1)=a[0][1])의 주소값은 4byte(int)=(0x04) 차이가 난당


여기서 나는 헷갈렸던게 a는 int 포인터 타입의 변수인줄 알았다.

즉 a는 배열을 메모리에 할당하고 그 주소값만 가지고 있는 변수라고 생각했는데

그게 아니라 그냥 그 배열 자체를 나타내는 변수였던 것이다

이게 무슨 말이냐면 변수를 사용할 때 

int a=4;

이케 선언하면 4라는 값을 메모리에 할당한 후 그 메모리의 주소값을 나타내는 애가 a이다. 

a라는 변수를 사용해서 4를 할당해준 메모리에 접근할 수 있는 것이다. 

(이게 변수를 만들어 주는 이유. 우리가 항상 메모리 주소를 기억하고 사용하기 불편하므로 변수를 사용해서 쉽게 사용하는 것)

a는 4를 갖고 있는 메모리자체라고 볼 수 있음

나는 이걸 a는 4라는 값을 갖는 int 자체를 나타내는 변수 라고 표현했다.

근데 포인터형 변수는 그 메모리 자체를 나타내는 변수가 아니라 메모리 주소값만을 갖는 변수이다

int* b=&a

라고 선언하면 a는 메모리의 주소값을 포인터형 변수인 b에게 알려주는 것이다.



그림을 보면 a는 4값을 담고 있는 메모리이고 b는 4를 담고 있는 메모리 주소를 담는 메모리이다.(아...쓰면서도 헷갈려)

b는 0xb4라는 주소를 따로 갖는 4를 담는 메모리와는 별개의 변수이지만 a는 4를 담는 메모리 자체인것이다.

부디 나중에 보면서도 이해되길....


다시 처음으로 돌아가서 왜

함수에서 

int a[3][4]로 선언한 배열을 

int** arr 로 받으면 에러가 나는가?


a는 배열을 가지고 있는 메모리 자체를 나타내는 변수고

arr은 정수값을 갖는 메모리를 찾아가기 위한 주소 값을 갖는 포인터형 변수이기 때문이다.





arr로 넘어오는 것은 a의 주소값인 0x00 이다. 

arr은 0x64번지에 메모리를 할당해주고 0x00이라는 주소값을 담고 있는 변수!

또 arr은 이중 포인터이기 때문에 처음에 0x00을 따라간 후에 0x00번지에 담겨 있는 값도 

8byte의 주소값(64bit에선 8byte, 32bit에선 4byte)으로 인식을 한다. 

0x00번지부터 0x08번지까지에 있는 binary값을 읽은 다음 그값을 16진수의 주소(0x21)로 인식을 하는 것이다. 

그래서 이를 따라간 다음 int 값으로 읽기 위해 4byte의 binary 값을 읽으면 값이 이상해진다.

(간단하게 그림을 나타내기 위해 정수값을 주소값으로 변환하는걸 그림에서는 야매로 0x21로 나타냈는데 

실제 컴퓨터에서는 정수값 1과 2를 연달아 binary값으로 읽은 다음 16진수 주소값으로 바꾸면

0x0000000200000001이 된다. 

정수값 1은 0x00000001로 저장 되고 정수값 2는 64bit에서는 0x00000002로 저장되는데 

little endian으로 8byte를 읽으면서 0x0000000200000001 이 되는 것이다)

그래서 제대로 넘겨주기 위해선



이케 써야된다 



뭐가 다르냐면....
int(*arr)[4]로 넘겨주는 것은 
arr이 가리키고 있는 건(*arr)
"int형 데이터 4개를 갖고 있는 배열(int[4]"을 담고 있는 메모리 자체라는 것을 알려주는 것이다. 
(*arr)이 int형 데이터 4개를 갖는 배열이라는 것을 알기 때문에 (*arr)은 16byte의 크기라는 것도 알고 있고
(*arr)에서 1을 더하면 arr주소에서 16byte를 더한 메모리로 이동할 수 있는 것이다. 
또 (*arr)이 int형 데이터를 4개 가지고 있는 배열이라는 것도 알기 때문에 (*arr)의 첫번째 원소를 읽기 위해서 
(*arr)[0]을 하면 시작 주소에서부터 int의 크기인 4byte를 읽을 수도 있다.
메모리 자체에 접근 가능
여기서 또 헷갈렸던게....@.@

내가 디버깅하며 테스트한 코드




디버깅하며 확인한 변수의 주소와 그 안에 들어있는 값


>>결과 




int(*arr2)[4]로 넘겨준 arr2를 보면 0x000000020bff8fbd0번지에 할당되어 

a의 주소값 0x000000020bff8fbf8을 갖고 있는 포인터형 변수라는 것을 알 수 있다. 

따라서 arr2의 사이즈는 8byte (64bit운영체제에서의 주소크기)이고 arr2가 가리키는 것은(0x000000020bff8fbf8번지에 있는 데이터=(*arr))

int[4]라는 것을 알려줬기 때문에 *(arr2)의 사이즈는 16byte라는 것을 알고 있다.

그리고 *(arr)이 가리키는 데이터(0x000000020bff8fbf8번지에 있는 데이터=*(*arr))는 int형이니까 4byte로 출력된다. 

(arr2=*arr2=*(*arr2)인 이유는 arr2가 이중 배열을 가지고 있는 메모리 자체를 나타내는 변수 이기 때문. 위에 설명해놨다)

이렇게 잘 넘어와서 넘겨받은 배열의 내용이 잘 출력된다.

반면 int** arr로 받은 arr을 보면 0x000000020bff8fbd0번지에 할당되어 
a의 주소값 0x000000020bff8fbf8을 갖고 있는 포인터형 변수라는 것을 알 수 있다. 
arr의 사이즈는 8byte이고 arr이 가리키는 것은 다시 포인터형이기 때문에 *arr의 사이즈는 주소크기인 8byte가 되고 
*arr을 int형 데이터가 4개 담겨있는 배열로 읽는 것이아니라 
*arr에 있는 값을 주소 형태인 8byte단위로 읽는다. 
그래서 *arr에 있는 데이터  (4byte 숫자 1(0x00000001)과 arr[0][1]에 있던 4byte 숫자 2(0x00000002))를 읽어
0x0000000200000001 형태의 주소로 읽는다. (*arr=0x0000000200000001)
(*arr+1(arr[1])은 그 다음 8byte 데이터인 0x0000000400000003(정수 3과 정수 4를 8byte 주소로 읽은 것)으로 읽는 것 봐라...절레절레...)
*(*arr)(=0x0000000200000001이 가리키는 데이터)는 당연히 없다. 



이중배열을 
int** b;
이렇게 선언해주고 동적할당 해주면 
함수인자로 
int printArr(int** arr)
이렇게 넘겨줘도 에러가 안난다.
무슨 차이인지는 2탄에 정리....



strcmp(비교할 대상,비교할 문자열)


함수를 추상화 하여 나타내면


strcmp(s1,s2){

if(s1==s2) return 0;

if(s1>s2) return 1;

if(s1<s2) return -1;

}


-한번에 주석달기

CTRL+c+k -> CTRL+c


-한줄만 주석 넣기

CTRL+k+c


-한줄만 주석 해제

CTRL+k+u


-한번에 주석 해제

CTRL+c+k -> CTRL+u


-자동정렬

CTRL+k+f


-브레이크 포인트 설정

  • 모든 브레이크 포인트 삭제 : CTRL+SHIFT+F9
  • 현재 커서의 브레이크 포인트 설정/해제 : F9


Ctrl-F7 : 현 파일만 컴파일     : 현 프로젝트만 빌드
Ctrl-Shift-B : 전체 프로젝트 빌드
Ctrl-F5 : 프로그램 시작


How to recognize executable files.


현재 디렉토리의 파일들



파일리스트를 출력하는 소스 test2.out 실행 결과 



sicsim.c.swp는 작성 중에 vim 종료를 비정상적으로 해서 디스크에 swap되어 있는 것 같다. 

디렉토리는 끝에 '/'를 출력하고 실행가능 파일은 끝에 '*'를 출력


그럼 이제 소스를 보장




괄호 안의 숫자들은 statbuf.st_mode를 8진수로 출력한 것


값들의 의미를 살펴보자면 (6개의 숫자 중 하위 3개의 숫자들만)


이런거라고 한다. 



위에 include한 헤더파일 중 sys/stat.h에 File type과 File mode bit를 위한 symbolic name들이 정의 되어 있다.

요기 참고



실행 여부를 가늠하기 위해서는 S_IXUSR, S_IXGRP, S_IXOTH 값이 필요하다. 

위의 값들은 8진수로 각각 100,10,1의 값을 가지고 있다. 

그래서 위에서 출력한 파일 모드의 값들과 &(AND연산)을 하면 실행가능하면 1, 불가하면 0의 값이 나오는 것이다. 


S_ISDIR은 헤더파일에 정의되어 있는 함수로 파일이 디렉토리인지 아닌지를 출력해주는 함수이다.


※포인터의 개념


C에서 int형 변수 a를 선언후 10을 대입한 후의 메모리를 추상적으로 나타내면 다음과 같다. 


int a=10;

이때 a를 담고 있는 메모리 주소를 가리키는 포인터는 &a 로 표현한다. 



포인터 &a100번지에 있는 정수형 공간을, 또는 100번지에 저장된 정수가리킨다고 말한다. 

  • a의 포인터 → &a
  • &: 포인터(주소) 연산자

- &a(a의 포인터)에 들어있는 정보

  • 변수 a가 저장된 공간의 시작주소: 100
  • 변수 a가 저장되는 방식: int(4byte 정수)


※포인터가 가리키는 공간/값 얻기

포인터가 가리키는 공간을 얻는 참조연산자: *
- 사용법: * 포인터
int a;  →   *(&a)

해당 공간을 변수처럼 사용할 수 있다.


-포인터가 가리키는 값 읽어서 변수에 저장하기 

int a=10, b;

b = *(&a)


-포인터가 가리키는 공간에 값 저장하기

int a, b = 10;

*(&a) = b;


※포인터 저장


그럼 포인터는 어떤 자료형에 저장해야 하는가?

int a; int b = &a;

이렇게 하면 되나?

그럼 'int'의 간접 참조수준이 'int*'과(와) 다릅니다. 

라는 에러를 만나게 될것이다. 

포인터를 저장하기 위해서는 새로운 자료형 변수가 필요하다. 


- 포인터 자료형의 선언

  • 가리키는 자료형 *  →  int *, char*, double *, ...
  • int * : 포인터 자료형(*)인데 가리키는 자료형이 int 형


int a = 10;

int *p = &a;


이 때 '*'는 포인터라는 의미이며 참조연산자의 의미가 아님. (*(&a)에 쓰였던 *와는 다름)


- 가리키는 변수를 접근하여 읽고 쓰기



+ Recent posts