指针--解决的疑惑

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

下面是对原文的复制,,最后有自己的链表程序--原文链接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);
}




				
时间: 2024-10-17 22:46:24

指针--解决的疑惑的相关文章

使用函数指针解决函数重载二义性调用问题

当实参对应重载函数的多个可行函数,且每个可行函数各自在一个实参上实现了更好的匹配时,编译器会因为程序具有二义性而报错. 例如: #include<iostream> using std::string; using std::cout; using std::cin; using std::endl; void ff(int, int) {//重载函数1 cout << "f1" << endl; } void ff(double, double =

树的插入(创建)为什么要使用指针的指针

一个二叉排序树的例子 首先看一个常见的二叉排序树的操作,下面的代码包括插入.创建和中序遍历.摘自这里. #include<stdio.h> #include<stdlib.h> typedef int ElemType; struct BTreeNode { ElemType data; struct BTreeNode* left; struct BTreeNode* right; }; //递归方式插入 void Insert(struct BTreeNode** BST, E

赋值运算符、拷贝初始化和this指针

一.赋值运算符和拷贝构造函数(重载技术) 赋值运算符和拷贝构造函数有编译器默认提供,但如果想做更复杂的事,需要重载. 1.下面用一个简单的例子先区分一下赋值运算符和拷贝构造函数: #include<iostream> using namespace std; class  alpha { public:     alpha():data(0) {}       //没有参数的构造函数     alpha(int d):data(d) {}  //一个参数的构造函数     void dipla

指针实现两数交换和指向函数的指针

指针就是地址,而指针变量就是存储地址的变量. 1.指针与函数 利用指针解决: C语言中函数的参数采用"单向传递",故第一个程序中改变形参不会改变实参,而在第二个程序中,使用指针变量作为参数,改变指针变量所指向的值,函数调用结束后,变量值被保留下来. 2.指向函数的指针的应用

【C++】 浅析智能指针

引言: 由于 C++ 语言没有自动内存回收机制,程序员每次 new 出来的内存都要手动 delete.程序员忘记 delete,流程太复杂,最终导致没有 delete,异常导致程序过早退出,没有执行 delete 的情况并不罕见. RAII(Resource Acquisition Is Initialization) 资源分配即初始化,定义一个类来封装资源的分配和释放,在构造函数完成资源的分配和初始化,在析构函数完成资源的清理,可以保证资源的正确初始化和释放. 所谓智能指针就是智能/自动化的管

智能指针的使用与陷阱

在包含指针的类中需要注意复制控制,复制指针时只复制指针中的地址,不会复制指针指向的对象. 大多数c++类采用三种方法管理指针成员: 1)指针成员采用常规指针型行为. 2)采用智能指针 3)采取值型行为 常规指针缺陷:可能会出现悬垂指针.当一个指针复制到另一个指针,两个指针指向同一个对象,当一个指针删除对象时,另一个指针不知道,所以出现悬垂指针.即使使用默认合成复制构造函数也会出现,类本身无法避免. 智能指针:加入了引用计数.引用计数跟踪该类有多少对象共享同一指针.当引用计数为0 时,删除对象.创

boost智能指针之shared_ptr和weak_ptr

std::auto_ptr很多的时候并不能满足我们的要求,比如auto_ptr不能用作STL容器的元素.boost的smart_ptr中提供了4种智能指针和2种智能指针数组来作为std::auto_ptr的补充. shared_ptr<boost/shared_ptr.hpp>:使用shared_ptr进行对象的生存期自动管理,使得分享资源所有权变得有效且安全. weak_ptr<boost/weak_ptr.hpp>:weak_ptr 是 shared_ptr 的观察员.它不会干

顿悟 - 对指针的理解

接触C语言也快两年了,对指针的理解也一直迷迷糊糊,虽然能用指针解决一些问题,但是对于复杂一点的问题,如两级指针的运用,却感到力不从心,无法做到运用自如,两下子就被搞得晕乎乎的了,究其原因,还是对于指针的理解不深刻导致的. 今天在回顾链表的创建时遇到了一些想不通的情况,于是下了“不到黄河心不死”的决心要把指针弄懂,弄透,终于对指针的理解有了质的提高,发现我以前对指针的理解大错特错了,于是将今天所悟记录下来,方便日后回顾. 如下: typedef struct _node { int data; s

require.js疑惑

昨天小颖分享了一篇require.js入门 ,今天小颖发现了一个很郁闷的问题,希望大神们帮小颖解释下到底是什么原理才能出现以下的现象,其实小颖昨天也有问过园友里的一位帅锅,只是他解释的小颖没太明白.嘻嘻所以写出来想通过博客园这个平台里集思广益,解决这个疑惑. 好啦我们一起来看看这个让小颖头疼的问题: demo目录: 代码来啦: 我们先来看看正常代码吧:   公用的文件index.html和ceshi.js index.html <!DOCTYPE html> <html> <