24 / 07 / 12

「一生一芯」Learn C the hard way - Ex 15

练习 15:指针,可怕的指针

#include <stdio.h> int main(int argc, char *argv[]) { // create two arrays we care about int ages[] = {23, 43, 12, 89, 2}; char *names[] = { "Alan", "Frank", "Mary", "John", "Lisa" }; // safely get the size of ages int count = sizeof(ages) / sizeof(int); int i = 0; // first way using indexing for(i = 0; i < count; i++) { printf("%s has %d years alive.\n", names[i], ages[i]); } printf("---\n"); // setup the pointers to the start of the arrays int *cur_age = ages; char **cur_name = names; // second way using pointers for(i = 0; i < count; i++) { printf("%s is %d years old.\n", *(cur_name+i), *(cur_age+i)); } printf("---\n"); // third way, pointers are just arrays for(i = 0; i < count; i++) { printf("%s is %d years old again.\n", cur_name[i], cur_age[i]); } printf("---\n"); // fourth way with pointers in a stupid complex way for(cur_name = names, cur_age = ages; (cur_age - ages) < count; cur_name++, cur_age++) { printf("%s lived %d years so far.\n", *cur_name, *cur_age); } return 0; }

assert 宏用于在程序中添加断言。它检查表达式 expression 是否为真。如果表达式为假,程序将打印错误信息并终止执行。

试着将cur_age指向names。可以需要C风格转换来强制执行,试着查阅相关资料把它弄明白。

#include <stdio.h> int main(int argc, char *argv[]) { // create two arrays we care about int ages[] = {23, 43, 12, 89, 2}; char *names[] = { "Alan", "Frank", "Mary", "John", "Lisa" }; // safely get the size of ages int count = sizeof(ages) / sizeof(int); // setup the pointers to the start of the arrays int *cur_age = ages; char **cur_name = names; // forcing cur_age to point to names (note: this is just for demonstration) cur_age = (int *)names; // output using the modified cur_age pointer for(int i = 0; i < count; i++) { printf("%s has %d years alive.\n", *(cur_name + i), *(cur_age + i)); } return 0; }
⮞ ./ex15_0 Alan has -1933959164 years alive. Frank has 22046 years alive. Mary has -1933959159 years alive. John has 22046 years alive. Lisa has -1933959153 years alive.

附加题 1

使用访问指针的方式重写所有使用数组的地方。

#include <stdio.h> int main(int argc, char *argv[]) { // create two arrays we care about int ages[] = {23, 43, 12, 89, 2}; char *names[] = { "Alan", "Frank", "Mary", "John", "Lisa" }; // safely get the size of ages int count = sizeof(ages) / sizeof(int); // setup the pointers to the start of the arrays int *cur_age = ages; char **cur_name = names; // first way using pointers for(int i = 0; i < count; i++) { printf("%s has %d years alive.\n", *(cur_name + i), *(cur_age + i)); } printf("---\n"); // second way using pointers for(int i = 0; i < count; i++) { printf("%s is %d years old.\n", *(cur_name + i), *(cur_age + i)); } printf("---\n"); // third way using pointers for(int i = 0; i < count; i++) { printf("%s is %d years old again.\n", *(cur_name + i), *(cur_age + i)); } printf("---\n"); // fourth way with pointers in a slightly complex way for(; cur_age < ages + count; cur_name++, cur_age++) { printf("%s lived %d years so far.\n", *cur_name, *cur_age); } return 0; }

附加题 2

使用访问数组的方式重写所有使用指针的地方。

#include <stdio.h> int main(int argc, char *argv[]) { // create two arrays we care about int ages[] = {23, 43, 12, 89, 2}; char *names[] = { "Alan", "Frank", "Mary", "John", "Lisa" }; // safely get the size of ages int count = sizeof(ages) / sizeof(int); int i = 0; // first way using indexing for(i = 0; i < count; i++) { printf("%s has %d years alive.\n", names[i], ages[i]); } printf("---\n"); // second way using pointers (converted to array indexing) for(i = 0; i < count; i++) { printf("%s is %d years old.\n", names[i], ages[i]); } printf("---\n"); // third way, pointers are just arrays (already using array indexing) for(i = 0; i < count; i++) { printf("%s is %d years old again.\n", names[i], ages[i]); } printf("---\n"); // fourth way with pointers in a stupid complex way (converted to array indexing) for(i = 0; i < count; i++) { printf("%s lived %d years so far.\n", names[i], ages[i]); } return 0; }

附加题 3

在其它程序中使用指针来代替数组访问。

附加题 4

使用指针来处理命令行参数,就像处理names那样。

附加题 5

将获取值和获取地址组合到一起。

