二级指针的思考

最近在群里出现了一道面试提,看着打架讨论挺热烈就开始思考一下这个问题。下边先把题贴出来:

#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++)中概念。

 

posted @ 2015-09-10 09:26  吉吉熊  阅读(111)  评论(0编辑  收藏  举报
.catelog-title {margin-left:0}