指针--解决的疑惑

简单的就不说了,今天学链表,在链表中遇到了自己疑惑的事情,后来在网上查二级指针,搜出来一个,才解除了自己的疑惑

下面是对原文的复制,,最后有自己的链表程序--原文链接http://www.jb51.net/article/37516.htm

好久没有用过C/C++的二级指针了,总觉的它就是指针的指针,没什么大不了的,但是今天看到一道面试题,感觉自己对二级指针的理解还是不够深刻。于是,从网上找资料,学习了一番……

题目是这样的:

#include "stdafx.h"
#include <iostream>
using namespace std;
void GetMemory(char *p, int num)
{
 p = (char *)malloc(sizeof(char) * num);
 //p = new char[num];  //C++当中
}
int main(int argc, _TCHAR* argv[])
{
 char *str = NULL;
 GetMeory(str, 100);
 strcpy(str,"Hello");
 cout << str << endl;
 return 0;
}

问:程序能否达到目的:在GetMemory()中为main函数中的开辟空间,并将str指向这段空间?

分析:str是一个指针,指向NULL,形参p也是一个指针,初始也指向NULL,在GetMemory函数中,这个指针又指向了新开辟的空间。但是只是形参的指向改变了,实参str仍然指向NULL,并没有改变。因此,程序达不到题目的要求,而且运行时会出现错误,由于str一直指向NULL,执行strcop时,会出现错误,提示某某内存不能写入。

正确的方法应该采用双指针,程序如下:

#include "stdafx.h"
#include <iostream>
using namespace std;
void GetMeory(char **p, int num)
{
 *p = (char *)malloc(sizeof(char) * num);
 //*p = new char[num];  //C++当中
}
int _tmain(int argc, _TCHAR* argv[])
{
 char *str = NULL;
 GetMeory(&str, 100);
 strcpy(str,"Hello");
 cout << str << endl;
 return 0;
}

分析:str是一个指针,指向NULL。而调用GetMemory函数时,传递的是str的地址,p是一个二级指针,*p是一个指针。因此,将str的地址赋给临时变量p,则*p就是指针str的值,改变*p的值就相当于改变str的值。因此这种方法能够得到题目要求的效果。另外还有一种方法,采用一级指针,让函数返回一个指针变量,指向新分配的内存,程序如下:

#include "stdafx.h"
#include <iostream>
using namespace std;
char * GetMeory2(char *p, int num)
{
 p = (char *)malloc(sizeof(char) * num);
 //p = new char[num];  //C++当中
 return p;
}
int _tmain(int argc, _TCHAR* argv[])
{
 char *str = NULL;
 str = GetMeory2(str, 100);
 strcpy(str,"Hello");
 cout << str << endl;
 return 0;
}

2. 另外用二级指针还经常用在动态申请二维数组。

void main() 
{ 
int m , n , **p; 
scanf("%d%d" , &m , &n); 
p = (int **)malloc(m * sizeof(int *)) 
//C++中建议使用:p = new int* [m]; 
for(i = 0 ; i < m ; i++) 
p[i] = (int *)malloc(n * sizeof(int)); 
//C++:p[i] = new int[n]; 
} 

这样就实现了二维数组的动态申请,因为一般数组声明时,不允许下标是变量,所以如果想动态决定数组各维的大小,最好这样做。

关于那个面试题,,,我也挂了,看后才有所警觉

下面看我的关于链表的

struct MyStruct
{
    int num;//编号
    float corse;//成绩
    struct MyStruct *pNext;//链表的下一个节点
};
typedef struct MyStruct st;

void add(st **p, int inum, float icore)//传入头结点地址,插入数据
{
    if(*p == NULL)//判断链表是否为空
    {
        st *fast ;//创建结构体指针
        fast = (st *)malloc(sizeof(st));//为新指针分配内存
        fast->num = inum;
        fast->corse = icore;
        fast->pNext = NULL;
        *p = fast;
    }
    else//链表不为空
    {
        st *pp = *p;//*p是结构体指针,,,,pp是结构体指针
        while(pp->pNext != NULL)//
        {
            pp = pp->pNext;//pp往后搜索
        }
        //退出循环说明找到了最后一个节点pp
        st *last ;//创建结构体指针
        last = (st *)malloc(sizeof(st));//分配空间
        pp->pNext = last;
        last->corse = icore;
        last->num = inum;
        last->pNext = NULL;
    }
}

