二级指针的思考
最近在群里出现了一道面试提,看着打架讨论挺热烈就开始思考一下这个问题。下边先把题贴出来:
#include <stdio.h> #include <stdlib.h> #include <string.h> void Getmemery(char *p) { p=(char *)malloc(100); } void main() { char *str=NULL; Getmemery(str); strcpy(str,"hello world"); printf("%s",str); free(str); }
这就是这个道题了,这里边是有个错误的。不知道看出来没有。
其实要更清楚的搞懂这个问题,我们首先需要了解一个知识点。就是 堆、栈、自由存储区、全局/静态存储区和常量存储区。
我们在弄懂这个问题之后 看下边一个小例子:
1 int main () { 2 char* p= "hello"; 3 *p = "world"; 4 printf("%s",p); 5 return 0; 6 }
你编译运行之后就会发现错误,这个错误就在于你试图去改变 常量存储区 里边的内容。那我们需要修改的话怎么办?
int main () { char* p= "hello"; char *str = "world"; p = str; //改变地址的指向 printf("%s",p); return 0; }
改变它地址的指向,这个就是方法,你如果非要改变常量存储内内容也不是没有方法,这个不属于考虑范围。
上边的问题理解清楚了我们就这是开始进入下一个话题
指针数组
定义: 指针数组中每一个元素都存放一个地址。都当于一个指针变量。
示例: int* p[4]; char* type = {"C", "C++", "JAVA", "C#", "Ruby"};
1 int main() { 2 3 char* type[] = { "C", "C++", "JAVA", "C#", "Ruby" }; 4 int a[] = { 1,2,3,4,5 }; 5 int *num[5]; 6 7 for (int n = 0; n < 5; n++) { 8 printf("%s ", type[n]); 9 } 10 11 for (int i = 0; i < 5; i++) { 12 num[i] = &a[i]; 13 } 14 printf("\n"); 15 16 for (int j = 0; j < 5; j++) { 17 printf("%p ", num[j]); 18 } 19 printf("\n"); 20 for (int k = 0; k < 5; k++) { 21 printf("%d ", *num[k]); 22 } 23 printf("\n"); 24 25 return 0; 26 }
在这个例子中我们应该能分清楚 指针数组里边到底存的是什么。 换句话说 int *num = {&a[0], &a[1], &a[2], &a[3], &a[4], &a[5]}
基本了解指针数组之后,我们做点小的测试看看对于指针的概念怎么样?
1 void change(char *p); 2 int main() { 3 char old[] = { 1,3 ,5 }; 4 printf("&old[0] = %p\n", &old[0]); 5 printf("&old[1] = %p\n", &old[1]); 6 printf("&old[2] = %p\n", &old[2]); 7 char *p = old; // [1] 8 change(p); 9 for (int i = 0; i < 3;i++,p++) { 10 printf("%d ", *p); 11 } 12 printf("\n"); 13 return 0; 14 } 15 void change(char *p) { 16 char news[] = {2,4,6}; //[2] 17 p = news; //[3] 18 }
结果是 还是 1, 3, 5。 可能某些就会产生疑问了,怎么没有改变。不是使用的地址传递。 我们解释一下这个问题。C不同于C++,参数是通过值传递或者地址传递的,而不是通过引用。也就是说,实际参数 先自己copy一份,然后传递给形式参数 *P接收
在执行完第一步之后 也就是 7行, P指向old 的都地址。 执行第二步,对值进行拷贝出现P_COPY 指向old,添加新的数组。 第三步,P_COPY 指向new 数组的首地址。 这个过程是P_COPY 指向new 与main()函数里边的 P 没有半毛钱关系。 -- 听到这里可能明白,也可能有点晕,我们下边继续了解。怎么让两个真交换呢?
void change(char **p); int main() { char old[] = { 1,3 ,5 }; printf("&old[0] = %p\n", &old[0]); printf("&old[1] = %p\n", &old[1]); printf("&old[2] = %p\n", &old[2]); char *p = old; // [1] change(&p); // [2] for (int i = 0; i < 3;i++,p++) { printf("%d ", *p); } printf("\n"); return 0; } void change(char **p) { char *pt = NULL; static char news[] = {2,4,6}; //[3] //特别注意哈 static *p = news; //[4] }
在进行完第二步进行数值的拷贝, P_COPY = &P; 意味着 *P_COPY 指向 P所在空间, 改变*P_COPY 里边的值 会改变P 里边的值,即最改变最终指向。这里边和上题的区别是我们是用到P这个指针变量的地址,用 P_COPY存储, 使得P_COPY 和 P 产生了关联。 这是和上题最大的区别。
下边还是一个交换的例子自己体会一下。
1 void swap(char **str, char **dest); 2 int main() { 3 char *A = "abcd", *B = "efgh"; 4 printf("A = %p, B = %p\n", A, B); 5 printf("&A = %p, &B = %p\n", &A, &B); 6 swap(&A, &B); 7 8 printf("交换后: A = %p, B = %p\n", A, B); 9 printf("交换后: A = %s ,B = %s", A, B); 10 return 0; 11 } 12 void swap(char **str, char **dest) { 13 char *p = NULL; 14 printf("str = %p, dest = %p\n", str, dest); 15 p = *str; 16 *str = *dest; 17 *dest = p; 18 }
这是一个交换 的实例, 在C语言中有值传递和地址传递, 但是没有引用(c++)中概念。