此篇為Pointers in C Programming A Modern Approach to Memory Management, Recursive Data Structures, Strings, and Arrays
這本書第四章筆記。
程式1:integer-bytes.c
#include <stdio.h>
int main(void)
{
int a[] = { 1, 2, 3, 4, 5 };
int n = sizeof a / sizeof *a;
for (int i = 0; i < n; i++) {
printf("%d = [", a[i]);
char *cp = (char *)(a + i);
for (int j = 0; j < sizeof(*a); j++) {
printf(" %d ", cp[j]);
}
printf("]\n");
}
return 0;
}
輸出:
1 = [ 1 0 0 0 ]
2 = [ 2 0 0 0 ]
3 = [ 3 0 0 0 ]
4 = [ 4 0 0 0 ]
5 = [ 5 0 0 0 ]
For the array, a + i is i integers past a, but cp +
j is j characters past cp. The type of the pointer/array determines what the step size is
when we add a number to them.
至於為什麼是0在後面,則跟big-endian或little-endian有關。
big-endian&little-endian模擬程式如下
程式2:endianess.c
#include <stdio.h>
int little_endianess(int i)
{
char *cp = (char *)&i;
int result = 0, coef = 1;
for (int j = 0; j < sizeof i; j++) {
result += coef * cp[j];
coef *= 256;
}
return result;
}
int big_endianess(int i)
{
char *cp = (char *)&i;
int result = 0, coef = 1;
for (int j = sizeof i - 1; j >= 0; j--) {
result += coef * cp[j];
coef *= 256;
}
return result;
}
int main(void)
{
for (int i = 0; i < 10; i++) {
printf("%d: little = %d, big = %d\n",
i, little_endianess(i), big_endianess(i));
}
return 0;
}
輸出:
0: little = 0, big = 0
1: little = 1, big = 16777216
2: little = 2, big = 33554432
3: little = 3, big = 50331648
4: little = 4, big = 67108864
5: little = 5, big = 83886080
6: little = 6, big = 100663296
7: little = 7, big = 117440512
8: little = 8, big = 134217728
9: little = 9, big = 150994944
big-endian&little-endian圖示如下:
指標混亂的別名情況...比如說用int的方式解釋double就會出問題,以下範例:
程式3:interpretation.c
#include <stdio.h>
int main(void)
{
printf("sizes: double = %zu, long = %zu, int = %zu, char = %zu\n",
sizeof(double), sizeof(long), sizeof(int), sizeof(char));
double d;
double *dp = &d;
long *lp = (long *)&d;
int *ip = (int *)&d;
char *cp = (char *)&d;
printf("dp == %p, lp = %p\nip == %p, cp == %p\n\n", dp, lp, ip, cp);
d = 42.0;
printf("*dp == %.20f, *lp == %ld, *ip == %d, *cp == %d\n",
*dp, *lp, *ip, *cp);
*ip = 4200;
printf("*dp == %.20f, *lp == %ld, *ip == %d, *cp == %d\n",
*dp, *lp, *ip, *cp);
*cp = 42;
printf("*dp == %.20f, *lp == %ld, *ip == %d, *cp == %d\n",
*dp, *lp, *ip, *cp);
return 0;
}
輸出:
sizes: double = 8, long = 8, int = 4, char = 1
dp == 0x7ffd35de8030, lp = 0x7ffd35de8030
ip == 0x7ffd35de8030, cp == 0x7ffd35de8030
*dp == 42.00000000000000000000, *lp == 4631107791820423168, *ip == 0, *cp == 0
*dp == 42.00000000002984279490, *lp == 4631107791820427368, *ip == 4200, *cp == 104
*dp == 42.00000000002940225841, *lp == 4631107791820427306, *ip == 4138, *cp == 42
指標vs陣列
程式4:
#include <stdio.h>
#include <assert.h>
int main(void)
{
int a[] = { 1, 2, 3, 4, 5 };
int n = sizeof a / sizeof *a;
// get a pointer to the beginning of a
int *ip = a;
char *cp = (char *)a;
for (int i = 0; i < n; i++) {
printf("a[%d] sits at %p / %p / %p\n",
i, (void *)&a[i], (void *)(ip + i),
(void *)(cp + i * sizeof(int)));
}
for (int i = 0; i < n; i++) {
// Add an integer to a pointer/array
// to get an element at an offset
assert(ip + i == a + i);
// The offset is the address at
// that index
assert(ip + i == &a[i]);
// Dereference and you get the value
assert(*(ip + i) == a[i]);
// The index operator is also valid
// for pointers
assert(ip[i] == a[i]);
}
return 0;
}
輸出:
a[0] sits at 0x7fff6b15e800 / 0x7fff6b15e800 / 0x7fff6b15e800
a[1] sits at 0x7fff6b15e804 / 0x7fff6b15e804 / 0x7fff6b15e804
a[2] sits at 0x7fff6b15e808 / 0x7fff6b15e808 / 0x7fff6b15e808
a[3] sits at 0x7fff6b15e80c / 0x7fff6b15e80c / 0x7fff6b15e80c
a[4] sits at 0x7fff6b15e810 / 0x7fff6b15e810 / 0x7fff6b15e810
strict alias rule
程式5:strict-alias.c
#include <stdio.h>
#include <stdalign.h>
int f(int *i, long *l)
{
*i = -1;
*l = 0;
return *i;
}
int g(char *c, long *l)
{
*c = -1;
*l = 0;
return *c;
}
int h(double *c, long *l)
{
*c = -1;
*l = 0;
return (int)*c;
}
int main(void)
{
long x;
int i = f((int *)&x, &x);
printf("x == %ld, f(&x,&x) == %d\n", x, i);
i = g((char *)&x, &x);
printf("x == %ld, g(&x,&x) == %d\n", x, i);
i = h((double *)&x, &x);
printf("x == %ld, h(&x,&x) == %d\n", x, i);
return 0;
}
The strict alias rule says that i and l cannot point to the same object, because they
do not have compatible types. So when the compiler works out what to return from f(),
it can see that we just assigned -1 to *i
, and the rule tells it that the assignment to *l
cannot have changed that, so it concludes that it can return -1 and that it does not need
to fetch *i
once more from memory.
根據以上敘述,可知有無開優化,結果會不同。
沒開優化輸出:
x == 0, f(&x,&x) == 0
x == 0, g(&x,&x) == 0
x == 0, h(&x,&x) == 0
有開優化輸出
x == 0, f(&x,&x) == -1
x == 0, g(&x,&x) == 0
x == 0, h(&x,&x) == -1
void指標應用--qsort
程式6:void.c
#include <stdio.h>
#include <string.h>
int int_compare(void const *x, void const *y)
{
// Get the objects, and interpret them as integers
int const *a = x;
int const *b = y;
return *a - *b;
}
int string_compare(void const *x, void const *y)
{
// Get the objects and interpet them as strings
char * const *a = x;
char * const *b = y;
return strcmp(*a, *b);
}
int main(void)
{
int int_array[] = { 10, 5, 30, 15, 20, 30 };
int int_array_length =
sizeof int_array / sizeof *int_array;
qsort(int_array, int_array_length,
sizeof *int_array, int_compare);
printf("int_array = ");
for (int i = 0; i < int_array_length; i++) {
printf("%d, ", int_array[i]);
}
printf("\n");
char *string_array[] = { "foo", "bar", "baz" };
int string_array_length =
sizeof string_array / sizeof *string_array;
qsort(string_array, string_array_length,
sizeof *string_array, string_compare);
printf("string_array = ");
for (int i = 0; i < string_array_length; i++) {
printf("%s, ", string_array[i]);
}
printf("\n");
return 0;
}
輸出:
int_array = 5, 10, 15, 20, 30, 30,
string_array = bar, baz, foo,