当前位置: 亚洲城ca88 > ca88 > 正文

小心C语言的概念与表明,C语言中malloc函数重返值

时间:2019-08-30 00:54来源:ca88
  小心C语言的概念与注明 转自360博客 注:为方便表达难点,文中提起的变量和函数都被简化。 一、起源 DBProxy在测量试验进程中,发掘对其施行某步管理操作后,程序临时会崩溃,但

 

小心C语言的概念与注明

转自360博客

注:为方便表达难点,文中提起的变量和函数都被简化。

一、起源

DBProxy在测量试验进程中,发掘对其施行某步管理操作后,程序临时会崩溃,但不是历次都冒出。

二、GDB跟踪

几度数次测量试验,然后用GDB展开core dump文件,查看程序崩溃时的货仓,开掘大概的倒台只有两处,这两处的共同点是前方都调用了三个函数get_pointer得到叁个指南针,如下图所示:
图片 1

下一场在选取该指针进行下步操作时前后相继崩溃。

翻看该指针的值,开掘其针对性二个不行地址,所以操作该地点发生了段错误,如下图所示:
图片 2

三、无效地址的产生原因

函数get_pointer的原型是char *get_pointer(void),函数体内只是通过轻松的malloc操作获取二个char*品类的指针,然后回到给调用者。
malloc()结果独有二种:

  1. 成功,重临多个针对合法地址的指针
  2. 失败,返回NULL

何以会回到二个空头地址呢?
在get_pointer函数重临前步向二个printf语句,打字与印刷将在再次来到给调用者的指针值:
图片 3
再在调用处之后也投入一个printf语句,打字与印刷调用者接收到的指针值:
图片 4
再一次编写翻译,一再开展频仍测量试验,开采打字与印刷出的五个值一时一样有的时候分化,同样偶然候程序符合规律化运行,区别期程序一定崩溃。
更主要的是,那一个值看上去很有规律:

p=1c34abd0
pointer=1c34abd0
正规运维

p=38ac7bda1690
pointer=7bda1690
接下去崩溃

p=a5ef6824
pointer=ffffffffa5ef6824
接下去崩溃

……

观望到一个场景:借使p值是4字节(即高4字节为0)且低4字节的万丈位为0,则二者一样,不然双方一定分裂。而pointer值的高4字节独有两种意况,一种是0×00000000,一种是0xFFFFFFFF,再结合低4字节的第三位,能够观望pointer值的高4字节是由p值的低4字节补全了高4字节形成的。

四、遗漏的头文件

小心到编写翻译时有如下消息输出“警告:初阶化时将整数赋给指针,未作类型调换”。当

初察看该函数,发掘重临的是指针,感觉是编写翻译器的误报,未作管理。重新审视该警告音讯,结合调用处的源文件,发现该文件并未有包涵证明get_pointer函数的头文件。在源文件底部增多#include语句,饱含该头文件后,重新编写翻译,警告消失,一再测验,程序不再崩溃,非常平静。看来难点发出的由来是因为漏包括了头文件,那么为啥不分包也足以编写翻译通过呢?

五、二个回顾的调查

/**********a.c**********/

#include <stdio.h>

int main()

{

void *p = func();

printf(“p=%lxn”, p);

return 0;

}

/**********b.c**********/

void *func()

{

void *p = (void*)0x1234567890ABCDEF;

return p;

}

gcc –c a.c b.c,系统提示:
图片 5
纵然有警告,不过编写翻译却成功了。

我们再来链接一下,gcc –o all a.o b.o,链接也成功了。

运营程序,./all,输出结果是p=FFFFFFFF90ABCDEF,而笔者辈盼望的值是1234567890ABCDEF,难点再次出现了。

充足-Wall选项后再度编译,开掘系统多了一行输出“警告:隐式注解函数 ‘func’”,

翻开了隐式证明的相干材质得知,原本,编译器会将具有隐式评释的函数的归来值类型都承认为int。

如此一来原因就相比较清楚了,在b.c里定义的func函数再次回到的真的是指针类型,而在a.c里认为func再次回到的是int类型,程序运转时会将func再次来到的指针类型值强制转变为int,然后再强制转换为void*,赋给变量p。在六十一人机上,指针为8字节,int为4字节,在由指针调换为int时,高4字节被舍弃,值由0x1234567890ABCDEF变为0x90ABCDEF,然后在由int调换为指针的进度中,依据有标识数的补齐原则,依照int最高位是0照旧1,将高4字节每壹位一体补全为0或1。0x90ABCDEF的最高位是1,所以高4字节每壹个人都补全为1,最终产生了结果0xFFFFFFFF90ABCDEF。

