[Harman 세미콘 아카데미] Day_87(Embedded)
포인터 : 특정 값이 저장된 메모리의 주소에 접근하기 위해 사용, (주소를 저장하는 메모리 공간)
ㄴ 식 : int *pa -> 주소를 구한 변수의 형태를 포인터 기호(*)를 통해 지정
위의 코드를 실행하여 조사식을 확인해 보면, a라는 변수가 있는 메모리의 주소가 pa에 0x5ffec4라는 값으로 저장이 되고, 이를 포인터로하여 해당 위치에 10이라는 값을 저장하게 됩니다. 그래서, 0x5ffec4라는 메모리 주소에 10이라는 값이 저장되게 됩니다.
Little Endian : CPU가 읽는 방식 -> 1234를 "일이삼사"라고 읽고, 4 -> 3 -> 2 -> 1 순으로 저장 (LSB가 낮은 메모리에 저장)
Big Endian : 사람이 읽는 방식 -> 1234를 "천이백삼십사" 라고 읽고, 1 -> 2 -> 3 -> 4 순으로 저장 (MSB가 낮은 메모리에 저장)
주소가 기본적으로 8byte이기 때문에, 주소 값을 저장하는 포인터의 크기를 확인하면 8이 나오게 됩니다 (만약 주소가 4bye라면 포인터의 크기는 4byte). int의 자료형을 갖는 a의 변수 크기를 확인하면 4byte임을 확인할 수 있고, char의 자료형을 갖는 b의 변수 크키는 1byte를 담고 있음을 확인할 수 있습니다.
--> 포인터의 크기는 주소의 크기와 동일하다는 것을 알 수 있습니다.
※주의 사항 : 포인터 사용시, 포인터의 변수 Type과 입력값의 Type을 맞춰줘야 합니다. -> 형 변환 사용 가능
ㄴ double a = 3.5; double *pd = &a; int *pi; , pi = (int *)pd ( -> int형으로 변환);
포인터 활용 Swap 함수 예제 :
Swap함수의 매개 변수는 포인터 즉, 주소값이 들어가야 하므로, 10과 20의 값을 저장하고 있는 주소값을 받게 됩니다. temp라는 변수를 활용하여, a와 b가 가리키는 주소값을 변경합니다. 또한, temp는 *주소의값 을 가리키므로 결과적으로 a와 b의 값이 변경됩니다.
결과 :
a = 20, b = 10의 값을 갖게 됨
Swap이 안되는 예제 :
메모리를 참고하여 확인하면, x와 y 그리고 temp에 대한 새로운 영역이 생성이 됩니다. 하지만, a와 b에 대한 주소를 가지지 않기 때문에 a와 b의 변수에 접근을 할 수 없어 결론적으로 값이 swap되지 않습니다. (독립적인 swap 함수 실행)
--> Original Data( a , b )를 접근하기 위해 포인터를 사용
결과 : a와 b의 값 간의 swap이 일어나지 않음
배열과 포인터 :
※ 배열의 이름은 -> 배열의 첫번째 주소 + 대표 주소
ary = 배열의 대표 주소를 나타내므로, 0 1 2의 index를 지니고 *를 통해 값을 저장하게 됩니다. 그리하여 2번째 값은 scanf를 통해 값을 입력 받고, 나머지 0 1번지에는 10과 20의 값을 저장합니다.
결과 : 30의 값은 입력하여 10, 20, 30의 값을 배열에 저장한 모습
※ 포인터도 배열로 사용할 수 있다
배열명과 포인터의 차이 :
int ary[3] -> 4byte *3, 총 12byte의 사이즈
int *pa = ary; -> 4byte 1개, 총 4byte의 사이즈
sizeof(ary[3]) -> 12
sizeof(pa) -> 4
ASCII 코드 : 128개의 문장에 대해 서로 다른 값을 정해놓은 약속
알파벳의 대문자와 소문자의 ASCII상 차이는 32입니다.
문자열 : 문자를 담고 있는 배열, 문자열은 주소를 반환합니다. 즉, "apple"이라는 문자열이 메모리 내 저장된 주소를 반환하여 접근할 수 있도록 합니다.
---> 문자열은 주소(배열의 첫번째 주소)다 !
결과 :
문자열 자체가 주소값이므로 1번째는 "apple"이 저장된 주소를, 2번째 printf를 통해 주소값 + 1의 값을 출력합니다.
3번째 printf는 *(첫번째)의 형태와 동일하므로, "apple"문자열이 저장된 배열의 첫번째인 "a"를 출력합니다.
4번째 printf는 배열의 두번째 값을 지정하므로 "p"라는 값을 출력합니다.
5번째 printf는 "apple" 문자열을 담고 있는 배열의 3번째 값이므로 "p"를 출력하게 됩니다.
문자열을 대입하는 함수 strcpy : 복사하고자 하는 내용을 새로운 배열에 저장
ㄴ strcpy (str1, str2) -> str2의 내용을 str1에 Copy함
원하는 개수의 문자만을 복사하는 strncpy :
ㄴ strncpy(str, "apple-pie", 5) -> "apple" 만 복사
문자열 길이 측정 strlen : Null까지의 길이를 측정
ㄴ strlen(str1) -> if str1 = "apple" --> 5
문자열 비교 strcmp, strncmp : ASCII 코드 값을 기준으로 문자열의 값을 하나씩 비교함
ㄴ strcmp(str1, str2) -> 같으면 0 반환, 비교하여 참이면 1
변수 :
1. 지역 변수 : 해당 scope 내부에서만 사용되는 변수
2. 전역 변수 : 코드 전역에서 사영되는 변수로 메모리의 .bss 영역에 할당됨
※ 같은 이름일 때 우선순위 : 지역 변수 > 전역 변수
3. 정적 지역 변수 : 해당 scope 내부에서만 사용되는 변수, 메모리 공간을 반환하지 않고 유지, .data 영역에 할당
ㄴ ex) static int a;
결과 :
일반 지역 변수를 사용하면, 메모리에 할당되고 삭제되고를 반복하며 a는 지속적으로 0이되고, printf 전 a++을 통해 계속 1을 출력합니다. 정적 지역 변수는 메모리를 유지하기 때문에 계속 a++ 되어 출력되기 때문에 2, 3, 4의 값이 출력됩니다.
다차원 배열 :
ㄴ 형태 : int score[3][4] -> 4개(요소) 짜리가 3묶음으로 존재 -> 묶음을 표현하기 위해 주소 사용 (%s)
ㄴ { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} }와 같은 문장으로 존재
초기화 방법 :
1. 일부 초깃값 생략 가능
2. 행의 수 생략 가능
3. 1차원 배열의 초기화 방식으로 초기화
4. 초기화시 남는 저장공간은 0으로 자동 초기화
5. 행의 수가 생략되고 초깃값이 적은 경우
포인터 배열 선언과 사용
ㄴ ex) char *pary[5] : 5개의 요소로 이뤄진 char type의 pary = *pary x 5 = 8byte * 5 = 40byte
ㄴ 주소를 담을 수 있는 배열
이중 포인터 : 주소를 담고 있는 포인터의 주소를 다시 저장
ㄴ 형태 : int **ppi;
이중 포인터를 사용하여, Data를 담고 있는 주소를 swap하여 Memory에 저장되어 있는 문자열을 변경하는 예제
ary = ary 배열의 첫번째 주소 (1개의 index), ary는 전체 배열의 주소 -> 4byte * 5 만큼 추가된 모습
함수 포인터 : 함수의 주소를 담을 수 있는 변수 (객체 지향 언어의 다형성을 이룸)
ㄴ 형태 : int (*fp) (int, int);
ㄴ 해당 함수를 대신해 주는 역할
-- > 다형성 : fp라는 동일한 변수명을 가지고 "sum", "sub"의 함수의 주소를 할당받아 명령어 수행을 하였기 때문
결과 :
int (*fp)(int, int)로 fp라는 함수 포인터가 선언이 되고, 'sum' 함수의 이름 즉, 주소를 할당받게 됩니다. 그러면, 함수 포인터는 함수의 역할을 대신하기 때문에 10과 20의 값을 입력하면 덧셈 결과인 30을 출력하고 res에 저장하게 됩니다.(sub도 동일)
Callback 함수 : 함수 포인터를 매개변수로 활용
ㄴ ex) void func int(*fp) (int, int);
void 포인터 : 자료형이 없는 포인터, 그러므로 모든 포인터를 다 받게됨 + Casting(자료형 선언) 꼭하기!
ㄴ ex) void *vp;
메모리 동적 할당 (malloc) : heap영역에 임시의 동적 메모리 영역을 할당함
ㄴ 형태 : (type *)malloc(sizeof(type)) -> type만큼의 byte를 할당하고 첫번째 주소를 return
ㄴ ex) (int *) malloc(sizeof(int) * 5) -> 4byte * 5 ==> 20byte를 갖는 메모리 영역 할당
ㄴ free 함수를 만나기 전까지 메모리 영역에 저장하게됨 -> 안하면 "메모리 누수(leak)" 발생
calloc : 할당한 공간을 0으로 초기화
- 형태 : void *calloc(unsigned int, unsigned int);
realloc : 할당한 공간의 크기를 늘이거나 줄임
- 형태 : void *realloc(void *, unsigned int);
사용자 정의 자료형 :
1. 구조체 : 여러 자료형의 변수들을 하나로 묶어 하나의 단위로 정의한 자료형 (관리하기 편함)
결과 :
s1이라는 명칭의 구조체의 구성 요소인 num과 grade를 s1.num, s1.grade의 형태로 값 변수에 접근합니다. 이를 출력하면 아래의 결과와 같이 나옵니다.
구조체 변수의 크기 : 실행 효율을 위해 패딩 바이트를 넣어 바이트 정렬
ㄴ ex) struct student { int num; double grade; } 시, 8byte(double) + 8byte(4bye + padding) = 16byte의 크기를 가짐
---> 구조체의 순서만 바꾼다면 최적화된 메모리 할당으로 실행 효율을 높일 수 있습니다.
구조체 포인터와 -> 연산자 : 해당 주소의 멤버로 접근 가능
결과 :
*ps = &yuni 를 통해 yuni 구조체의 값들에 접근할 수 있음. 이를 ps -> 변수 를 통해 불러옴
공용체 : 공용체의 멤버들에 따라 메모리 byte가 변화함. int 멤버 시 4byte, double 멤버 시 8byte
ㄴ 형태 : union student { int num; double grade; };
열거형 : 관련된 상수들을 의미있는 이름으로 묶어놓음
ㄴ 형태 : enum season {SPRING, SUMMER, FALL, WINTER};
전처리 : Compile 기준으로 처리
분할 컴파일 : 파일을 분할하여 따로따로 compile하고 link를 거쳐 실행 파일을 만듬