[C] 배열, 포인터, 구조체, 메모리 동적 할당 정리
🍐 자료구조 이해하는 데 도움이 될까 해서 간단히 정리한다.. 혼공C 참고해서 요약정리했다!
배열
배열 선언과 사용
- 사용 이유
- 같은 자료형의 많은 데이터를 반복문으로 처리하기 위함
- 메모리에 연속적으로 저장해두고 쪼개서 사용하는 방법
- 저장 공간
int arr[5]와 int형 변수 5개를 각각 선언하는 저장 공간의 크기는 같음
- 단, 변수는 각각의 공간에 이름을 지정함
- 배열은 메모리에 연속된 공간이 할당되어 하나의 이름을 사용
- 이름과 index로 구분함(첨자, index는 0부터 시작)
- 배열 선언 속 숫자(요소 개수) ≠ 배열 요소 사용시 대괄호 속 숫자(인덱스)
- 인덱스 범위를 벗어날 경우
- 컴파일러가 경고 메시지로 알려줄 수도 있으나
- 배열 요소에 포인터 연산을 통해 접근하므로 확실한 에러메시지 X
배열 초기화
- 선언시 최초 한 번만 가능
- 이후에는 배열 요소에 일일이 값을 대입해야 함
- & 중괄호를 사용한 대입 연산 불가능
- int arr[5] = {1,2,3}; ➡️ 3,4 인덱스는 0으로 채움
- int arr[] = {1,2,3}; ➡️ 배열 요소 개수 3개
sizeof 연산자를 활용한 배열 처리
- 요소의 개수가 바뀌면 반복문을 수정해야 하는 경우, 요소의 개수를 직접 계산하여 반복문에 사용
count = sizeof(배열명) / sizeof(배열 요소) // 전공기초때 했던 것 같다문자 저장을 위한 char 배열
apple → char형 배열로 저장하는 것이 효율적 (모든 알파벳 문자는 1byte)
char str[100] = {'h', 'i', 'p', 'p', 'o'}; char str[100] = "hippo"; // 결과는 당연히 같다. 후자가 당연히 더 사용하기 편하다.- apple을 저장하기 위해서 남는 공간이 하나 있어야 함 ➡️ 널 문자(\0)을 저장하기 위함
- 널 문자 쓰임새
- 남는 배열 요소에 0이 채워지는데, char형 배열에 저장된 0을 특별히 널 문자라고 부름
- 모든 문자는 아스키코드 값으로 저장되며 > 널 문자는 아스키코드 값이 0인 문자
- 문자열의 끝을 표시하는 용도로 쓰임
- printf가 배열 크기와 무관하게 문자열만을 정확하게 출력하는 이유
- 문자열을 처리하는 모든 함수에 적용됨
- char형 배열 선언시 초기화하지 않으면
- 마지막 문자 다음에는 반드시 널 문자를 직접 대입해줘야 함.
- 배열의 크기는 최대한 넉넉하게, 최소한 문자열+1.
strcpy 함수의 사용
- char형 배열이 문자열을 저장하는 변수 역할이고
- 새로운 문자열을 다시 저장하기 위해서는 대입연산자가 아닌 strcpy를 사용함
왜?
대입연산자를 사용한다면, 왼쪽에 사용한 배열명이 컴파일 과정에서 배열이 할당된 메모리의 주소값으로 변경됨 100 = “hihi”가 됨. (말이그렇다는거고) strcpy는 그 위치로 이동해서 메모리의 공간을 사용하도록 구현되어 있음➡️ 저장할 문자열의 길이를 파악하여 그 길이만큼만 복사하며 널문자도 붙여줌
hoxy.. 컴파일 안될때
define crt secure no warnings 붙여주면 된다#include <stdio.h>
#include <string.h> // 문자열 관련 함수 원형을 모아놓은 헤더 파일
int main(void)
{
char str1[80] = "cat";
char str2[80];
// strcpy(저장할 배열명, 문자열);
strcpy(str1, "tiger"); // str1 배열에 "tiger" 복사
strcpy(str2, str1); // str2 배열에 str1 배열의 문자열 복사
printf("%s, %s\n", str1, str2);
return 0;
}
문자열 전용 입출력 gets, puts 함수
- scanf는 char형 배열에 문자열 입력이 가능하나, 중간에 빈칸이 있을 경우 빈칸 전까지 입력 ➡️ 빈칸이 있는 경우 빈칸을 포함하여 문자열을 입력하게 해주는 gets(), 출력하는 puts()
컴파일되지 않는다면
crt secure no warnings 추가.- gets 함수 사용법
- gets(char형 배열);
- 문자열 입력 중간에 빈칸, 탭 문자 사용 가능하며 전체를 하나의 문자열로 배열에 저장함(엔터 전까지)
- 입력할 배열의 크기를 검사하지 않으므로, 배열 크기보다 긴 문자열 입력시 메모리 영역 침범 가능성이 있ㅇ므로 입력 시 배열 크기를 고려해야 함
- puts 함수 사용법
- char형 배열의 배열명을 주면 문자열을 출력함
- 단, 문자열을 출력한 후 printf와 다르게 자동으로 줄을 바꿈 ➡️ 경우에 따라 편할 수도 있으나 문자열 출력 후 바로 이어 입력하는 것은 불가능함
포인터
-
사용 범위를 벗어난 경우에도 데이터를 공유할 수 있게 도와줌
-
메모리의 주소
- 주소값 - 바이트 단위로 구분, 0에서 시작하여 1씩 증가함 ➡️ 2 바이트 이상의 크기를 갖는 변수는 여러개의 주소값에 걸쳐 할당됨
주소 연산자
- 변수가 할당된 메모리 공간의 시작 주소 (265)
- &을 사용하여 구함, *은 간접 참조 연산자
- 메모리 주소의 출력 변환 문자 → printf(”%p”, &a);
- 필요할 때마다 주소 연산을 수행하기 보다 주소를 저장하여 사용하는 것이 편리함
int a;int *pa;> 포인터 선언, 주소 위치에 있는 변수의 자료형을 적어야 함pa=&a;> 포인터에 a 주소 대입, pa → a*pa=10> printf(”%d\n”, *pa) 결과는 10, a도 10🍐 scanf(”&d”, &a)와 scanf(”&d”, pa)는 같은 코드이다.
- 선언 동시에 초기화도 가능하다.
int *pa = &a; - const
int *pa = &a;로 상수화 ? a가 pa를 간접참조하여 변경할 수 없게 하기 위함
주소와 포인터의 차이
- 주소는 할당된 메모리 저장공간의 시작 주소 값
- 포인터는 그 값을 저장하는 다른 메모리 공간 ⇒ 특정 변수의 주소값은 바뀌지 않으나, 포인터는 다른 주소를 대입하여 값을 바꿀 수 있음
- 두 포인터가 같은 주소를 저장하는 일(하나의 변수를 동시에 가리키기) 가능
- 주소(상수)는 포인터(변수)처럼 간접 참조 연산자를 쓸 수 있으나, 상수이므로 대입 연산자 왼쪽에 올 수 없음
댓글