일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 버킷리스트
- 치유
- 취약점
- 여행
- 여행계획
- Shared Elements
- bytecode
- 심리학
- javap
- 회피
- Transition
- Navigation Component
- 일상회피
- abstract
- Recylcer
- Android
- static
- bytecode 분석
- extends
- throws
- 보안
- ㅇ
- 심리여행
- 일상탈출
- HelloWorld
- 보안취약점
- jvm
- opcode
- IMPLEMENT
- Interface
- Today
- Total
패스트터틀
[basic] c 배열 포인터 메모리 본문
본 포스팅은 목차순서에 연결성이 없고 단지 궁금증을 풀고 순서없이 적은 목차입니다.
목차-----------------------------------------------
-포인터가 필요한이유?
-포인터에서 *int 와 *char의 차이점
-배열과 포인터 메모리
-배열과 메모리 헷갈리니까 메모리구조에서 확인하기
-메모리상에서 숫자 읽기
-메모리 숫자를 거꾸로 읽는이유
-scanf 말고 scanf_s 사용이유
-형변환은 왜필요한가?
-포인터 형변환은 왜 필요한가?
-포인터* = 포인터*는 무슨의미인가, 또한 선언과 동시에 int 포인터* = 무엇인가?
-포인터 형변환
-동적할당
-sizeof(int),sizeof(int*)
-1차원 동적할당 2차원 동적할당 3차원 동적할당
-NULL 포인터 역참조는 왜 발생하는가
-MMU란?
-segmentation 그리고 paging은 무엇인가?
-내부단편화,외부단편화란 무엇인가?
-segmentation fault는 왜 발생하는가?
-*,**,***,****의 차이
-컴파일(compile)과 런타임(runtime)차이점
-malloc()
-qsort()와 cmpfunc()
-memset()
-----------------------------------------------------
-기타 잡다한 정보
-----------------------------------------------------
포인터가 필요한이유?
1. 지역성극복
2. 연속된 메모리의 참조
3. 힙영역참조
4. 재귀적함수
포인터에서 *int 와 *char의 차이점
unsigned *char말고 unsigned *short(2byte) 로 할때는
d2 02 96 49 에서
d2 02 만을 읽어오면 읽어오는순서는 02 d2순서대로 읽어온다.(리틀 엔디안방식)
*int *char *short 등등 차이는 불러오는 주소값을 1바이트냐 2바이트냐 4바이트냐를 정하는거이고
이와더불어 불러온값을 int,char,short로 한정하여 담는것이다.
예를들어서 char* 일경우 1바이트를 불러오고 그 값을 범위또한 char 로 한정하여 담음
( 왜 이런식으로 하는지는 모르겠음ㅇㅇ)
그리고 char의 범위는 -128~127 이기 때문에 d2를 불러올때 d2는 char형태로 변환되어서 불러옴
결론적으로 불러오는 주소의 양과 불러오는 데이터의 범위가 차이가 있는것
그러하여 포인터 형변환이 필요하다.
배열과 포인터 메모리
배열 ch[20]에서 ch는 주소값을 나타낸다.
1. t를 출력하면 변수가 아닌 주소가 출력이된다.
2. 포인터랑 똑같이 변수를 출력할경우 주소값이 나온다.
--- 아니다. 틀리다.(아래사진)
3. 스택영역에서 어떻게 찾는지는 잘모르지만 스텍을 쌓는방식으로 찾는것이 아닐까 예상함
배열과 메모리 헷갈리니까 메모리구조에서 확인하기
#include
void main(int argc, char* argv[]) {
char t[20] = "asdasd";
int* a = 2;
int b = 4;
a = &b;
printf("%p\n", a);
printf("%p\n", &a);
printf("%d\n", *a);
}
에서 포인터 a의 주소는
0x0025FC68에있고 그 안에 54 fc 25 00 (역순으로) 되어있음
그리고 0x0025FC54에는 변수값이 들어감
메모리상에서 숫자 읽기
123 = 7b 00 00 00 >>> 7b
1234 = d2 04 00 00 >>> 04 d2
123456 = 40 e2 01 00 >>> 01 e2 40
1234567890 = d2 02 96 49 >>> 49 96 02 d2
메모리 숫자를 거꾸로 읽는이유
이유 :
char t[20] = "asdasd"
비정상적 배열 선언
scanf 말고 scanf_s 사용이유
scanf_s는 입력시 메모리크기를 설정해주는것이 scanf와 차이가있는데 이렇게 메모리크기를 설정해주는것은
버퍼 오버플로우를 막아주기때문이다.
보안공부를 하면서 버퍼 오버플로우가 얼마나 위험한것임을 알고있기때문에 위험성은 인지하고있었다.
근데 사실 이것에 대해서는 전혀 몰랐는데 community를 설치하고 c를 컴파일할때 scanf를 사용하자마자
scanf_s를 사용하라는 경고로 실행을 할수없어서 왜 visual studio에서 이것을 권고하는지 찾아보다가 알게되었다. 권고사항이라고 하니 그냥 사용해야겠다.
형변환은 왜필요한가?
원래 컴파일러가 자동으로 형변환을 알아서 해준다.
num1 = num2일경우에는 num2가 num1의 변수에 맞춰서 자동으로 해줌
(연산자를 기준으로 오른쪽에서 왼쪽으로 형변환)
그렇다면 자동으로 형변환을 알아서 해주는데 왜필요한가?
http://blog.naver.com/PostView.nhn?blogId=ljc1928&logNo=220606601156
이곳에서 나와있듯이 형변환을 하지않아도 자동으로 되긴 되지만 하지않았을경우
값의 손실을 가져올수있기때문이다.
포인터 형변환은 왜 필요한가?
우선 기존에 형변환이 필요한 이유는 자동으로 변환되지만 프로그래머가 의도한 값을 메모리에 넣고 싶거나 출력하기 위해서였다.
그렇다면 포인터 형변환은 왜 필요한것일까?
결론은 찾아보니 형변환을 하지 않아도 된다고 한다.
왜냐하면 컴파일러가 자동으로 바꿔주기 때문이다.
하지만 하라고 권장한다.
https://untitle-ssu.tistory.com/69
또한 실수를 줄이기 위해서 한다고 한다.
포인터* = 포인터*는 무슨의미인가, 또한 선언과 동시에 int 포인터* = 무엇인가?
선언과 동시에 초기화하는
int 포인터* = 는 바로 주소를 연결해주는것을뜻함
int *p = 0;의 의미는 0의 주소를 연결해준다는뜻
int *p = 0x11223344; 의 의미는 11223344의 주소에 연결해준다는것을 의미(에러발생)(허용되지않는곳에 접근)
주소를 연결해준다는뜻 예를들어서
int a = 400;
char c = 5;
int* numPtr1=NULL; // int형 포인터 선언
char* numPtr2 = NULL;
numPtr2 = &c;
numPtr1 = &a;
numPtr1 = numPtr2;
포인터 형변환
그냥 앞에 numPtr2 = (int*)numPtr1; 처럼 명시해주면된다.
정확히 말하면 형을 변환하는게 아니고 실수를 줄이기 알려주는것이다.
동적할당
동적할당을 알기위해서 우선 알아야되는 개념이 heap영역임
힙영역은 사용자가 임의로 만들어주는 영역으로 그냥 프로그래밍을 하다보면 이 영역이 왜필요한가싶다
하지만 메모리를 잘 다뤄야하는 c에서는 필수적인것이 메모리의 낭비적인 측면에서 내가 원하는만큼의 메모리를 가지고 놀수 있어야하는데 이 부분을 만족시켜주기위해서 heap영역이 필요하다.
우선
void main(int argc,char* argv[]){
int i=10;
int t[i];
return 0;
}
이 왜 컴파일이 되지 않는지부터 알아야하는데
우선 스택 영역에 할당될 메모리의 크기는 컴파일 타임(컴파일 하는 동안)에 결정된다고 되어있다.
배열 선언문의 크기값은 변수로 지정할 수 없고 반드시 상수로만 지정할 수 있기 때문이다
위에 함수가 안되는이유는
i 가 10으로 초기화되었다는사실은 컴파일시에는 모르고 런타임때 알수있기 때문에 컴파일시에는
i의 값이 무엇인지 알수없어서 t[i]에서 오류가 난다. 즉 초기화는 런타임때 알수있기에 t[i]는 불가능하다.
그리고 기본적으로 알아야할 운영체제의 메모리 관리 방법
16비트 운영체제에서는 응용 프로그램이 임의의 주소 공간을 액세스할 수 있었지만 32비트의 보호된 운영체제들은 안전성을 높이기 위해 응용 프로그램이 임의의 메모리를 액세스하는 것을 금지하고 있다. 반드시 운영체제를 통해서만 메모리를 할당받을 수 있다.
sizeof(int),sizeof(int*)
int는 무엇이고 int*란 무엇일까?
int의 크기는 4
int*는 포인터의 크기이기때문에 즉 주소의 크기이다.
결국에는 int*는
32비트일경우 주소의크기가 4바이트이고
64비트일경우 주소의크기가 8바이트이다.
더 쉽게 말해 int*는 주소를 담을 간의 크기를 말한다
32비트에서는 주소를 0x11223344 하여서 총 4바이트가 필요하지만
64비트에서는 주소를 0x1111222233334444 하여서 총 8바이트 필요하다.
32비트에서는 char*,int*,short*,double* 전부 4바이트이다.
64비트에서는 char*,int*,short*,double* 전부 8바이트이다.
1차원 동적할당 2차원 동적할당 3차원 동적할당
1차원
#include <stdio.h>
#include <stdlib.h> // malloc, free 함수가 선언된 헤더 파일
int main()
{
int a=0;
printf("size : ");
scanf_s("%d", &a);
int* arr;
arr = (int*)malloc(sizeof(int) * a);
for (int i = 0; i < a; i++) {
printf("%p\n", arr + i);
}
}
2차원
#include<stdio.h>
int main(int argc,char* argv[]){
int height = 6, width = 8;
int **arr;
arr = (int**) malloc ( sizeof(int*) * height );
for(int i=0; i<height; i++){
arr[i] = (int*) malloc ( sizeof(int) * width );
}
}
NULL 포인터 역참조는 왜 발생하는가
대부분의 운영체제에서는 이 널 포인터를 메모리의 첫 페이지 주소 0(0x00000000)로 사용하고 있습니다. 따라서 여기에 접근하면 보통 프로그램이 비정상적으로 종료합니다.
예를들어 ) int*p = 0;으로 접근하ㅁ면 종료됨
그런데
절대다수의 코드에서 Null Pointer Dereference 는 이렇게 눈에 빤히 보이는 코드에서 잘 발생하지 않습니다. 보통 라이브러리를 잘못 다룰 때 많이 쓰이죠. 많은 라이브러리 함수는 사용자가 의도한 값이 없거나, 기타 에러 등의 이유로 널 포인터를 리턴하는 경우가 많습니다. 이 리턴된 값을 확인 없이 바로 무언가를 쓸 때 Null Pointer Dereference 가 종종 발생합니다.
그러니까 메모리0에 접근하는경우보다 함수를 가져다쓸때 return nulll 로 인하여 nullpointer 역참조가 발생한다는것
그리고 malloc 함수를 호출 시 메모리가 부족하면 널 포인터가 리턴됩니다.
여기에서는 malloc으로 메모리를 할당받은줄 알았지만 실제적으로 메모리를 할당받지 않았던것
그래서 결국 null pointer 역참조가 발생하였음
이렇게 NULL일경우 return -1을 하면 정상적으로 작동한다.
MMU란?
segmentation 그리고 paging은 무엇인가?
메모리를 관리하는 기법으로
페이징과 세그멘테이션이 있는데
페이징은 피지컬메모리(실제메모리)에서 분할한 하나의 프레임과 일치시켜서 관리하는것이고
세그멘테이션은 페이징과 달리 서로다른 메모리를 피지컬메모리에 관리하는것이다.(사용자관점)
같은 크기의 세그먼트들은 같이 연속된 공간안에있으며 크기별로 나뉘어져있음(?)
세그멘테이션 : 외부단편화
페이징 : 내부단편화
내부단편화,외부단편화란 무엇인가
내부단편화는 메모리가 50M 필요한공간은 30M여서 20M가 낭비되는것
외부단편화는 남은메모리가 50M + 50M인데 나는 70M를 요구할경우 용량이 충분함에도 활용하지못하는것
segmentation fault는 왜 발생하는가?
- 이미 메모리 해제 된 변수에 접근할 때
- read-only로 설정된 메모리 영역에 쓰려고 할 때 등이 있습니다.
그러니까 세그멘테이션으로 나눠놓고 쓰는데 할당 해줬는데 해당 세그멘테이션을 초과한곳을 메모리에서 사용하려고 할때 발생함
그렇다면 nullpointer는 무엇인가?
널포인터(Null Pointer)란 아직 할당되지 않는, 어떠한것도 안가르키는 포인터
대부분의 운영체제에서는 이 널 포인터를 메모리의 첫 페이지 주소 0(0x00000000)로 사용하고 있습니다.
-> 널포인터 역참조로 이동
*,**,***,****의 차이
컴파일(compile)과 런타임(runtime)차이점
컴파일은 프로그래머가 작성 소스코드 -> 기계어코드로 변환
런타임은 응용프로그램이 동작되어지는 때
컴파일이 완료되더라도 런타임오류 발생가능
malloc()
a = malloc();
a의 데이터의 주소로 이동하여 그 주소의 데이터를 malloc한 주소값과 연결한다는뜻
malloc()은 heap영역에 메모리를 할당해줌
#include <stdio.h>
#include <stdlib.h> // malloc, free 함수가 선언된 헤더 파일
int main()
{
int num1 = 20; // int형 변수 선언
int* numPtr1; // int형 포인터 선언
numPtr1 = &num1; // num1의 메모리 주소를 구하여 numPtr에 할당
int* numPtr2; // int형 포인터 선언
numPtr2 = malloc(sizeof(int)); // int의 크기 4바이트만큼 동적 메모리 할당
printf("%p\n", numPtr1); // 006BFA60: 변수 num1의 메모리 주소 출력
// 컴퓨터마다, 실행할 때마다 달라짐
printf("%p\n", numPtr2); // 009659F0: 새로 할당된 메모리의 주소 출력
// 컴퓨터마다, 실행할 때마다 달라짐
free(numPtr2); // 동적으로 할당한 메모리 해제
return 0;
}
int* numPtr2; // int형 포인터 선언
numPtr2 = malloc(sizeof(int)); // int의 크기 4바이트만큼 동적 메모리 할당
numPtr2는 스택영역
numPtr2가 가르키는 영역은 힙영역
해석: numPtr2의 주소가 malloc으로 할당한 힙영역의 주소로 연결
int *ptr = (char*)malloc(sizeof(int));
는 포인터처럼 형변환을 하자 실수를 줄이기위해서, 사실 할필요는 없지만
qsort()와 cmpfunc()
qosrt : 퀵정렬 해주는 함수
cmpfunc : 퀵정렬해줄때 필요한함수
qsort( [ 값(ex. 배열) ] , [정렬할곳 예를들면 1,2,3,4,5 중에 3을 입력했다면 1,2,3만 정렬] , [정렬할값의 크기], [비교함수]);
cmpfunc(const void* a,const void* b){
return (*(int*)a - *(int*)b);
}
a랑 b를 받은것을 정렬할때 int형으로 정렬하여 정렬하면
약속한것임
- a < b일 때는 -1을 반환
- a > b일 때는 1을 반환
- a == b일 때는 0을 반환
https://dojang.io/mod/page/view.php?id=638
memset()
#include <stdio.h>
#include <stdlib.h> // malloc, free 함수가 선언된 헤더 파일
#include <string.h> // memset 함수가 선언된 헤더 파일
int main()
{
long long *numPtr = malloc(sizeof(long long)); // long long의 크기 8바이트만큼 동적 메모리 할당
memset(numPtr, 0x27, 8); // numPtr이 가리키는 메모리를 8바이트만큼 0x27로 설정
printf("0x%llx\n", *numPtr); // 0x2727272727272727: 27이 8개 들어가 있음
free(numPtr); // 동적으로 할당한 메모리 해제
return 0;
}
기타 잡다한 정보
1.
func라는 함수에서 func(n)을 할때
call by value(콜바이 벨류) = 메모리를 차지함
func라는 함수에서 func(&n)을 할때
call by references(콜바이 리퍼런스) = 메모리를 차지안하지만 원본값이 변경될 위험이있음
2.
------------------------------------
stack(지역,매개,리턴)
------------------------------------
heap(동적메모리할당, 이 영역접근위해서는 malloc
bss
data(전역,정적,배열,구조체)
code(text)(hex나 bin파일 메모리다)
-------------------------------------
3.
포인터 연산 +,-는 크기만큼 더하거나 뺀다.
4.
const는 변수를 상수화하기 위해 사용한다.
const int *n; (*n의값 변경 불가)
int* const n = &b; (포인터 주소변경불가)
5.
int *temp = 1024 해버릴경우에는
실행은 가능하다만(억지로 할경우) 주소를 지정해주지 않았기때문에 쓰레기값으로 들어간다.
이말인즉슨, 알수 없는 이상한 주소에 1024라는 값이 들어가는것이다.
실행은 되지 않는다.
6.
포인터도 변환할때 자료형이 다르면 컴파일경고 발생함
'Development language > c' 카테고리의 다른 글
[basic] 함수 - Function (0) | 2019.08.29 |
---|---|
[basic] 파일 (0) | 2019.08.29 |
[basic] 구조체 포인터 함수 포인터 (0) | 2019.08.21 |