C语言二重指针与malloc

(内容主要源于网上,只是加入了些自己的剖析)

假设有一个二重指针:

char **p;

同时有一个指针数组

char *name[4];

如何引用p呢? 首先我们有程序代码如下

#include <stdio.h>
int main() {
    char *s = "I love you";
    char *s1 = "you love me";
    char *s2 = "she love you";
    char *s3 = "he love her";
    char *name[4];
    name[0] = s;
    name[1] = s1;
    name[2] = s2;
    name[3] = s3; 

    char **p;
    p = name + 2;
    printf("%d\n", *p);
    printf("%d\n", **p);
    printf("%c\n", **p); //为什么这个打印s
    printf("%d\n", p);
    printf("%d\n", *&p);
    printf("%d\n", &p);
    printf("%s\n", *p);  //而这个打印出了字符串
    return 0;
}

 输出结果依次为:

4333636

115

s

1244976

1244976

1244964

she love you

分析:

1、 p是一个二重指针,它本身是一个变量。既然是变量,那么它本身就应该存储值。 这就和 int i = 65;一样,i 存储了数值 65。而p存储的是地址,地址也是一种数值。 所以当我们打印p时,打印出来的就是p中存储的地址数值,结合本题就是1244976。

2、 同理:打印*&p,我们先取p的地址(&p),p是变量,当然自身也会有存放的地址;然后我们 用*去用该地址上存储的数值,显然就是p这个变量所代表的数值,当然还是1244976。

3、 我们在分析用%d来打印*p, 为何得到了4333636。 理由是:p是二重指针,所以*p表示还是一个一重指针变量,它的具体表示可以是*(p + 0)。 为了便于理解,我们这样假设有一个 char *row; row = *(p + 0);这样我们就把问题转化 成了一维的指针了,我们打印*p,其实就是打印row,这样我们可以参考分析(1)。

4、 分析用%s来打印*p,为何得到了 she love you 其实我们完全可以利用分析(3)的思路,这时候,row代表的是这句话的首地址, 我们通过该句的首地址,来打印这个字符串。

5、 我们再来分析用%d打印**p 为何得到的是115;其实我们可以这样来看*(*(p + 0) + 0);这样的好处是可以简化,变成*(row + 0);既然row是一维 指针,那么这个(row + 0)代表的就是我们想要打印的字符串的第0个字符的地址!那么我们用* 来用这个地址的值,刚好就是‘s‘,便忘了‘s‘对应的ACSII码就是115!

6、 分析%c 为何能打印‘s‘。分析(5)已经全说了。

相关面试题

错误的例程:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void Getmemery(char *p)
{
    p=(char *)malloc(100);  //下面一行是自己加的  *p=1; //为了对比问题出在哪里
}

void main()
{
    char *str=NULL;

    Getmemery(str);
    strcpy(str,"hello world");
    printf("%s",str);
    free(str);
}

编译时通过,运行时会输出段错误。

其实还是C里面经典的错误(值传递swap问题),为什么第一眼看上去是对的,因为我们一贯地认为指针传递就可以这么操作,然后忽略了后面的修改是否有效。

错就错在:传入了指针,后面做的却不是对指针的操作,而是对指针变量的操作。而对指针变量的操作又变成了原来类似值传递swap的问题,使其对指针的修改无效。指针变量与其他普通类型的变量没太大区别,都是一段内存的别称(只不过指针变量表示的内存放的是地址,其他类型的变量放的是数据),传入子函数后,只是将以str为别称的内存的值NULL地址传给了以p为别称的内存,以p为别称的内存则通过malloc成了另一块内存的别称。但是子函数结束后,回到原来的主函数,str还是原来的str,并没有发生变化,还是NULL指针。

若想要其有效只能,用指针的方式传入str(其实指针变量也是某一块内存的内容)[&str]并对指针操作(下面第2种修改方法),或者将修改后的指针变量返回(下面第1种方法)。

一种修正方法:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char *Getmemery(void)  //通过返回指针变量
{
    char *p=(char *)malloc(100);
    return p;
}

void main()
{
    char *str=NULL;

    str = Getmemery();
    strcpy(str,"hello world");
    printf("%s",str);
    free(str);
}

