tonybai在他的网站上写了一个本书的知识点纲要
栈帧的组成
返回值:程序中的函数执行完毕后返回的地址
本地变量存储区:为局部变量分配的内存
函数参数的存储区:为函数参数所分配的内存
栈和基指针(Stack and base pointers):运行时为管理栈所使用的指针
传递与返回指针
原文:
If the data needs to be modified in a function, it needs to be passed by pointer. We can
pass data by pointer and prohibit it from being modified by passing it as a pointer to a
constant, as will be demonstrated in the section “Passing a Pointer to a Constant” on
page 63. When the data is a pointer that needs to be modified, then we pass it as a pointer
to a pointer. This topic is covered in “Passing a Pointer to a Pointer” on page 68.
如果数据需要在函数中修改则需要传指针。我们可以将数据以常量指针传递以禁止数据被修改,这在63页的
“传递指向常量的指针”会展示。当传递的数据本身是指针且该指针需要被修改,我们将其作为指针的指针。该话题会在68页的“传递指向指针的指针”会涉及到
用指针传递数据
作者举了一例子
void swapWithPointers(int *pnum1,int *pnum2) { int tmp; tmp = *pnum1; *pnum1 = *pnum2; *pnum2 = tmp; } int main(void) { int n1 = 5; int n2 = 10; swapWithPointers(&n1,&n2); return 0; }
void passingAddressOfConstants(const int* num1,int* num2) { *num2 = *num1; } int main(void) { const int limit = 100; int result = 5; passingAddressOfConstants(&limit,&result); return 0; }上述程序不会报错,且函数会将100赋值给result
void passingAddressOfConstants(const int* num1,int* num2) { *num1 = 100; *num2 = 200; } const int limit = 100; passingAddressOfConstants(&limit,&limit);编译就会产生错误,说第二个参数与其所传参数类型不匹配,也会报错说尝试修改由第一个参数指向的常量
int* allocateArray(int size,int value) { int *arr = (int *) malloc(sizeof(int) * size); int i; for(i = 0; i < size; i++) { arr[i] = value; } return arr; } int* vector = allocateArray(5,45); int i; for(i = 0; i < 5; i++) { printf("%d\n",vector[i]); }
如图所示,左图显示return执行之前程序的状态,右图显示return执行程序的状态。执行后变量vector包含着
函数中分配的内存。尽管arr变量在函数执行完毕后就失效了(went away),但指针arr指向的内存并没有回收。
这些内存最终也需要被回收
尽管上面的例子运行正确,但是从函数中返回指针可能会发生一些潜在的问题如下:
1.返回没有初始化的指针
2.返回的指针指向不合法的地址
3.返回一个指向函数局部变量的指针
4.返回指针但却没有释放它(returning a pointer bu failing to free it)。
allocateArray()函数是典型的第四类问题。返回由函数中分配的内存意味着函数调用者需要负责释放他。
考虑如下例子:
int* vector = allocateArray(5,45); ...... free(vector);一旦用完它就需要将其释放掉,如果没有则会产生内存泄露。
int* allocateArray(int size,int value) { int arr[size],i; for(i = 0; i < size; i++) { arr[i] = value; } return arr; }不幸的是,函数返回的数组地址在函数运行完毕后就失效了(no longer valid),因为函数的栈帧已经
int* vector = allocateArray(5,45); for(int i = 0; i < 5; i++) { printf("%d\n", vector[i]); }printf函数调用时将原来的数据45有可能覆盖,事实上gcc会给出警告:
int* allocateArray(int size,int value) { static int arr[size]; ..... return arr; }但是,这种方法并不是总能奏效。每次调用函数allocateArray时,它将会重复使用该数组(static表明改变量会在该函数的调用之间共享,因为使用的是
int* allocateArray(int *arr,int size,int value) { if(arr != NULL) { for(int i = 0; i < size; i++) { arr[i] = value; } } return arr; } int* vector = (int*) malloc(5 * sizeof(int)); allocateArray(vector,5,45);往函数中传递指针时最好先检验一下他是不是NULL指针。
void allocateArray(int **arr,int size,int value) { *arr = (int *) malloc(size * sizeof(int)); if(*arr != NULL) { for(int i = 0; i < size; i++) { *(*arr + i) = value; } } } int* vector = NULL; allocateArray(&vector,5,45);编写自己的free函数,这样就无需为free函数中传递了NULL指针而麻烦
void safeFree(void **p) { if(p != NULL && *p != NULL) { free(*p); *p = NULL; } }
使用函数指针
int (*fptr1)(int); int square(int num) { return num * num; } int n = 5; fptr1 = square; //如同数组名一样,当单独使用函数名时,它返回函数的地址 printf("%d squared is %d\n",n,fptr1(n));对于上面fptr1 = square也可改为fptr1 = &square,这样更清楚。
typedef int (*funcptr)(int); ... funcptr = fptr2; fptr2 = square; printf("%d squared is %d\n",n,fptr1(n));
int add(int num1,int num2) { return num2 + num1; } int subtract(int num1,int num2) { return num1 - num2; } typedef int (*fptrOperation)(int,int); int compute(fptrOperation operation,int num1,int num2) { return operation(num1,num2); } printf("%d\n", compute(add,5,6)); printf("%d\n", compute(subtract,5,6));
fptrOperation select(char opcode) { switch(opcode) { case '+': return add; case '-': return subtract; } } int evaluate(char opcode,int num1,int num2) { fptrOperation operation = select(opcode); return operation(num1,num2); } printf("%d\n", evaluate('+',5,6)); printf("%d\n", evaluate('-',5,6));
typedef int (*operation)(int,int); operation operations[128] = {NULL};也可以这样声明
int (*operations[128])(int,int) = {NULL}; void initializeOperationsArray() { operation['+'] = add; //char其实是数值,所以这样并没有错 operation['-'] = subtract; } int evaluateArray(char opcode,int num1,int num2) { fptrOperation operation; operation = operations[opcode]; return operation(num1,num2); } initializeOperationsArray(); printf("%d\n", evaluateArray('+',5,6)); printf("%d\n", evaluateArray('-',5,6));
fptrOperation fptr1 = add; if(fptr1 == add) { printf("fptr1 points to add function\n"); } else { printf("fptr1 does not points to add function\n"); }
a pointer to one function can be cast to another type.this should be done with care since
the runtime system doesnot verify that parameters used by a function pointer are correct.
It is also possible to cast a function pointer to a different function pointer and then back.
The resulting pointer will be equal to the original pointer. The size of function pointers used
are not necessarily the same. 下面的代码解释了这个操作:
typedef int (*fptrToSingleInt)(int); typedef int (*fptrToTwoInt)(int,int); int add(int,int); fptrToTwoInts fptrFirst = add; fpreToSingleInt fptrSecond = (fptrToSingleInt)fptrFirst; fptrFirst = (fptrToTwoInts)(fptrSecond); printf("%d\n",fptrFirst(5,6));
void* pv = add; //不能这样做然而交换函数指针时,常声明一个如下的"基"函数指针
fptrBase basePointer; fprrFirst = add; basePointer = (fptrToSingleInt)fptrFirst; fptrFirst = (fptrToTwoInts)basePointer; printf("%d\n",fptrFirst(5,6));