void main()
{
    st *pfast = NULL;//定义一个头结点地址

    add(&pfast, 1, 1);//传入头结点地址,插入数据
    printf("%p\n",pfast);

    add(&pfast, 2, 2);//传入头结点地址,插入数据
    printf("%p\n",pfast);

    add(&pfast, 3, 3);//传入头结点地址,插入数据
    printf("%p\n",pfast);

    add(&pfast, 4, 4);//传入头结点地址,插入数据
    printf("%p\n",pfast);

    printf("%d,%f\n",pfast->num,pfast->corse);
    printf("%d,%f\n",pfast->pNext->num,pfast->pNext->corse);

    getchar();
}

我打印了一下

add(&pfast, 1, 1);//传入头结点地址,插入数据
经过这一句pfast的指向当然是fast

我一直在怀疑

add(&pfast, 2, 2);//传入头结点地址,插入数据
经过这一句pfast的指向应该是last
所以我怀疑
printf("%d,%f\n",pfast->pNext->num,pfast->pNext->corse);
这一句应该会报错
因为
我感觉如果pfast的指向永远指向最后一个链表那么
pfast->pNext->num
就不存在
可是我一打印,,,
pfast的地址一直没变,,,,,永远指向第一个链表的地址
看了上述文章以后才有所警觉
其实如果一开始链表为空

*p = fast;

pfast确实指向了第一个链表

可是接着

add(&pfast, 2, 2);//传入头结点地址,插入数据

只是把第一个链表的地址赋值给了pp,,但是并没有改变*p的指向

下面的pp->pNext = last;只是给pp赋了新值

就如同

#include"stdio.h"
#include"stdlib.h"

void main()
{
    int i = 4;
    int *p = &i;
    int *pp = p;//就只是变量间的赋值 就像int a=1; int b; b=a;a的值并没有改变
  
    int j=3;
    pp = &j;

    printf("%p\n",p);
    printf("%p\n",pp);
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏zaking's

用js来实现那些数据结构09(集合01-集合的实现)

  说到集合,第一个想到的就是中学学到的那个数学概念:集合。在我们开始集合相关的js实现前,我们有必要来了解一下什么是集合以及集合的数学概念。   好吧,我们一...

37010
来自专栏Java架构沉思录

你真的懂Java中的String、StringBuilder和StringBuffer吗?

相信String这个类是Java中使用得最频繁的类之一,并且又是各大公司面试喜欢问到的地方,今天就来和大家一起学习一下String、StringBui...

1713
来自专栏Seebug漏洞平台

以太坊智能合约OPCODE逆向之理论基础篇

在我们对etherscan等平台上合约进行安全审查时,常常会遇到没有公布Solidity源代码的合约,只能获取到合约的OPCODE,所以一个智能合约的反编译器对...

2033
来自专栏Deep learning进阶路

C++随记(二)---动态分配内存问题(1)

C++随记(二)---动态分配内存问题(1) 面向对象的编程的一个特点就是在运行阶段(而不是编译阶段)进行决策。运行阶段决策提供了灵活性,可以根据当时的情况进行...

2010
来自专栏Python中文社区

Python元编程:控制你想控制的一切

專 欄 ❈松直,Python中文社区专栏作者,计算机在读,Python拥趸,知乎专栏:从Python开始❈ 很多人不理解“元编程”是个什么东西,关于它也没有一...

2178
来自专栏软件开发 -- 分享 互助 成长

C++中关于指针初始化和使用NULL的理解

1、严禁使用未被初始化的指针:C++创建指针的时候,只分配存储地址的内存,并不会分配存储数据的内存,所以指针可能指向任何位置。    (1)使用解除运算符(*)...

28910
来自专栏北京马哥教育

深入 Python 字典的内部实现

字典是通过键(key)索引的,因此,字典也可视作彼此关联的两个数组。下面我们尝试向字典中添加3个键/值(key/value)对: 这些值可通过如下方法访问: 由...

38515
来自专栏我是攻城师

10行Java代码实现最近被使用(LRU)缓存

3744
来自专栏Kotlin源码阅读

kotlin源码阅读——函数式编程

我主要写Kotlin源码阅读,函数式编程的基本概念,概念大家可以在网上做一些了解,这里推荐一下百度百科的定义,函数式编程概念,蛮清晰的。

3295
来自专栏微信公众号:Java团长

探秘Java中的String、StringBuilder以及StringBuffer

  相信String这个类是Java中使用得最频繁的类之一,并且又是各大公司面试喜欢问到的地方,今天就来和大家一起学习一下String、StringBuilde...

832

扫码关注云+社区

领取腾讯云代金券