另一种修改方法

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void Getmemery(void **p)  //通过使用二重指针
{
    *p=(void **)malloc(100);
}

void main()
{
    char *str=NULL;

    Getmemery(&str);
    strcpy(str,"hello world");
    printf("%s",str);
    free(str);
}

分析一下之前的程序为什么会错了。

(1)void Getmemery(char *p)

(2)char *str=NULL;

(3)Getmemery(str);

1中子程序的形参是一个指针,然后很自然会想到2,3中的调用方式,本来的想法是用malloc分配内存,然后修改传入的指针变量,那么最后就根据通过 strcpy(str,"hello world"); 就可以向分配的内存里面写数据了。一切都是那样流畅,对,因为这个用法平时用习惯了,所以根本不会去考虑正确性。

然而,这里就出问题了。首先,Getmemery(str) 传递的是 srt指针的地址,这个没有问题,C不同于C++,参数是通过传递的,而不是通过引用。也就是说,实际参数 str 先自己copy一份,然后传递给形式参数 *P接收,这个C语言的指针的时候已经强调多次了,但是自己还是错了啊,哈哈。

然后,在子程序里面,如果通过 *P 那么访问到的将是 *str的内容,这是等价的。但是,本程序一个致命的错误,非常隐蔽,那是子程序企图修改 p 的内容,而不是 *p 的内容!!这个错误找了我很久终于给揪出来了。修改了 p 的值是没有意义的,这个值是形式参数,并不会返回任何的东西,而 *p 则是通过p的地址直接访问需要的变量,这是不同的用法。所以说白了,void Getmemery(char *p) 执行之后并没有改变任何的东西,str的值并没有修改过,保持NULL,所以访问 *0 地址会被操作系统禁止,得到一个错误。

解决办法,是用2重指针。目的是要修改指针的地址,但是按照上面的分析,我们并不能去修改,但是我们可以用2重指针,将*str的地址值str,用2重指针来改变。

void Getmemery(void **p)
{
 *p=(void **)malloc(100);
}

子程序修改为这个样子,出入的参数也得修改

char *str=NULL;
Getmemery(&str);

那么可以这样理解,因为形参是2重指针,所以 p 对应 &str ,*P 对应 str,之前说了,我们的目的是要修改 str的值,所以很自然,我们用 *p = xxx 这样的形式去修改了。

这样得到的程序就正确了。

 

malloc

标准3部曲:malloc + free +指针置空

malloc申请的是堆(heap)空间

/*
date:20100824
description:malloc使用规范探讨
in参数:申请的堆内存字节数,注意int,short,float需要自己乘上相应字节数。
out返回值:void *
*/
main()
{
char *str=NULL;
str=(char *)malloc(10);   //注意malloc返回值是void *,申请时需要强制转换成需要的类型
memset(str,0,10);       //如果不清空,申请的区域值是随机的,养成好习惯
strcpy(str,"happylife");   //使用strcpy特别注意拷贝的字符串长度<=10-1,即要预留字符串结束标志‘\0‘
puts(str);
free(str);
printf("str[address]:%s[%x]\n",str,str);  //这里的str内容为空,但是指针地址还在

str=NULL;            //注意指针free之后该指针仍然存在,最好将它指向为空。
printf("str[address]:%s[%x]\n",str,str);  //这里的str内容为空,地址也为空
}

  

时间: 2024-10-09 23:23:59

C语言二重指针与malloc的相关文章

C语言 二重指针深入理解

k->[sss] ------>k是指针,指向内存区,内存区存的数据是sss   ,那么,*K的内容就是sss           (*k)<==>sss sss->[hhh]---->sss是新的地址,是指针,指向内存区域,内存区域的数据是hhh,那么,*sss的内容就是hhh     (*sss)<==>hhh 综上所述,二重指针(*(*k))=====>就等于hhh 也就是取k地址的内容作为一个新的地址,然后再取这个新的地址的内容 2.思考链表

C语言中指针*p[N], (*P)[N],及**p的区别

