# C 語言練習程式(4) -- 指標相關程式集錦(3)

Posted by nathan2009729 on 2022-07-23

## 簡單概論

#include <stdio.h>

void doesnt_mutate(int i)
{
i += 42;
}

void mutates(int *i)
{
*i += 42;
}

void foo(int *ip)
{
ip++;
}

void bar(int **ip)
{
(*ip)++;
}

int main(void)
{
int i = 0;
doesnt_mutate(i);
printf("i is %d\n", i);
mutates(&i);
printf("i is %d\n", i);

int *ip = 0;
foo(ip);
printf("%p\n", ip);
bar(&ip);
printf("%p\n", ip);

return 0;
}


void bar(int **ip)。想修改指標，就需要二級指標(pointer of pointer)。
To change an object, you need a reference to it.

#include <stdio.h>

typedef struct point {
double x, y;
} point;

point move_point_horisontally(point p, double amount)
{
p.x += amount;
return p;
}

point move_point_vertically(point p, double amount)
{
p.y += amount;
return p;
}

point move_point(point p/*1 copy*/, double delta_x, double delta_y)
{
p = move_point_horisontally(p, delta_x);/*2 copies*/
p = move_point_vertically(p, delta_y);/*2 copies*/
return p;/*1 copy*/
}

void print_point(point p)
{
printf("point <%.2f, %.2f>\n", p.x, p.y);
}

typedef struct rectangle {
point upper_left;
point lower_right;
} rectangle;

rectangle move_rectangle(rectangle rect,/*2 copies*/
double delta_x,
double delta_y)
{
rect.upper_left =
move_point(rect.upper_left, delta_x, delta_y);/*6 copies*/
rect.lower_right =
move_point(rect.lower_right, delta_x, delta_y);/*6 copies*/
return rect;/*2 copies*/
}

void print_rectangle(rectangle rect)
{
printf("rectangle:\n");
print_point(rect.upper_left);
print_point(rect.lower_right);
printf("\n");
}

int main(void)
{
point p = { .x = 0.0, .y = 0.0 };
print_point(p);

p = move_point(p, 10, 10);
print_point(p);
printf("\n");

rectangle rect = {
.upper_left =  {.x =  0.0, .y = 10.0},
.lower_right = {.x = 10.0, .y =  0.0}
};
print_rectangle(rect);

rect = move_rectangle(rect, 10, 10);
print_rectangle(rect);

return 0;
}


typedef struct point {
double x, y;
} point;
point p = { .x = 0.0, .y = 0.0 };

typedef struct rectangle {
point upper_left;
point lower_right;
} rectangle;
rectangle rect = {
.upper_left =  {.x =  0.0, .y = 10.0},
.lower_right = {.x = 10.0, .y =  0.0}
};


#include <stdio.h>

typedef struct point {
double x, y;
} point;

void move_point_horisontally(point *p, double amount)
{
p->x += amount;
}

void move_point_vertically(point *p, double amount)
{
p->y += amount;
}

void move_point(point *p, double delta_x, double delta_y)
{
move_point_horisontally(p, delta_x);
move_point_vertically(p, delta_y);
}

void print_point(point *p)
{
printf("point <%.2f, %.2f>\n", p->x, p->y);
}

typedef struct rectangle {
point upper_left;
point lower_right;
} rectangle;

void move_rectangle(rectangle *rect,
double delta_x,
double delta_y)
{
move_point(&rect->upper_left, delta_x, delta_y);
move_point(&rect->lower_right, delta_x, delta_y);
}

void print_rectangle(rectangle *rect)
{
printf("rectangle:\n");
print_point(&rect->upper_left);
print_point(&rect->lower_right);
printf("\n");
}

int main(void)
{
point p = { .x = 0.0, .y = 0.0 };
print_point(&p);

move_point(&p, 10, 10);
print_point(&p);
printf("\n");

rectangle rect = {
.upper_left =  {.x =  0.0, .y = 10.0},
.lower_right = {.x = 10.0, .y =  0.0}
};
print_rectangle(&rect);

move_rectangle(&rect, 10, 10);
print_rectangle(&rect);

return 0;
}


