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


Posted by nathan2009729 on 2022-07-30

此篇為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,


#c pointer #big-endian&little-endian







Related Posts

DSA 實作教學網頁

DSA 實作教學網頁

[04] Renderless Component

[04] Renderless Component

Command Line 指令 & Vim 心得筆記

Command Line 指令 & Vim 心得筆記


Comments