Understanding and using c pointers 第三章读书笔记

来源:互联网 发布:win10如何开启23端口 编辑:程序博客网 时间:2024/05/01 03:53

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;}



按值传递
这里的按值传递是狭义的,即非指针传递,其实C语言函数全是按值传递的,作者也讲到了传递指向常量的指针
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);
编译就会产生错误,说第二个参数与其所传参数类型不匹配,也会报错说尝试修改由第一个参数指向的常量
(it will complain that we are attempting to modify the presumed constant referenced by the first
parameter).


返回指针
如果需要从函数中返回一个对象,常用到如下两种技术:
1.在函数中使用malloc分配内存并返回其地址。函数调用者负责释放返回的内存。
2.向函数传递一个对象并在函数中对其作出修改(之后返回该对象)。函数调用者负责分配与回收对象所占内存。


第一种方法的例子
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);
一旦用完它就需要将其释放掉,如果没有则会产生内存泄露。


指向本地数据的指针(Pointers to Local data)
如果你不懂程序栈的工作原理,那么返回一个指向本地数据的指针是很容易犯的错误。看如下例子:
int* allocateArray(int size,int value){int arr[size],i;for(i = 0; i < size; i++){arr[i] = value;}return arr;}
不幸的是,函数返回的数组地址在函数运行完毕后就失效了(no longer valid),因为函数的栈帧已经
从栈中弹出。尽管每个数组元素中的数值还是value,这些值可能在调用其他的函数后被覆盖掉。
解析如下:
int* vector = allocateArray(5,45);for(int i = 0; i < 5; i++){printf("%d\n", vector[i]);}
printf函数调用时将原来的数据45有可能覆盖,事实上gcc会给出警告:
warning:function returns address of local variable
一种变通方法是将函数中的变量声明为static的。this will allocate the variable's scope to the function
but allocate it outside of the stack frame.eliminating the possiblity of another function overwriting 
the variable's value:
int* allocateArray(int size,int value){static int arr[size];.....return arr;}
但是,这种方法并不是总能奏效。每次调用函数allocateArray时,它将会重复使用该数组(static表明改变量会在该函数的调用之间共享,因为使用的是
同一块内存)。这就使得前面对该函数的调用失效,另外静态数组的长度必须是固定长度的。这也限制了函数处理可变数组的能力。


传递null指针
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;}}



函数指针
使用函数指针时程序员需要注意确保恰当的使用,因为C不检查是否传递了正确的参数。
声明函数指针


使用函数指针

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));


函数指针和普通数据指针之间的转换并不能保障能正常工作
the use of void* is not guaranted to work with function pointers.也就是说,不能向下面一样将函数指针
赋值给void*指针。
void* pv = add;//不能这样做
然而交换函数指针时,常声明一个如下的"基"函数指针
fptrBase basePointer;fprrFirst = add;basePointer = (fptrToSingleInt)fptrFirst;fptrFirst = (fptrToTwoInts)basePointer;printf("%d\n",fptrFirst(5,6));


原创粉丝点击