C 언어/[책 정리] 혼자 공부하는 C언어

    이중 포인터와 배열 포인터

    이중 포인터와 배열 포인터

    포인터가 가지는 주소 값은 처리하려는 데이터가 아니었다 포인터를 사용하는 목적이 가리키는 데이터를 사용하기 위한 것이었는데 이제부터는 주소 값 자체를 처리할 데이터로 생각해보자 즉, 주소를 저장한 포인터도 하나의 변수이고 따라서 그 주소를 구할 수 있으며 또 다른 포인터에 저장하고 가리키는 것도 가능하다 => 찾아간 주소 위치에 주소 값이 있다는 말이 된다 이중 포인터 개념 포인터도 메모리 저장 공간을 갖는 하나의 변수이다. 따라서 주소 연산으로 포인터의 주소도 구할 수 있다 예를 들어 어떤 변수를 가리키는 포인터 pi가 있고, 이 포인터 pi가 할당된 메모리의 시작 위치가 200번지일 때 그 주소를 구하면 다음과 같다 이 주소를 저장하는 포인터가 이중 포인터이다. 즉, 포인터의 주소는 이중 포인터에 저..

    배열을 처리하는 함수 (1)

    배열을 처리하는 함수 (1)

    배열을 입출력하는 함수 배열명을 꼭 포인터에 넣는 방식으로 배열을 처리할 필요가 없다 하지만 함수로 배열을 처리하려면 포인터가 필요하다 ary 배열에서 배열명 ary는 첫 번째 배열 요소의 주소이다 이 주소 값을 함수의 인수로 주면, 함수는 이 값을 받아 주소 계산을 통해 모든 배열 요소를 사용할 수 있다 이때 배열명을 받을 함수의 매개변수 자리에 포인터가 필요하다 배열의 값을 출력하는 함수 ==> 배열을 처리하는 함수에 필요한 것은 배열의 주소이다 배열을 처리하는 함수 #include void print_ary(int* pa); // 함수 선언 int main(void) { int ary[5] = { 10, 20, 30, 40, 50 }; print_ary(ary); // 배열명을 주고 함수 호출 re..

    배열과 포인터의 관계 (2)

    배열과 포인터의 관계 (2)

    배열명과 포인터의 차이 포인터가 배열명처럼 쓰이기는 하지만 서로 다른 점이 더 많다 차이점 1. sizeof 연산의 결과가 다르다 배열명에 사용하면 배열 전체의 크기를 구하고 포인터에 사용하면 포인터 하나의 크기를 구한다 따라서 배열명을 포인터에 저장하면 포인터로 배열 전체의 크기를 확인하는 것은 불가능하다 int ary[3]; int* pa = ary; sizeof(ary) // 12바이트, 배열 전체 크기 sizeof(pa) // 4바이트, 포인터 하나의 크기 차이점 2. 변수와 상수의 차이가 있다 포인터는 그 값을 바꿀 수 있지만 배열명은 상수이므로 값을 바꿀 수 없다 즉, 포인터 pa에 1을 더하여 다시 pa에 저장할 수는 있으나, 배열명 ary는 1을 더 하는 것은 가능하고 그 값을 다시 저장하..

    배열과 포인터의 관계 (1)

    배열과 포인터의 관계 (1)

    배열명으로 배열 요소 사용하기 주소는 정수처럼 보이지만 자료형에 대한 정보를 갖고 있는 특별한 값이다 주소 + 정수 --> 주소 + (정수 + 주소를 구한 변수의 크기) ==> 배열명은 첫 번째 배열요소의 주소 값이다 #include int main(void) { int ary[3]; int i; *(ary + 0) = 10; *(ary + 1) = *(ary + 0) + 10; // ary[1] = ary[0] + 10 printf("세 번째 배열 요소에 키보드 입력 : "); scanf_s("%d", ary + 2); // ary[2]가 세 번째 배열 요소이므로 &를 사용하여 입력, ary + 2는 &ary[2]와 같다 for (i = 0; i < 3; i++) { printf("%5d", *(ary..

    전처리 지사자 (2)

    전처리 지사자 (2)

    이미 정의된 매크로 매크로에는 이미 그 정의가 약속되어 있어 사용자가 취소하거나 바꿀 수 없는 매크로명이 있다 #include void func(void); int main(void) { printf("컴파일 날짜와 시간 : %s, %s\n\n", __DATE__, __TIME__); // 컴파일을 시작한 날짜와 시간으로 치환 printf("파일명 : %s\n", __FILE__); // 전체 디렉터리 경로를 포함한 파일명으로 치환 printf("함수명 : %s\n", __FUNCTION__); // 매크로명이 사용된 함수 이름으로 치환 printf("행번호 : %d\n", __LINE__); // 매크로명이 사용된 행 번호로 치환(10행에서 사용했으므로 정수 10이 됨) #line 100 "marco...

    전처리 지시자 (1)

    전처리 지시자 (1)

    전처리 ==> 전처리기가 소스 코드를 컴파일하기 좋게 다듬는 과정이며 소스 코드에서 #으로 시작하는 지시자를 처리한다 다양한 전처리 지시자 파일을 포함하는 #include #include는 지정한 파일의 내용을 읽어와 지시자가 있는 위치에 붙여놓은다 꺽쇠갈호()는 컴파일러의 헤더파일을 포함할 때 사용한다 큰따음표(" ")는 사용자의 헤더파일을 포함할 때 사용한다 student.h (사용자가 만든 헤더파일) typedef struct { int num; char name[20]; } Student; main.c (소스파일) #include // 컴파일러가 만든 헤더파일을 포함한다 #include "student.h" // 사용자가 만든 헤더파일을 포함한다 int main(void) { Student a =..

    포인터 완전 정복을 위한 포인터 이해하기 (2)

    포인터 완전 정복을 위한 포인터 이해하기 (2)

    포인터의 대입 규칙 규칙 1. 포인터는 가리키는 변수의 형태가 같을 때만 대입해야 한다 #include int main(void) { int a = 10; int* p = &a; double* pd; pd = p; printf("%lf\n", *pd); return 0; } ==> 위에 그림처럼 *pd가 사용하는 영역이 변수 a의 영역보다 크기 때문에 알 수 없는 값이 나오는 것이다 (포인터 pd의 반환형이 int형 이라면 컴파일했을 때 10이 나왔을 것이다) 규칙 2. 형 변환을 사용한 포인터의 대입은 언제나 가능하다 double a = 3.4; double *pd = &a; int *pi; pi = (int*)pd; // double형 포인터 pd를 int형으로 바꿔줬다 ==> 위의 그림처럼 *pi가..