#include <math.h>
#include <float.h>
#include <stdio.h>

typedef struct vector {
double x;
double y;
double z;
} vector;

void print_vector(vector const *v)
{
double x = v->x, y = v->y, z = v->z;
printf("<%.2f, %.2f, %.2f>\n", x, y, z);
}

double vector_length(vector *v)
{
double x = v->x, y = v->y, z = v->z;
return sqrt(x*x + y*y * z*z);
}
vector *shortest(int n, vector *vectors[n])
{
vector *shortest = &(vector){
.x = DBL_MAX, .y = DBL_MAX, .z = DBL_MAX
};
double shortest_length = vector_length(shortest);

printf("%p %p\n", (void *)shortest, (void *)&shortest_length);

for (int i = 0; i < n; ++i) {
vector *v = vectors[i];
double length = vector_length(v);
if (length < shortest_length) {
shortest = v;
shortest_length = length;
}
}

return shortest;
}

void trash_stack(void)
{
volatile char x[1000];
for (int i = 0; i < 1000; i++) {
x[i] = 0;
}
}

int main(void)
{
vector *vectors[] = {
&(vector){ .x = 10.0, .y = 13.0, .z = 42.0 },
&(vector){ .x = -1.0, .y = 32.0, .z = 15.0 },
&(vector){ .x =  0.0, .y =  3.0, .z =  1.0 }
};

print_vector(shortest(3, vectors));
print_vector(shortest(2, vectors));
print_vector(shortest(1, vectors));
print_vector(shortest(0, vectors)); // BOOOM!!!

vector *v = shortest(0, vectors);
print_vector(v);
trash_stack();
print_vector(v);

return 0;
}


vector *v = shortest(0, vectors);
print_vector(v);
trash_stack();
print_vector(v);


#include <math.h>
#include <float.h>
#include <stdio.h>

typedef struct vector {
double x;
double y;
double z;
} vector;

double vector_length(vector *v)
{
double x = v->x, y = v->y, z = v->z;
return sqrt(x*x + y*y * z*z);
}

void print_vector(vector const *v)
{
if (!v) {
printf("NULL\n");
} else {
double x = v->x, y = v->y, z = v->z;
printf("<%.2f, %.2f, %.2f>\n", x, y, z);
}
}

vector *shortest(int n, vector *vectors[n])
{
if (n < 1) return 0; // Return a NULL pointer

vector *shortest = vectors[0];
double shortest_length = vector_length(shortest);
for (int i = 1; i < n; ++i) {
vector *v = vectors[i];
double length = vector_length(v);
if (length < shortest_length) {
shortest = v;
shortest_length = length;
}
}

return shortest;
}

int main(void)
{
vector *vectors[] = {
&(vector){ .x = 10.0, .y = 13.0, .z = 42.0 },
&(vector){ .x = -1.0, .y = 32.0, .z = 15.0 },
&(vector){ .x =  0.0, .y =  3.0, .z =  1.0 }
};

print_vector(shortest(3, vectors));
print_vector(shortest(2, vectors));
print_vector(shortest(1, vectors));
print_vector(shortest(0, vectors)); // BOOOM!!!

vector const longest = {
.x = DBL_MAX, .y = DBL_MAX, .z = DBL_MAX
};
vector const *v = shortest(0, vectors);
v = v ? v : &longest;
print_vector(v);

return 0;
}


