포인터와 주소 계산 연습 문서의 문제 4 풀이 코드다.
/* 포인터와 주소 계산 연습: 문제 p1~p4의 답을 실제로 돌려 확인한다.
*
* 문서: 4.연습문제/05.포인터와 주소 계산 연습
* 실행: cc -std=c11 -Wall -Wextra 05_pointer-address-practice_p4.c -o ptr_practice && ./ptr_practice
*/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
static int fails = 0;
#define CHECK(cond, msg) \
do { \
if (!(cond)) { \
printf("[FAIL] %s (%d번째 줄)\n", (msg), __LINE__); \
fails++; \
} \
} while (0)
/* p3 */
static void f(int x, int *y)
{
x = x + 1;
*y = *y + x;
}
/* p3 변형 */
static void g(int *x, int *y)
{
*x = *x + 1;
*y = *y + *x;
}
/* p4: 포인터로 쓴 길이 세기 */
static int length(const char *s)
{
const char *p = s;
while (*p != '\0')
p++;
return (int)(p - s);
}
/* p2 변형: 원소 하나가 12바이트인 구조체 */
struct triple {
int x, y, z;
};
/* p4 변형: 끝 표시를 베끼지 않는 잘못된 복사 */
static void copy_without_end(char *s, const char *t)
{
while (*t != '\0') {
*s = *t;
s++;
t++;
}
}
/* 색인으로 쓴 길이 세기 */
static int length_indexed(const char *s)
{
int i = 0;
while (s[i] != '\0')
i++;
return i;
}
/* 문서 p4의 포인터 판 비교 */
static int compare(const char *s, const char *t)
{
for (; *s == *t; s++, t++)
if (*s == '\0')
return 0;
return *s - *t;
}
/* 문서 p4의 포인터 판 복사에 세는 줄을 넣은 것 */
static void copy_counted(char *s, const char *t, int *assigns, int *moves)
{
*assigns = 0;
*moves = 0;
for (;;) {
*s = *t;
(*assigns)++;
if (*s == '\0')
break;
s++;
t++;
(*moves)++;
}
}
int main(void)
{
/* p1: 포인터 추적 */
{
int a = 10, b = 20;
int *p = &a;
int **q = &p;
char out[32];
*p = *p + b; /* 1줄: a = 30 */
CHECK(a == 30 && b == 20 && p == &a, "p1 1줄 뒤: a = 30");
p = &b; /* 2줄: p가 b를 가리킨다 */
CHECK(a == 30 && b == 20 && p == &b && *q == &b, "p1 2줄 뒤: p -> b");
**q = **q + 1; /* 3줄: b = 21 */
CHECK(a == 30 && b == 21, "p1 3줄 뒤: b = 21");
*q = &a; /* 4줄: p가 다시 a를 가리킨다 */
CHECK(p == &a, "p1 4줄 뒤: p -> a");
*p = *p - b; /* 5줄: a = 30 - 21 = 9 */
CHECK(a == 9 && b == 21, "p1 5줄 뒤: a = 9");
snprintf(out, sizeof out, "%d %d %d", a, b, **q);
CHECK(strcmp(out, "9 21 9") == 0, "p1 출력: 9 21 9");
printf("[OK] p1: 출력 %s\n", out);
}
/* p1 변형: 4줄을 빼면 */
{
int a = 10, b = 20;
int *p = &a;
int **q = &p;
*p = *p + b;
p = &b;
**q = **q + 1;
*p = *p - b; /* p가 b를 가리키므로 b = 21 - 21 = 0 */
CHECK(a == 30 && b == 0 && **q == 0, "p1 변형 출력: 30 0 0");
printf("[OK] p1 변형: 출력 %d %d %d\n", a, b, **q);
}
/* p2: 주소 계산 */
{
char c[10];
short s[10];
int arr[10];
double d[10];
int *p = arr + 2;
CHECK(sizeof(char) == 1 && sizeof(short) == 2 && sizeof(int) == 4 && sizeof(double) == 8, "칸 크기 1, 2, 4, 8");
CHECK(100 + 3 * sizeof(int) == 112, "(가) int: 100 + 3 x 4 = 112");
CHECK(400 + 2 * sizeof(double) == 416, "(나) double: 400 + 2 x 8 = 416");
CHECK(2000 + 7 * sizeof(short) == 2014, "(다) short: 2000 + 7 x 2 = 2014");
CHECK(50 + 9 * sizeof(char) == 59, "(라) char: 50 + 9 x 1 = 59");
/* 실제 배열에서 같은 식 */
CHECK((uintptr_t)&arr[3] - (uintptr_t)arr == 12 && (uintptr_t)&d[2] - (uintptr_t)d == 16, "실제 주소 차 12, 16");
CHECK((uintptr_t)&s[7] - (uintptr_t)s == 14 && (uintptr_t)&c[9] - (uintptr_t)c == 9, "실제 주소 차 14, 9");
CHECK(&arr[4] - &arr[1] == 3, "(마) 포인터끼리 빼면 칸 수 3");
CHECK((uintptr_t)&arr[4] - (uintptr_t)&arr[1] == 12, "(마) 바이트로는 12");
CHECK(&p[1] == &arr[3] && p - arr == 2, "(바) p = arr + 2 이면 p[1]은 arr[3]");
CHECK(&p[-2] == &arr[0], "p[-2]는 arr[0]");
{
struct triple t[10];
CHECK(sizeof(struct triple) == 12 && 100 + 3 * sizeof(struct triple) == 136, "변형: 12바이트 칸이면 136");
CHECK((uintptr_t)&t[3] - (uintptr_t)t == 36, "변형: 실제 주소 차 36");
}
printf("[OK] p2: 112, 416, 2014, 59, 칸 수 3(12바이트), arr[3]. 변형 136\n");
}
/* p3: 값으로 받는 인자와 주소로 받는 인자 */
{
int a = 1, b = 2;
f(a, &b); /* x = 2, b = 2 + 2 = 4. a는 그대로 1 */
CHECK(a == 1 && b == 4, "p3 첫 호출 뒤: a = 1, b = 4");
f(b, &a); /* x = 5, a = 1 + 5 = 6. b는 그대로 4 */
CHECK(a == 6 && b == 4, "p3 출력: 6 4");
printf("[OK] p3: 출력 %d %d\n", a, b);
a = 1; b = 2;
g(&a, &b); /* a = 2, b = 2 + 2 = 4 */
g(&b, &a); /* b = 5, a = 2 + 5 = 7 */
CHECK(a == 7 && b == 5, "p3 변형 출력: 7 5");
printf("[OK] p3 변형: 출력 %d %d\n", a, b);
}
/* p4: 문자열 함수 */
{
static const char *words[] = {"", "a", "tree", "now is the time", "data structure"};
char buf[16];
int assigns, moves;
size_t i;
for (i = 0; i < sizeof words / sizeof words[0]; i++) {
CHECK(length(words[i]) == (int)strlen(words[i]), "length == strlen");
CHECK(length_indexed(words[i]) == length(words[i]), "색인 판과 포인터 판의 길이가 같다");
}
CHECK(length("tree") == 4 && length("") == 0 && length("now is the time") == 15, "길이 4, 0, 15");
CHECK(compare("data", "date") == 'a' - 'e' && compare("data", "date") == -4, "(나) data, date: -4");
CHECK(compare("list", "lis") == 't' && compare("list", "lis") == 116, "(나) list, lis: 116");
CHECK(compare("heap", "heap") == 0, "(나) heap, heap: 0");
CHECK((compare("data", "date") < 0) == (strcmp("data", "date") < 0), "부호가 strcmp와 같다");
CHECK((compare("list", "lis") > 0) == (strcmp("list", "lis") > 0), "부호가 strcmp와 같다");
copy_counted(buf, "tree", &assigns, &moves);
CHECK(strcmp(buf, "tree") == 0 && assigns == 5 && moves == 4, "(다) tree: 대입 5번, 옮기기 4번");
copy_counted(buf, "", &assigns, &moves);
CHECK(buf[0] == '\0' && assigns == 1 && moves == 0, "빈 문자열: 대입 1번, 옮기기 0번");
memset(buf, '#', sizeof buf);
copy_without_end(buf, "tree");
CHECK(memcmp(buf, "tree#", 5) == 0, "변형: 끝 표시를 베끼지 않아 다섯째 칸이 그대로 '#'이다");
buf[4] = '\0'; /* 반복 뒤에 한 줄을 더 써야 문자열이 된다 */
CHECK(strcmp(buf, "tree") == 0, "변형: 끝 표시를 붙이면 tree");
printf("[OK] p4: 길이 4, 비교 -4 · 116 · 0, 복사는 대입 5번과 옮기기 4번. 변형은 끝 표시가 빠진다\n");
}
if (fails) {
printf("%d개 확인 실패\n", fails);
return 1;
}
printf("ALL CHECKS PASSED\n");
return 0;
}