那干什么实际运作时不是每趟都完蛋呢?那是因为被调用的函数所重回的指针是动态分配的,其值事先不定点,假设被开头化的指针地址的高4字节和低4字节的万丈位原本正是0,如0×0000000012345678,那么在将挟持调换为int时放弃高4字节对其就从未有过其余影响了,值照旧0×12345678,然后再由int转为指针,高4字节补0,值为0×0000000012345678,所以程序能够健康运转下去。

六、编写翻译与链接、定义与注脚

在编写翻译阶段,各样源文件独立编写翻译,所以a.c和b.c是分离编写翻译的,a.c里调用了func

函数而从不包涵其声称(声美赞臣(Meadjohnson)般接纳#include “b.h”,也得以使用extern函数原型的样式),编写翻译器会感觉func函数为隐式评释,将其归来值类型定为int。所以编写翻译即便有警告,但却成功了。

编写翻译阶段是无需函数的定义的。大家把b.c里的func函数注释掉,gcc –c a.c b.c同样能够实行成功。

在链接阶段,链接器将全体源文件编写翻译获得的二进制文件以及调用的库链接到四个可实行文件中,此时链接器会去找func函数的切实定义,以供main函数调用。因为func函数确实有定义,所以链接也会马到功成。

链接阶段必得有函数的概念,不然链接器会报错。我们依旧注释掉func,编写翻译后再举办gcc –o all a.o b.o,系统输出如下:
图片 6
在运作阶段,因为a.c在编写翻译时认为func再次来到int类型,所以func的重返值(8字节指针)被截断为4字节的int,然后再进行高4字节的恢弘,最终赋给了main函数里的变量p。在这一遍类型调换中,p的值就有希望与func的再次来到值分裂样了,p实际桃浪经济体改成贰个野指针。

我们换用g 来编写翻译看下效果:

图片 7

总的来讲g 对语法供给更严刻,分裂意隐式评释func函数。

再次回到值有希望因为隐式证明而不适合大家的期待,那么函数的参数呢?我们再来尝试一下。

/**********c.c**********/

extern void func(long);

int main()

{

func(0x1234567890ABCDEF);

return 0;

}

/**********d.c**********/

#include <stdio.h>

void func(int a)

{

printf(“a=%xn”, a);

}

gcc -c c.c d.c -Wall,成功。

gcc -o all c.o d.o -Wall,成功。

运维程序./all,输出a=90abcdef,那显著不是大家想要的结果,但在编写翻译和链接时却并未有别的不当或警示报出。

行使g 编译,无法通过。

倘若大家新建贰个头文件d.h,将func函数的原型在d.h里声称,然后在c.c和d.c里都富含d.h,就足避防止参数或重临值可能的不等同了。

七、总结

  1. 在支付进程中,应该严酷根据先注解后定义、先注解后采用的尺度,一方面保持优异的编码风格,另一方面也能制止过多私房的荒谬;
  2. 从参数不均等导致的难点来看,最佳不用使用extern评释函数,而应该使用含有头文件的款型;
  3. 编写翻译时展开-Wall选项,对于编写翻译进程中输出的每种WAPAJERONING都要细致检查,制止出现各类不敢相信 不恐怕相信的bug;
  4. 在一些场所,使用g 取代gcc可以获得越来越好的安全性。

malloc calloc 和 realloc,callocrealloc

realloc()函数

原型:extern void *realloc(void *mem_address, unsigned int newsize);

语法:指针名=(数据类型*)realloc(要退换内部存款和储蓄器大小的指针名,新的大大小小)。

头文件:#include <stdlib.h> 某个编写翻译器供给#include <alloc.h>,在TC2.0中得以行使alloc.h头文件

作用:先根据newsize钦点的分寸分配空间,将原有数据从头到尾拷贝到新分配的内部存款和储蓄器区域,而后释放原本mem_address所指内部存储器区域,同一时候再次回到新分配的内部存款和储蓄器区域的首地址。即重新分配存款和储蓄器块的地方。

重临值:假诺重新分配成功则赶回指向被分配内存的指针,不然重返空指针NULL。

瞩目:这里原本内部存储器中的数据也许保持不改变的。当内存不再行使时,应选用free()函数将内存块释放。

 

malloc()函数

原型:extern void *malloc(unsigned int num_bytes);

头文件:在TC2.0中能够用malloc.h或 alloc.h (注意:alloc.h 与 malloc.h 的内容是完全一致的),而在Visual C 6.0中得以用malloc.h只怕stdlib.h。

效果:分配长度为num_bytes字节的内部存储器块

重回值:即使分配成功则赶回指向被分配内部存款和储蓄器的指针,不然重返空指针NULL。当内部存款和储蓄器不再使用时,应运用free()函数将内存块释放。

表明:关于该函数的原型,在旧的版本中malloc重返的是char型指针,新的ANSIC标准规定,该函数重回为void型指针,由此须求时要扩充类型转变。

 

calloc()函数

calloc是三个C语言函数

功 能: 在内部存款和储蓄器的动态存款和储蓄区中分红n个长度为size的连接空间,函数再次回到叁个针对分配初阶地址的指针;假使分配不成功,再次来到NULL。

跟malloc的区别:

calloc在动态分配完内部存款和储蓄器后,自动初步化该内部存款和储蓄器空间为零,而malloc不开端化,里边数据是不管三七二十一的排泄物数据。

用 法: void *calloc(unsigned n,unsigned size);

 

头文件:stdlib.h或malloc.h

#include <stdio.h>
#include <stdlib.h>
int main(void)
{    
    int num = 10;    
    int i;     
    long *p = (long *)malloc(num * sizeof(long)); 
    long *p1=(long *)calloc(num,sizeof(long));

    for (i = 0; i < num; i  )
    {
         printf("%dt", p[i]);
    }
    for (i = 0; i < num; i  )
   {
         printf("%dt", p1[i]);
    }

    printf("内存地址: %pn~~~~~~~~n", p);   
    for (i = 0; i < num; i  )  p[i] = i 1;    
    for (i = 0; i < num; i  )   printf("%dt", p[i]); 

    printf("n------------------n");
    num = 4;     
    p = (long *)realloc(p, num*sizeof(long));   
    printf("内存地址: %pn~~~~~~~~n", p); 
    for (i = 0; i < num; i  )   printf("%dt", p[i]); 

    printf("n------------------n");     
    num = 10;      
    p = (long *)realloc(p, num*sizeof(long));   
    printf("内存地址: %pn~~~~~~~~n", p);   
    for (i = 0; i < num; i  )   printf("%dt", p[i]);

    free(p);    
    free(p1); 
    getchar();    
    return 0; 
}

 

运维结果为:

图片 8

转载自:

calloc 和 realloc,callocrealloc realloc()函数 原型:extern void *realloc(void *mem_address, unsigned int newsize); 语法:指针名=(数据类型*)realloc(要改...

  1. 在C语言中, 若是调用的函数未有函数原型, 则其重临值将默以为 int 型.

虚构调用malloc函数时忘记了 #include <stdlib.h>的情况

那儿malloc函数再次来到值将为 int 并非void * (那是C语言的条条框框, 全体未有函数原型的函数重临值都为int), 此时一旦在前后相继中有如下语句

[cpp]
int *p =  malloc(10); 

int *p =  malloc(10);

则编写翻译器会提交通协警告, ``assignment of pointer from integer lacks a cast'' ( 应该是较早版本的编译器, 最新的编写翻译器如gcc中, 要是函数原型未有显式的提交, 则会交到警告"warning: implicit declaration of function ‘malloc’", 所以其实只要忘了 #include <stdlib.h> 编写翻译器就能给警告了), 借使此时是之类语句

[cpp]
int *p = (int *) malloc (10); 

int *p = (int *) malloc (10);
则会将 malloc 的回到值 int 强制调换为指向int类型的指针, 此时编写翻译器不提交``assignment of pointer from integer lacks a cast'' 的警示(实际上将来的编写翻译器会因为malloc函数未有注脚函数原型而付出警告), 但那样的转换有异常的大大概会带来难点.

 

  1. 在C 中, 要是不对malloc函数的重临值举办展示的勒迫类型调换, 则编写翻译会出错.

下边包车型客车言辞

[cpp]
int *p =  malloc(10); 

int *p =  malloc(10);在.c文件中以C语言语法编写翻译不会报错, 能够因而, 但如果改为.cpp文件以C 语法编写翻译则会报错 " error: invalid conversion from ‘void*’ to ‘int*’ ", 因而能够看到C 的语法检查更严苛一点. 不过在C 中更应该用new来分配内部存款和储蓄器, 并非malloc.

 

 

 

于是最棒的措施应该是在C语言中malloc函数不用强制类型转换, 但假诺程序考虑到C 的兼容性的话, 这应该运用强制类型调换, 而在C 程序中应当用new来替代malloc分配内部存款和储蓄器.

 

 

. 在C语言中, 假使调用的函数没有函数原型, 则其重回值将默以为 int 型. 考虑调用malloc函数时忘记了 #include stdlib.h的状态 此时malloc函数再次回到...

编辑:ca88 本文来源:小心C语言的概念与表明,C语言中malloc函数重返值

关键词: 亚洲城ca88