vector *shortest(int n, vector *vectors[n])
{
if (n < 1) return 0; // Return a NULL pointer


vector *shortest = &(vector){
.x = DBL_MAX, .y = DBL_MAX, .z = DBL_MAX
};


## const專論

For any type T, T const is a constant of that type.
For any type T, T * is a pointer to that type.

#include <stdio.h>

int main(void)
{
int i = 42;
int *ip = &i;
const int *cp = &i;

for (int j = 0; j < 10; ++j) {
i++;
printf("*ip == %d, *cp == %d\n", *ip, *cp);
}

int const x = 42;
int *ip_x = (int*)&x;
*ip_x = 13;
printf("*ip_x == %d", *ip_x);

return 0;
}


*ip == 43, *cp == 43
*ip == 44, *cp == 44
*ip == 45, *cp == 45
*ip == 46, *cp == 46
*ip == 47, *cp == 47
*ip == 48, *cp == 48
*ip == 49, *cp == 49
*ip == 50, *cp == 50
*ip == 51, *cp == 51
*ip == 52, *cp == 52
*ip_x == 13


const有assign的問題。比如:

#include <stdio.h>

int main(void)
{
int *p = 0;
int const **q = 0;
int const i = 42;
q = (int const **)&p;//created an alias for p in *q.
*q = &i;
// Now I have an int alias to an int const!
printf("&i == %p, *p == %p\n", (void *)&i, (void *)p);
*p = 5; // DANGER: We are trying to change const int
// This may or may not actually change i.
// It is up to the C compiler
printf("i == %d / %d\n", i, *p);
return 0;
}


&i == 0x7fff5a92f5f4, *p == 0x7fff5a92f5f4
i == 5 / 5


Here, we have an int **pointer p, we assign its address to an int const * const **pointer u, and when we then assign the address of an int const * const object, r, into *q, we have created not just one but two illegal aliases.

#include <stdio.h>

int main(void)
{
int *             i_p   = 0;
int const *       ic_p  = 0;
int * const       i_pc  = 0;
int const * const ic_pc = 0;

#if 0
i_p = i_p;     // Ok, T * => T *
ic_p = ic_p;   // Ok, T * => T * (T = U const)
i_pc = i_pc;   // No! You cannot assign to T const
ic_pc = ic_pc; // No! You cannot assign to T const
#endif

//i_p = ic_p; // No! T const * => T *
i_p = i_pc; // Ok, T * const => T *
//i_p = ic_pc; // No, there is a T const * => T *

ic_p = i_p;   // Ok, T * => T const *
ic_p = i_pc;  // Ok, T const => T then U * => U const *
ic_p = ic_pc; // Ok, T * const => T * (T = U const)

#if 0
int **               i_p_p   = 0;
int const **         ic_p_p  = 0;
int * const *        i_pc_p  = 0;
int const * const *  ic_pc_p = 0;
#endif
typedef int * T;
typedef int const * U;
T *        i_p_p   = 0;
U *        ic_p_p  = 0;
T const *  i_pc_p  = 0;
U const *  ic_pc_p = 0;
// plus the ones with const last, but they
// are simply const versions and we learn
// nothing new from them.

// We do not have const pointers here, so we can only apply
// T * => T * and T * => T const *, i.e. add const to the
// pointed at object. If the pointed at objects have different
// types then we cannot assign. Call the pointed at objects T and
// U for the right-hand side and left-hand side, respectively

// No, no and no
// i_p_p = ic_p_p; No, T = int *, U = int const *, T != U
// i_p_p = i_pc_p; No, T = int *, U = int * const, removing const not allowed
// i_p_p = ic_pc_p; No, T = int *, U = int const * const, T != U

// No, no and no
// ic_p_p = i_p_p; No, T = int const *, U = int *, T != U
// ic_p_p = i_pc_p; No, T = int const *, U = int * const, T != U
// ic_p_p = ic_pc_p; No, removing const

i_pc_p = i_p_p; // Ok, T const * => T *, T = int *, U = int *
// i_pc_p = ic_p_p; No, T = int * const, U = int const *, T != U
// i_pc_p = ic_pc_p; No, T = int const *, U = int * const, T != U

// ic_pc_p = i_p_p; No, int const * const != int *
ic_pc_p = ic_p_p; // Ok, Adding a const to int const *
//ic_pc_p = i_pc_p; No, int const * const != int * const

printf("to turn off warning... %p %p %p %p\n",
(void *)i_p, (void *)ic_p, (void *)i_pc, (void *)ic_pc);
printf("to turn off warning... %p %p %p %p\n",
(void *)i_p_p, (void *)ic_p_p, (void *)i_pc_p, (void *)ic_pc_p);

return 0;
}