포인터와 포인터 변수
메모리는 각 바이트 별로 주소가 붙여진 1차원 배열이며 할당 받은 메모리 공간은 변수 이름으로 접근이 가능하다.
예 1) int a,b,c; → a,b,c를 위한 메모리 할당을 해준다. (int만 쓰는 건 메모리 할당 x)
a=b+c; → 변수 이름 a,b,c로 메모리에 접근해 줌.
포인터란? 포인터는 주소를 다루기 위한 자료형으로 데이터를 가진 메모리 공간을 주소로 접근하기 위해 사용한다.
포인터 변수란? 값으로 메모리 주소를 갖는 변수이며 포인터 변수를 선언할 때는 타입 뒤에 별표를 입력한다. int *p; → int형 포인터 변수 p선언. int *a,b; → int형 포인터 변수 a와 int형 변수 b선언. (두 개의 포인터변수를 동시에 선언하고 싶다면 int *a, *b; 처럼 두 변수 모두 *적어주기)
주소 연산자 &
메모리에 할당된 변수의 주소값을 알려주는 연산자
int main(void){int i=100;pirntf("주소값: %p 값: %d",&i,i)printf("주소값+1: %p",&i+1)//출력결과 1576008244, 100 1576008248 //주소값에 +1을 해주었을 때 1이 커진 45가 아니라 4가 더해진 48인 이유는주소값과의 더하기 연산에서는 자료형 크기만큼 커지기 때문.//%p 대신 %u 넣으니까 타입 오류 남.
*주의 : “상수”나 “수식” 앞과 레지스터 변수에는 주소 연산자를 사용할 수 없음
&3이나 &(i+3)은 잘못 사용한 예)
포인터 변수와 주소 연산자
//사용 예제int i, *p; ->ram에 할당register int v; ->cpu에 할당p=0; //p=NULL; p=0이라고 하든 p=NULL이라고 하든 0000000000000000이 출력. 포인터가 아무것도 가리키지 않는다는 것.p=&i; //포인터 p가 int형 변수 i를 가리킨다. 올바른 식.p=3000; //오류; 포인터 변수가 상수를 가리키려고 한다. p는 int*형식이고 3000은 상수라서 형이 맞지 않는다p=(int*)1776; //int형 상수를 (int*)로 캐스트. 오류는 나지 않지만 안 쓰는 게 좋음 &d라고 하면 1776이출력되고 &p라고 하면 00000000000006F0가 출력.//이 주소가 실제로 유효한 메모리 주소인지 무효한 주소인지 알 수 없기 때문에 1776이라는 주소를 계속 출력하지만//이 주소는 실제로 존재하지 않는 메모리에 대한 참조로 인해 예상못한 동작 유발.p=&(i+99); //오류; 수식 앞에 주소연산자 사용p=&v; //오류; register변수에 사용
역참조 연산자 *
포인터가 가리키는 메모리 공간에 접근하기 위한 연산자로 단항 연산자이며 일반 변수와 같이 배정 연산자 (=) 좌측에 올 수 있다.
단항연산자 - 우에서 좌로의 결합순위. 오른쪽에 있는 변수나 포인터가 먼저 평가되고 그 다음에 *연산자를 적용하는 것
사용 예
int i=100, *p;
p=&i; //변수 i의 메모리 주소를 p에 할당
printf(”%d”, *p); //100 출력.
int i, j=5,*p;p=&i;i=10;printf("%d %d %d",i,j,*p); *p=*p*j; -----------------> //우에서 좌로의 결합 순위 (단항연산자)printf("%d %d %d",i,j,*p);p=&j;printf("%d %d %d",i,j,*p)//출력결과10 5 1050 5 5050 5 5
포인터 변수의 크기
시스템에 따라 상이하기 때문에 sizeof 연산자로 계산하는 것이 좋다.
포인터 변수의 크기는 포인터 변수가 가리키는 형과 관계없이 크기가 동일하다.
int i → 4바이트 double d → 4바이트 int *ip → 8바이트 double *dp → 8바이트
sizeof() 사용해서 바이트 크기 확인해보기.
printf(”%d”,sizeof(ip)); 하면 8 출력
void 포인터 변수
형이 없는 포인터로 void형 포인터 변수를 사용할 때에는 적절한 형 변환이 필요하다.
int *i, float *f, void *v 라는 포인터가 있다고 하자.
v=&i 처럼 void형 포인터 변수인 v가 int형 포인터 변수인 i를 가리키는 건 가능하지만 i=&f 처럼 int형 포인터 변수가 float형 포인터 변수인 f를 가리키는 건 불가능하다.
이렇게 void포인터 변수가 어떤 변수를 가리킬 땐 오류가 나지 않지만 이 변수를 사용할 때는 형 변환을 적절하게 해주어야 한다. void는 주소는 다 받아올 수 있는 반면 읽을때는 1바이트를 읽어야할지 4바이트를 읽어야 할 지 그 크기를 모르기 때문에 무조건 타입을 앞에 나타내야 함.
int i,j=20; void *v; 식이 있다고 하자. v=&i; 처럼 v가 i를 가리키고 i의 값을 v를 이용해서 바꾸고 싶을 때는 i=*((int*)v)+10; 처럼 해주어야 한다. v가 가리키는 곳의 데이터 형을 int형으로 다룬 것이다
포인터의 포인터 변수
포인터를 가리키는 포인터 변수이다.
포인터도 어렵고 헷갈리지만 포인터의 포인터 변수가 더더 헷갈림
포인터 연산
p와 q가 포인터 변수라면 p+i, p-i는 p가 가리키고 있는 곳으로부터 i번째 앞 또는 뒤 원소이고 p-q는 p와 q 사이의 원소 개수이다.(배열 내 원소의 개수, 자료형의 크기에 따른 거리 반환) p와 q는 주소를 가지고 있고 포인터끼리 뺄셈 덧셈이 가능하다. 주소 차를 나타내는 것이 아닌, 원소의 개수를 나타낸다.
//예제#include <stdio.h>int main(void){double x[10], *p, *q;p=&x[2];q=p+5;printf("q-p=%d/n", q-p);printf("(int)q-(int)p=%d\n",(int)q-(int)p);return 0;}//출력결과q - p = 5(int) q - (int) p = 40
포인터와 함수
- 값에 의한 호출 - 변수가 함수의 인자로 전달될 때, 변수의 복사본이 전달 → 호출한 환경의 변수 자체는 변경하지 않음
void swap(int, int);int main(void){int i=3, i=5;swap(i,j);printf("%d %d \n",i,j)return 0;}void swap(int a, int b){int tmp;tmp=a;a=b;b=tmp;}//이 코드의 의도는 swap 함수를 이용하여 main함수 안의 i,j의 값을 바꿔주려는 것이지만, 변수의 복사본만 전달된 것이기 때문에 swap에서만 a,b값이 변경되고 main에서는 값이 swap되지 않는다.
- 주소에 의한 호출 - 원본에 접근
1. 함수 매개변수를 포인터 형으로 선언2. 함수 몸체에서 역참조 연산자 사용3. 함수를 호출할 때 주소를 인자로 전달void swap(int*, int*);int main(void){int i=3, i=5;swap(&i,&j);printf("%d %d \n",i,j)return 0;}void swap(int *a, int *b){int tmp;tmp=*a;*a=*b;*b=tmp;}//출력 결과는 swap된 5,3이다.
//예제#include<stdio.h>int divide(int, int, int*, int*);int main(void) {int i, j, q, r;printf("피제수를 입력하세요 : ");scanf("%d", &i);printf("제수를 입력하세요 : ");scanf("%d", &j);if (divide(i, j, &q, &r))printf("0으로 나눌 수 없습니다\n");elseprintf("%d/%d: 몫은 %d이고 나머지는 %d입니다.\n", i, j, q, r);return 0;}int divide(int i, int j, int *q, int *r) {if (j == 0)return -1;*q = i / j;*r = i % j;return 0;}//if()에서 괄호 안에 0이 아닌 모든 수가 있으면 if문 실행. 제수가 0일때는 -1을반환하고 divide함수가 끝나버려서 *q와 *r에 값이 안 들어감.
포인터 배열 - 1차원 배열
배열 이름은 포인터이고, 그 값은 배열의 첫 번째 원소의 주소값이다. 포인터에 배열의 원소를 지정하는 첨자([])가 사용 가능하며 배열 이름은 고정된 주소를 갖는 상수 포인터이다.
*주의 ! 배열 이름이 상수 포인터이기 때문에 아래 내용은 오류가 난다.
int array[3]={1,2,3};
array=array+1; ———>오류
array++; ———>오류
//3=3+1; 3++; 라는 것과 같은 말인 거임. (3은 3이지 4가 아니다)
#define N 100int main(void){int a[N], i, *p; //a는 int형 포인터로 a[0]의 주소를 가지고 있다p=a; //p=&a[0];과 같은 말이다.p=a+i; //a+i는 a[0]에서 i번째 떨어진 원소 위치(주소)라는 것*(a+i)=10;p[i]=10;*(p+i)=10; //p[i]==*(p+i)a=&i; //오류; a는 상수 포인터로 고정된 주소를 가지고 있기 때문에 다른 주소를 넣을 수 없음}
포인터 배열 - 2차원 배열, 3차원 배열
-1차원 배열이 인덱스 하나를 제거하면 포인터가 되듯이 2차원 배열도 인덱스 하나를 제거하면 포인터가 된다.
-2차원 배열에서 인덱스를 두 개 제거하면 배열 포인터가 된다.
1)인덱스 하나 제거→ 포인터가 됨
b[0]은 1번째 줄 1차원 배열명이다. (배열명이 포인터, 즉 주소를 값으로 가짐)
2)인덱스 두 개 제거→ 배열 포인터가 됨.
배열 포인터가 된다는 게 인덱스 하나를 제거했을 때와 달리 배열을 껑충껑충 뛴다는 이야기다. 이차원 배열 그림상으로는 가로 아래 방향으로 이동.
이차원 배열 원소인 b[i][j]의 다양한 표기 방법
#define N 100
int b[N][N];
- b[i][j]
- *(b[i]+j)
- *(*(b+i)+j)
- (*(b+i))[j]
이거 이차원 배열 표기방법이 맞나 헷갈리면 첨자가 두 개인지, 별표가 두 개인지, 첨자+별표가 두 개인지 확인해보면 됨
배열과 함수
배열을 매개변수로 갖는 함수이다. 배열 매개변수는 포인터이다. 배열을 매개변수로 갖는 함수를 호출할 때 다음 인자는 주소값이면 된다.
int grade_sum2(int gr[], int size){ int sum, i;for(sum=0, i=0; i<size; i++)sum+=gr[i];return sum;}//-gr : 포인터//int gr[] 말고 int*gr해주고 gr[i]부분을 *(gr+i)라고 바꿔줘도 됨#define N 100int i, a[N], sum;...sum=grade_sum2(a,N); //sum= a[0]+a[1]+..+a[99]sum=grade_sum2(&a[5],10); //sum= a[5]+a[6]+..+a[14]sum=grade_sum2(&i,1); //sum= i;//여기서 i는 배열이 아닌데 들어갈 수 있나? 싶지만 사실 int gr[]이나 int*gr은정수의 주소를 저장하는 변수라 상관이 없다.
문자열과 포인터
문자 배열과 문자열 포인터 차이
동적 메모리 할당 - calloc(), malloc()
효율적인 메모리 사용이 가능하다. 동적 메모리는 heap영역에 할당된다. 일반적인 매개변수나 지역변수들이 stack영역에 할당되는 것과 다르다. 메모리 할당 변수에는 malloc()와 calloc()가 있다.
#include <stdlib.h> 꼭 넣어주어야 함
// 함수 원형
void calloc(size_t N, size_t el_size); // 0으로 초기화
void malloc(size_t N_bytes); // 초기화 안함
두 함수 모두 할당된 메모리 주소를 리턴하며 배열, 구조체, 공용체를 위한 공간을 동적으로 생성한다. 사용이 끝나면 free() 사용해서 메모리 해제해주어야 함.
int *p; double *q;p=(int*)calloc(10,sizeof(int));q=(double*)calloc(10,sizeof(double));free(p);free(q);//함수 원형을 보면 void형이기 때문에 이걸 쓰려면 (int*)처럼 타입을 지정해야
- malloc (사실 calloc도 malloc와 괄호 안 내용만 조금 다른 거지 나머지는 똑같)
//malloc 예제 1#include<stdlib.h>#include<stdio.h>int main() {int* p = (int*)malloc(sizeof(int) * 4);for (int i = 0; i < 4; i++) {p[i] = i;}for (int i = 0; i < 4; i++) {printf("%d\n", p[i]);}free(p);}//malloc 예제 2int main() {char* pc = NULL;int i = 0;pc = (char*)malloc(sizeof(char) * 100);for (i = 0; i < 26; i++) {(*(pc + i) )= 'a' + i;}*(pc + i) = 0;printf("%s", pc);free(pc);return 0;}// abcdefghijklmnopqrstuvwxyz출력//malloc 예제 3#include<stdlib.h>#include<stdio.h>int main() {int* arr = (int*)malloc(3 * sizeof(int));int i;for ( i = 0; i < 3; i++) {arr[i] = i;printf("%d\n", arr[i]);}int* parr = (int*)malloc(10 * sizeof(int));for (i = 0; i < 3; i++) {parr[i] = arr[i];}arr = parr;// 동적할당으로 만든 배열 arr에 동적할당으로 만든 배열 parr을 넣을 수 있음//만약 동적할당으로 만든 배열이 아니라면 arr=parr에서 오류가 난다. arr은 수정할 수 있는 식이 아니기 때문이다.//배열의 이름은 배열의 시작주소를 저장하는 포인터변수지만 그 값을 변경할 수는 없다.for (i = 0; i < 10; i++) {printf("%d", parr[i]);}}
- 2차원 배열 동적 할당 - 첫 번째 방법
int main() {int** arr; int input;int subject, student, sum=0;printf("과목 수 입력 : ");scanf_s("%d", &subject);printf("학생 수 입력: ");scanf_s("%d", &student);arr = (int**)malloc(sizeof(int*) * subject);for (int i = 0; i < subject; i++) {arr[i] = (int*)malloc(sizeof(int) * student);}for (int i = 0; i < subject; i++) {printf("과목 %d 점수 : \n", i + 1);for (int j = 0; j < student; j++) {printf("학생 %d의 점수 : ", j + 1);scanf_s("%d", &input);arr[i][j] = input;}}for(int i=0; i<subject; i++){sum = 0;for (int j = 0; j < student; j++) {sum += arr[i][j];}printf("과목 %d의 평균 점수 : %d \n", i, sum / student);}for (int i = 0; i < subject; i++) {free(arr[i]);}free(arr);}//처음에 sizeof(int)*subject가 아니라 sizeof(int)*subject*student해줘야 하는 줄 알았다.//2차원 배열에 대해서 한 번 malloc로 동적생성 해주고 생성된 배열에 대해서 malloc으로 배열을 한번 더 생성해줘야 한다.//만약에subject가 3이라면 int형 배열 *subject[0], *subject[1], *subject[2]이렇게 3개 생성된다//이 각각의 배열에 대해서 또 malloc를 지정해주면 동적할당이 되는 것이다.//arr[0]=(int*)malloc(sizeof(int*)*student);//arr[1]=(int*)malloc(sizeof(int*)*student);//arr[2]=(int*)malloc(sizeof(int*)*student); 이렇게 해주면 됨
- 2차원 배열 동적 할당 - 두 번째 방법
동적할당 전에 이차원배열보기int arr[3][2]={{10,11},{20,21},{30,31}}; int(*p)[2];//포인터p=arr;printf(“%p”,*p); //원래 1차원 배열에서는 p만 해도 주소 출력되지만 이차원 배열이라 *p라고 해줘야함 값을 출력하려면 **p 하거나 *(*p+1) 하면 각각 10과 11이 출력이 된다.*((*p)+1)해도 11출력*(*(p + 1) + 1)하면 21출력됨 *(*(p+1));하면 20출력됨printf(“%p”,arr); -> 같은 주소 두 개 출력됨근데 int (*p)[2]; 라고 해 준 이유는? 자료형(*포인터이름)[가로크기]로 선언해줘야 2차원배열을 포인터 변수가 받을 수 있다. 세로의 크기가 아니라 가로의 크기!!#define N 5int main(){int i;int(*p)[N], * q;p = (int(*)[N])malloc(N * N* sizeof(int));//p=2차원 배열을 포인트 하기 위해 //calloc라고 바꾸고 N*N, sizeof(int)라고 해줘도 됨q = (int*)p;//p가 가리키는 메모리를 int포인터로 형변환 함 q는 p가 가리키는 이차원배열을 1차원배열처럼 사용가능for (i = 0; i < N * N; i++)q[i] = i;for (i = 0; i < N; i++) {for (int j = 0; j < N; j++)printf("%3d", p[i][j]);putchar('\n');}free(p);p = NULL;q = NULL;}//int(*p)[N]라고 해준 이유는? 자료형(*포인터이름)[가로크기]로 선언해줘야 포인터 변수가 받을 수 있다.int(*p)[N]는 배열 포인터이다. 열의 크기가 2인 배열포인터로 *p를 괄호로 꼭 묶어주어야한다, (int *p[2]는 포인터배열이 됨)배열포인터는 []에 가로크기를 넣어준다면 포인터배열은 세로크기를 넣어줘야 함
포인터 배열
포인터 배열을 보기 전에 2차원 배열의 메모리 공간 낭비를 보자.
포인터 배열은 포인터를 배열 원소로 갖는 배열이다.
int main() {const char* words[3] = { "cat","dog","elephant" };printf("%s %s", words[0], words[1]);printf("%c %c", *(words[0]), *(words[1] + 1));printf("%s", *(words + 1) + 1);}
#include<stdlib.h>#include<stdio.h>#include<string.h>#pragma warning(disable:4996)#define N 50#define L 10int input_words(char* []);void sort_words(int, char* []);void print_words(int, char* []);int compare(const void* p, const void* q) {return strcmp(*(const char**)p, *(const char**)q);}//strcmp : 매개변수로 들어온 두 개의 문자열을 비교하여 문자열이 완전히 같다면 0을 반환//str1<str2인 경우 음수, str1>str2인 경우 양수, str1==str2인 경우 0 반환int main() {char* words_p[N];int num_word = 0;num_word = input_words(words_p);sort_words(num_word, words_p);print_words(num_word, words_p);for (int i = 0; i < num_word; i++) {free(words_p[i]);}return 0;}]int input_words(char* words[]) {int num = 0;char temp[100];while (1) {printf("단어를 입력하세요 : ");scanf_s("%[^\n]", temp,100); //%[^\n]은 개행 문자를 입력할때까지 모든 문자열을 받겠다는 것. 공백이 필요한 문자열을 입력받을 때 유용.getchar(); //getchar()는 한 번에 하나의 문자만 입력받음. 버퍼에 남은 \n을 받아 없애줌.if (strcmp(temp, "end") == 0)break;if (strlen(temp) < 10) {words[num] = (char*)calloc(strlen(temp) + 1, sizeof(char));strcpy(words[num], temp); //strcpy: str1의 길이가 str2의 길이보다 길거나 같아야 복사됨.}else continue;if (++num == N)break;}return num;}void sort_words(int num, char* words[]) {qsort(words, num, sizeof(char*), compare);}void print_words(int num, char* words[]) {for (int i = 0; i < num; i++) {printf("%s\n", words[i]);}
메인함수의 인자
main()함수는 프로그램 실행 시 명령어 라인으로부터 인자를 전달받을 수 있다.
int main(int argc, char*argv[])
-argc: 명령어 라인에서 전달된 인자 개수
-argv: 명령어 라인에서 입력된 문자열들에 대한 포인터
argc의 값은 옵션이 입력되지 않아도 기본적으로 1이며 argv[0], argv의 첫번째 요소는 항상 프로그램 자신의 파일명이다. 인덱스가 0인 문자열은 프로그램의 실행경로로 항상 고정되어있다. 따라서 입력한 옵션의 정확한 개수를 구하려면 argc에서 -1을 해주어야 한다. argv는 문자상태이기 때문에 만약에 숫자 입력을 받으려면 atoi
를 사용해서 문자열을 상수로 바꿔준다. ex)atoi(argv[2]);
#include<stdlib.h>#include<stdio.h>int main(int argc, char*argv[]) {int i;printf("총 인자 개수 : %d\n", argc);for (i = 0; i < argc; ++i)printf("%d 번째 인자 : %s\n", i, argv[i]);return 0;}
형 한정자-const, restrict
형 한정자는 변수의 사용 제한을 설정하는 것으로 기억영역 클래스 뒤와 형 앞에 지정한다.
const
const 변수는 초기화될 수는 있지만, 그 후에 배정되거나, 증가, 감소,또는 수정될 수 없다.
const float pi=3.14; //pi에는 다른 값을 배정할 수 없다. pi=3.15하면 오류가 난다.
const변수를 포인트 할 때 주의해야 한다. const int a=7; int *p=&a라고 하면 안 됨(경고). (a=8하면 무조건 오류) p는 int를 포인트 하는 보통의 포인터이기 때문에 나중에 ++p와 같은 수식을 사용하여 a에 저장되어 있는 값을 변경할 수 있기 때문이다.
- const변수를 포인트 해야 할 경우에는 const int a=7; const int p=&a;라고 해주면 된다. 여기서 p자체는 상수가 아니다. p에 다른 주소를 배정할 수 있지만, *p에 값을 배정할 수는 없다.
- 상수포인터-int a; int *const p=&a;는 이전과 다르다. p는 int에 대한 상수 포인터이다. p에 값을 배정할 수는 없지만 *p에는 가능하다. 즉, ++*p ,*p=10등과 같은 수식은 가능하다.
- const변수에 대한 상수 포인터 선언- const int a=7; const int *const p=&a; p는 const int를 포인트 하는 상수포인터이다. p와 *p값은 변경이 안 된다.
restrict(c99에서 추가)
이 포인터 변수는 다른 포인터 변수랑 같은 곳을 가리키지 않는다고 알려주는 것과 같다.
a = b + c; i = j - a; k = l / m; - 컴파일 시 세 번째 문장이 처음 두 문장보다 먼저 실행되거 나 동시에 실행되도록 실행코드를 생성할 수 있음
- a = *b + *c; *i = *j - *a; *k = *l / *m; // a와 k가 가리키는 곳이 같다면? - 포인터들이 어떤 메모리 공간을 가리키는지 알 수 없으므로 실행코드가 최적화 될 수 없음 ->restrict를 붙이면 최적화 함
포인터 변수에 적용되며, 현재 포인트 되는 객체는 다른 포인터에 의해서는 포인트 안 됨을 명시하기 위해 사용된다. 컴파일러가 코드 최적화를 수행한다. 포인터 변수명 앞에 restrict키워드를 붙여서 사용한다. restrict한정자는 컴파일러에게 포인터 간에 메모리 참조가 겹치지 않음을 보장하는 것을 요청한다. a=b=c; i=j-a; k=l/m; 에서는 컴파일 시에 세번째 문장이 처음 두 문장보다 먼저 실행되거나 동시에 실행되도록 실행코드를 생성할 수 있다. *a=*b=*c; *i=*j-*a; *k=*l/*m;에서 만약에 a와 k가 가리키는 곳이 같다면? 포인터들이 어떤 메모리 공간을 가리키는지 알 수 없으므로 실행코드가 최적화 될 수 없다.
함수 포인터
하나의 함수를 여러 목적으로 유연하게 사용하고자 할 때 유용하다. 유지보수가 용이하고 함수명도 배열명과 같이 함수명 자체가 함수 포인터이다. 변수를 선언하면 메모리 공간이 할당되고 그 공간의 위치가 주소로 존재하듯이 함수를 선언해도 변수와 마찬가지로 메모리에 공간이 할당되며 그 위치를 표현하는 주소가 생겨난다. 변수를 가리키는 포인터처럼 함수 포인터는 메모리 상에 올라간 함수의 시작주소를 가리킨다. 만약 hello()라는 함수가 있을 때 printf(”%p”,hello)라고 하면 주소값이 출력될 것이다.
💡 선언 방법 - 형(*변수명)(매개변수_목록)형: 함수 포인터 변수가 가리키는 함수의 리턴형변수명: 함수 포인터 명매개변수_목록: 함수 포인터 변수가 가리키는 함수의 매개변수 목록
*주의! int(*fp)(int,int);는 올바른 선언이지만 int*fp(int,int)는 틀린 선언이다
- 반환 값과 매개변수가 없는 경우
void print_hell(){printf(“hello”);}int main(){void (*fp)();fp=print_hello; //print_hello메모리 주소를 함수 포인터 fp에 저장, 왜 계속 fp=print_hello()라고 실수하지;fp(); //함수 포인터로 print_hello함수 호출return 0;
- 반환 값과 매개변수가 있는 경우
int add(int a, int b){ return a+b}’int main(){int (*fp)(int, int); //->함수포인터 fp의 정의이다. 이 함수포인터 fp는 함수의 리턴값이 int형이고 인자 두 개가 각각 int인 함수를 가리키는구나라고 알 수 있다.fp=add;printf(“%d”,fp(10,20));//add함수를 호출}
예제#include<stdlib.h>#include<stdio.h>#include<string.h>int sum(int, int);int mul(int, int);int main() {int a,b;int(*pfunc)(int, int);pfunc = sum;scanf("%d %d", &a, &b);printf("sum(a,b): %d\n", sum(a, b));printf("pfunc(a,b): %d\n", pfunc(a, b));pfunc = mul;printf("mul(a,b): %d\n", mul(1, 2));printf("pfunc(a,b): %d\n", pfunc(1, 2));return 0;}int sum(int a, int b) {return a + b;}int mul(int a, int b) {return a * b;}리턴형이 int이고 두 개의 인자 각각의 포인터 형이 int인 함수를 가리킨다. sum과 mul함수 모두가 이 조건을 만족한다.즉 pfunc가 이 두 함수를 모두 가리킬 수 있다는 것이다.
#include<stdio.h>#include<stdlib.h>int sum(int a, int b) {return a + b;}int mul(int a, int b) {return a * b;}void func(int(*fp)(int, int)) {int a, b;printf("두 정수값을 입력하세요: ");scanf_s("%d %d", &a, &b);int res = fp(a, b);printf("결과: %d \n", res);}int main() {int num;printf("1.두 정수의 합 2.두 정수의 곱\n");scanf_s("%d", &num);switch (num) {case 1: func(sum); break;case 2: func(mul); break;}}
💡 함수포인터를 만들 때 인자의 형이 무엇인지 알기 힘든 경우가 종종 있다. 예를 들어 int increase(int(*arr)[3],int row)와 같은 경우엔 첫번째 인자형을 알아보기 힘들다. int (pfunc)(int(*)[3],int);라고 해주면 되는데 특정한 타입의 인자를 판별하는 일은 단순히 변수의 이름만을 빼버리면 된다. 따라서 첫번째 인자형은 int(*)[3]이다.
qsort()
💡 형식 : qsort(정렬할 값의 주소, 요소의 개수, 요소의 크기, 기준 함수);예시 : qsort(N, 10, sizeof(int), compare);정렬할 값의 주소 : 배열 N이 있다고 하면 N을 적어주면 해당 배열에 대해 정렬을 시작한다. &N[2]라고 하면 0-1번째 요소는 무시하고 2번째 요소부터 정렬한다.요소의 개수 : 정렬할 요소가 몇 개인지 정수로 적어주기요소의 크기 : 요소 한 개당 몇 바이트인지 적어주면 된다. sizeof연산자 이용하면 편하다기준 함수 : 정렬을 어떤식으로 할 건지 기준 함수를 만들어 그 함수 이름을 이곳에 적어준다
stdlib.h헤더파일에 존재한다. qsort()는 다양한 형의 배열을 퀵 정렬로 정렬할 수 있게 한다. el_size의 크기의 원소가 n_els개 있는 array배열을 정렬한다. 마지막 매개변수인 compare는 함수 포인터이다. compare는 const void * 형 매개변수를 두 개 갖고 int형을 리턴 하는 함수를 포인터 할 수 있다.
int형을 위한 compare 함수 예#include<stdio.h>#include<stdlib.h>int compare(const void* p, const void* q) {if (*(int*)p > *(int*)q) //void*형 포인터이 p와q를 통해 값을 비교할 때 먼저 (int*)로 캐스팅 해야 return 1; ----------->자리 바꿈else if (*(int*)p < *(int*)q)return -1; ----------->자리 안 바꿈return 0;}int main() {int a[] = { 3,2,1 };qsort(a, 3, sizeof(int), compare);for (int i = 0; i < 3; i++) {printf("%d", a[i]);}}
#include<stdio.h>#include<stdlib.h>#include<string.h>int compare(const void* p, const void* q) {return strcmp(*(char**)p, *(char**)q);}int main() {const char* word[3] = { "dog","cat","elephant" };qsort(word, 3, sizeof(char*), compare);for (int i = 0; i < 3; i++) {printf("%s\n", word[i]);}}//출력결과catdogelephant매개변수로 들어온 두 개의 문자열을 비교하여 문자열이 완전히 같다면 0을 반환하고다르면 음수 혹은 양수를 반환하는 함수이다.여기서 -1, 1은 매개변수로 들어온 문자열들을 비교하다가 다른 문자가 나왔을때그문자의 아스키코드 값에 의해서 정해진다(1) str1 < str2 인 경우에는 음수 반환(2) str1 > str2 인 경우에는 양수 반환(3) str1==str2인 경우에는 0을 반환
예제1 -문자열 받아서 sorting후 출력하기#include<stdio.h>#include<stdlib.h>#include<string.h>#define N 50#define L 10int input_words(char* []);void sort_words(int, char* []);void print_words(int, char* []);int compare(const void* p, const void* q) {return strcmp(*(const char**)p, *(const char**)q);}int main(void) {char* word[N];int num = 0;num = input_words(word);sort_words(num, word);print_words(num, word);for (int i = 0; i < num; i++) {free(word[i]);}return 0;}int input_words(char* a[]) {int num = 0;char temp[100];while (1) {printf("단어 입력\n");scanf("%[^\n]", temp);getchar();if(strcmp(temp,"end")==0)break;else if (strlen(temp) < 10) {a[num] = (char*)malloc(sizeof(char) * strlen(temp) + 1);strcpy(a[num],temp);}elsecontinue;if (++num == N)break;}return num;}void sort_words(int num, char* a[]) {qsort(a,num,sizeof(char*),compare);}void print_words(int num, char* a[]) {for (int i = 0; i < num; i++) {printf("%s", a[i]);}}