#include <stdio.h> int main(int argc, char *argv[]) { int i = 0; // Output addresses and values of argv for(i = 0; i < argc; i++) { printf("argv[%d]: %p: %s\n", i, (void *)&argv[i], argv[i]); } printf("---\n"); // let's make our own array of strings char *states[] = { "California", "Oregon", "Washington", "Texas" }; int num_states = 4; // Output addresses and values of states for(i = 0; i < num_states; i++) { printf("states[%d]: %p: %s\n", i, (void *)&states[i], states[i]); } return 0; }
⮞ ./ex15_5 aaaa bbbb cccc argv[0]: 0x7ffddf1cce58: ./ex15_5 argv[1]: 0x7ffddf1cce60: aaaa argv[2]: 0x7ffddf1cce68: bbbb argv[3]: 0x7ffddf1cce70: cccc --- states[0]: 0x7ffddf1ccd10: California states[1]: 0x7ffddf1ccd18: Oregon states[2]: 0x7ffddf1ccd20: Washington states[3]: 0x7ffddf1ccd28: Texas

附加题 6

在程序末尾添加一个for循环,打印出这些指针所指向的地址。你需要在printf中使用%p

// print the addresses of the pointers for(i = 0; i < count; i++) { printf("Address of names[%d]: %p, Address of ages[%d]: %p\n", i, (void*)&names[i], i, (void*)&ages[i]); }
Address of names[0]: 0x7fff7758f590, Address of ages[0]: 0x7fff7758f570 Address of names[1]: 0x7fff7758f598, Address of ages[1]: 0x7fff7758f574 Address of names[2]: 0x7fff7758f5a0, Address of ages[2]: 0x7fff7758f578 Address of names[3]: 0x7fff7758f5a8, Address of ages[3]: 0x7fff7758f57c Address of names[4]: 0x7fff7758f5b0, Address of ages[4]: 0x7fff7758f580

附加题 7

对于每一种打印数组的方法,使用函数来重写程序。试着向函数传递指针来处理数据。记住你可以声明接受指针的函数,但是可以像数组那样用它。

#include <stdio.h> // Function declarations void print_by_indexing(char **names, int *ages, int count); void print_by_pointer_arithmetic(char **names, int *ages, int count); void print_by_pointer_as_array(char **names, int *ages, int count); void print_by_complex_pointer_arithmetic(char **names, int *ages, int count); void print_addresses(char **names, int *ages, int count); int main(int argc, char *argv[]) { // create two arrays we care about int ages[] = {23, 43, 12, 89, 2}; char *names[] = { "Alan", "Frank", "Mary", "John", "Lisa" }; // safely get the size of ages int count = sizeof(ages) / sizeof(int); // Using different methods to print arrays print_by_indexing(names, ages, count); printf("---\n"); print_by_pointer_arithmetic(names, ages, count); printf("---\n"); print_by_pointer_as_array(names, ages, count); printf("---\n"); print_by_complex_pointer_arithmetic(names, ages, count); printf("---\n"); // Print the addresses of the pointers print_addresses(names, ages, count); return 0; } // Function definitions void print_by_indexing(char **names, int *ages, int count) { for(int i = 0; i < count; i++) { printf("%s has %d years alive.\n", names[i], ages[i]); } } void print_by_pointer_arithmetic(char **names, int *ages, int count) { for(int i = 0; i < count; i++) { printf("%s is %d years old.\n", *(names + i), *(ages + i)); } } void print_by_pointer_as_array(char **names, int *ages, int count) { for(int i = 0; i < count; i++) { printf("%s is %d years old again.\n", names[i], ages[i]); } } void print_by_complex_pointer_arithmetic(char **names, int *ages, int count) { char **cur_name = names; int *cur_age = ages; for(int i = 0; i < count; i++, cur_name++, cur_age++) { printf("%s lived %d years so far.\n", *cur_name, *cur_age); } } void print_addresses(char **names, int *ages, int count) { for(int i = 0; i < count; i++) { printf("Address of names[%d]: %p, Address of ages[%d]: %p\n", i, (void*)&names[i], i, (void*)&ages[i]); } }

附加题 8

for循环改为while循环,并且观察对于每种指针用法哪种循环更方便。

#include <stdio.h> int main(int argc, char *argv[]) { // create two arrays we care about int ages[] = {23, 43, 12, 89, 2}; char *names[] = { "Alan", "Frank", "Mary", "John", "Lisa" }; // safely get the size of ages int count = sizeof(ages) / sizeof(int); int i = 0; // first way using indexing i = 0; while(i < count) { printf("%s has %d years alive.\n", names[i], ages[i]); i++; } printf("---\n"); // setup the pointers to the start of the arrays int *cur_age = ages; char **cur_name = names; // second way using pointers i = 0; while(i < count) { printf("%s is %d years old.\n", *(cur_name+i), *(cur_age+i)); i++; } printf("---\n"); // third way, pointers are just arrays i = 0; while(i < count) { printf("%s is %d years old again.\n", cur_name[i], cur_age[i]); i++; } printf("---\n"); // fourth way with pointers in a stupid complex way cur_name = names; cur_age = ages; i = 0; while((cur_age - ages) < count) { printf("%s lived %d years so far.\n", *cur_name, *cur_age); cur_name++; cur_age++; i++; } return 0; }

看起来还是 for 更加简单呢。