在C语言编程中指针经常困扰着我们,但是若能灵活运用指针的话,将会使得我们编程变得更加轻松与高效.这里讲下*p[N], (*P)[N],及**p的区别,这也是之前经常困扰我的地方. 这三者的定义分别为: int  *p[N]表示指针数组,也就是说定义了N个不同指向int型的指针. int (*p)[N]表示定义一个指针,指向一个int[N]型的指针. int **p 表示定义一个指向指针的指针. 注意[]的优先级要高于*. 指针即表示一个存放某种数据类型变量的地址,例如: #include <st

C/C++语言的标准库函数malloc/free与运算符new/delete的区别

概括地说 1.malloc与free是C++/C的标准库函数,new/delete是C++的运算符,它们都可用于申请动态内存和释放内存. 2.对于非内部数据类型的对象而言,只用malloc/free无法满足动态对象的要求.对象在创建的同时,要自动执行构造函数,对象在消亡之前要自动执行析构函数.而由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free. 3.因此C++语言需要一个能完成动态内存分配和初始化工作的运算符

关于C语言的指针、链表的原理和各类操作

今天课上我们老师为我们讲述了c语言的指针.链表的原理以及各类操作. 一.指针 1.指针 指针是一个存储计算机内存地址的变量.从指针指向的内存读取数据称作指针的取值.指针可以指向某些具体类型的变量地址,例如int.long和double.指针也可以是void类型.NULL指针和未初始化指针.指针是一个存储计算机内存地址的变量.从指针指向的内存读取数据称作指针的取值.指针可以指向某些具体类型的变量地址,例如int.long和double.指针也可以是void类型.NULL指针和未初始化指针. 2.数

指针做参数的动态内存分配与二重指针(上)

C中的动态内存分配问题: 格式:Int *pointer; Pointer = (int *)malloc(100 * sizeof(int)); 可以在被调用函数(该函数返回指针的函数)中动态分配内存,(并且该内存是分配在堆内存中的,故而被调函数可以返回指向该堆内存的指针),然后返回该指针值,该指针指向动态分配的内存,然后可以在主函数中free掉pointer.哪怕在主函数中将该指针值赋值给pointer_2,free掉pointer_2也是可以的,(可以理解的,它们都指向该堆内存),以避免出

指针做参数的动态内存分配与二重指针(下)

要实现指针作为函数形参,改变实参指针的值.一般有两种做法: 使用双指针,或者指针数组形式作为形参,将实参的地址传入函数,也即要给形参传入指针的地址! http://blog.csdn.net/liuyajun2013/article/details/17151309#0-tsina-1-79483-397232819ff9a47a7b7e80a40613cfe http://www.cnblogs.com/heat-man/p/4646051.html http://www.jb51.net/a

C#委托与C语言函数指针及函数指针数组

C#委托与C语言函数指针及函数指针数组 在使用C#时总会为委托而感到疑惑,但现在总新温习了一遍C语言后,才真正理解的委托. 其实委托就类似于C/C++里的函数指针,在函数传参时传递的是函数指针,在调用的时候通过指针访问这个函数. 在C语言中函数指针的申明如下: //可以理解为申明一个指着变量 Func ,它的类型是 返回Type(可以为 void )类型的参数,接收 (Type one,Type two,...)类型的//参数(可以不接受参数). Type *Func(Type one,Type

C语言的指针变量

C语言的指针变量 在C语言中,变量是固定范围的存储空间,它存储的是赋给他的值, 比如: int a = 12; /* 这里是定义一个整型变量a,并把12这个值存储在a的地址空间上 这个地址空间是系统随机分配的,对用户是透明的不用关心 */ 指针变量存储的是变量的地址, 比如: int a = 12 , *b; b = &a; /*如上,定义了一个整型变量a并赋值12,一个整型指针变量b 然后 用 & 取值运算符 取到变量a的地址空间值,存储到指针变量b中 此时变量b中存储的是变量a中的地址

黑 马 程 序 员_视频学习总结&lt;c语言&gt;----03 指针

---------------------- ASP.Net+Unity开发..Net培训.期待与您交流! ---------------------- 一.什么是指针? 用来存放变量地址的变量,就称为"指针变量". 二.指针的定义 一般形式:类名标识符  *指针变量名; int *p; float *q; "*"是一个说明符,用来说明这个变量是个指针变量,是不能省略的,但它不属于变量名的一部分 前面的类型标识符表示指针变量所指向的变量的类型,而且只能指向这种类型的