十四. 动态分配

● C语言实现动态数组

C语言实现动态数组,克服静态数组大小固定的缺陷

C语言中,数组长度必须在创建数组时指定,并且只能是一个常数,不能是变量。一旦定义了一个数组,系统将为它分配一个固定大小的空间,以后不能改变,称为静态数组。但在编程过程中,有时我们所需的内存空间无法预先确定,对于这个问题,用静态数组的办法很难解决。

动态数组是相对于静态数组而言。静态数组的长度是预先定义好的,在整个程序中,一旦给定大小后就无法改变。而动态数组则不然,它可以随程序需要而重新指定大小。动态数组的内存空间是从堆(heap)上分配(即动态分配)的。是通过执行代码而为其分配存储空间。当程序执行到这些语句时,才为其分配。程序员自己负责释放内存。

#include <stdio.h>

#include <stdlib.h>

int main(){

int arrLen; // 数组长度

int *array; // 数组指针

int i; // 数组下标

printf("输入数组长度:");

scanf("%d", &arrLen);

// 动态分配内存空间,如果失败就退出程序

array = (int*)malloc( arrLen*sizeof(int) );

if(!array){

printf("创建数组失败!\n");

exit(1);

}

// 向内存中写入数据

for(i=0; i<arrLen; i++){

array[i] = i+1;

}

// 循环输出数组元素

for(i=0; i<arrLen; i++){

printf("%d ", array[i]);

}

printf("\n");

free(array);

system("pause");

return 0;

}

1 2 3 4 5 6 7 8 9 10

注意: 静态数组的长度不能改变, 但是它的数组元素的值可以改变,

如果要删除某个数组元素就比较复杂了, 可在学了数据结构后解决

● C语言的动态内存分配(堆管理)

Dynamic memory allocation in C----C语言的动态内存分配(堆管理)

malloc(), calloc()和realloc()这三个函数用来在堆(heap)中分配内存空间, free()函数用来释放已经分配好的堆空间; 使用上述函数时要包含头文件<stdlib.h>

malloc () function is used to allocate space in memory during the execution of the program. It does not initialize the memory allocated during execution, e.g., it carries garbage value. malloc () function returns null pointer if it couldn't be able to allocate requested amount of memory.

calloc () function is also like malloc () function. But calloc () initializes the allocated memory to zero. But, malloc() doesn't (It carries garbage value).

realloc () function modifies the allocated memory size by malloc () and calloc () functions to new size.

※ 上述三个函数返回值是一个指针, 指向一段可用内存的首地址

free () function frees the allocated memory by malloc (), calloc (), realloc () functions and returns the memory to the system.

※ free()函数无返回值

malloc ()

malloc (number *sizeof(int));

calloc ()

calloc (number, sizeof(int));

/* allocate memory for an array of 50 integers */

int *numbers;

numbers = (int *) malloc(50 * sizeof(int));

/* allocate memory for a 100-character string */

char *str;

str = (char *) malloc(100);

//为避免出错, 最好写成: str = (char *) malloc(100 * sizeof(char));

long * newmem;

newmem = (long *)calloc(100, sizeof (long));

realloc ()

realloc (pointer_name, number * sizeof(int));

free ()

free (pointer_name);

char *p;

p = malloc(20 * sizeof(char));    //没有强转malloc()返回的void*型的值

...

p = realloc(p, 100 * sizeof(char));

//也可以利用realloc()和已经分配好的内存空间再分配一个新的空间, 如:

pp = realloc(p, 100 * sizeof(char));

//p和pp两个指针不同

free(umbers) //numbers是已经分配的一个内存空间的首地址

在C里,用malloc()等函数动态分配内存时,是不推荐强转其返回值的。例如写成:

int *arr = malloc(sizeof(int) * 5);

而不是:

int *arr = (int *)malloc(sizeof(int) * 5);

因为void *到其它类型的指针是隐式转换的 (C++要强转,不然会报错), 而malloc(), calloc(), realloc()的返回值类型是void *, 所以不推荐强转其返回值, 但也不会出错.

不过大多参考书都有强制转换, 而且在VC++ 6.0这种比较老的编译器上, 不强转的话, 编译会出错

Difference between malloc() and calloc() functions in C:

malloc()

calloc()

It allocates only single block of requested memory

It allocates multiple blocks of requested memory

int *ptr; ptr = malloc( 20 * sizeof(int) );

For the above, 20*4 bytes of memory only allocated in one block.

Total = 80 bytes

int *ptr; ptr = calloc( 20, 20 * sizeof(int) );

For the above, 20 blocks of memory will be created and each contains 20*4 bytes of memory.

Total = 1600 bytes

malloc () doesn't initializes the allocated memory. It contains garbage values

calloc () initializes the allocated memory to zero

type cast must be done since this function returns void pointer

int *ptr; ptr = (int*)malloc(sizeof(int)*20 );

Same as malloc () function int *ptr; ptr = (int*)calloc( 20, 20 * sizeof(int) );

Difference between static memory allocation and dynamic memory allocation in C

Static memory allocation

Dynamic memory allocation

In static memory allocation, memory is allocated while writing the C program. Actually, user requested memory will be allocated at compile time.

In dynamic memory allocation, memory is allocated while executing the program. User requested memory will be allocated at run time.

Memory size can't be modified while execution.

Example: array

Memory size can be modified while execution.

Example: Linked list

△reallocNULL,

malloc()和free()

#include<stdlib.h>

main()

{

int *numbers;

numbers = (int *) malloc(50 * sizeof(int));

printf("%d\n",(int *)malloc(50 * sizeof(int)));

//为什么这两句的结果不一样

printf("%d\n",numbers);    //number是一个指针, 存储了被分配空间(一段内存单元: a series of data units)的首地址

printf("%d\n",numbers+2);    //结果是number存储的地址加两个sizeof(int)

*numbers=10;

*(numbers+2)=20;

printf("%d\n",*numbers);

printf("%d\n",*(numbers+2));

free(numbers);

//释放完块内存之后,没有把指针置 NULL,这个指针就成为了"野指针",也有书叫"悬垂指针"

//这是很危险的,而且也是经常出错的地方, 所以一定要记住一条:free 完之后,一定要给指针置 NULL, 即:

numbers = NULL;

}

calloc()和realloc()

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

int main()

{

个字节的空间; 另外, 因为不知道是将一个字符串常量还是若干字符赋给char *, 所以不要用const修饰

strcpy(p, "abcdef123");

printf("p = %s\n", p);

const char *p1 = "hello world";

是用于合并后的字符串末尾的'\0'

char *p2 = (char *)realloc(p, 1024 * 100);

strcat(p2, p1);

printf("p2 = %s\n", p2);

printf("%d\n",p);

printf("%d\n",p2);

if (p == p2)

{

printf("realloc没有改变指针变量p指向的变量的地址\n");

}

else

{

printf("改变了\n");

}

free(p);

return 0;

}

使用calloc函数分配数组内存

#include <stdlib.h>

#include<stdio.h>

int main()

{

int* array;        /*定义指针*/

int i;                /*循环控制变量*/

array=(int*)calloc(3,sizeof(int));    /*数组内存*/

for(i=1;i<4;i++)    /*使用循环对数组进行赋值*/

{

*array=10*i;    /*赋值*/

printf("NO%d is: %d\n",i,*array);    /*显示结果*/

array+=1;        /*移动指针到数组到下一个元素*/

}

return 0;

}

个英文字母

/* Demonstrates the use of malloc() to allocate storage */

/* space for string data. */

#include <stdio.h>

#include <stdlib.h>

char count, *ptr, *p;

int main( void )

{

/* Allocate a block of 35 bytes. Test for success. */

/* The exit() library function terminates the program. */

ptr = (char *)malloc(35 * sizeof(char));

if (ptr == NULL)

{

puts("Memory allocation error.");

return 1;

}

/* Fill the string with values 65 through 90, */

/* which are the ASCII codes for A-Z. */

/* p is a pointer used to step through the string. */

/* You want ptr to remain pointed at the start */

/* of the string. */

p = ptr;

for (count = 65; count < 91 ; count++)

*p++ = count;

/* Add the terminating null character. */

*p = '\0';

/* Display the string on the screen. */

puts(ptr);

free(ptr);

return 0;

}

● 内存泄露memory leakage (内存丢失)

内存泄露memory leakage (内存丢失)

● 关键字new和delete

关键字new和delete

※ new和malloc一样, 如果申请内存空间成功, 则返回被分配内存的首地址给定义的指针; 否则(比如没有足够的内存空间), 则返回0, 即空指针.

new的用法:

:指针变量名=new 类型标识符;

:指针变量名=new 类型标识符(初始值);

:指针变量名=new 类型标识符[内存单元个数];

delete的用法:

1. 删除单变量地址空间

int *a = new int;

delete a; //释放单个int的空间

2. 删除数组空间

int *a = new int[5];

delete []a; //释放int数组空间

:指针变量名=new 类型标识符:

#include <iostream.h>

void main()

{

int *pa;

pa=new int;    //申请一个int大小的空间

*pa = 100;

cout<<"*pa= "<<*pa <<endl; //100

delete pa;

pa = NULL;    //delete只是删除动态内存单元, 并不会将指针pa本身删除(△那该怎么删除呢?); 根据前人经验, 为避免引入bug, 最好将pa赋值为NULL, 这样pa就不是野指针了

:指针变量名=new 类型标识符(初始值);

//因此上述代码等同于:

/*    int *pa;

pa=new int(100);    //申请一个int大小的空间

cout<<"*pa= "<<*pa <<endl; //100

delete pa;

pa = NULL; */

//_____________________________________________

:指针变量名=new 类型标识符 [内存单元个数];

int *pb;

pb=new int[5];                                //申请一个数组空间

for(int i=1;i<5;i++)

{

*(pb+i)=i;

}

cout<<"*(pb+2)= "<<*(pb+2)<<endl;//2

delete[] pb;    //也可以是delete[5] pb;

pb = NULL;

//_____________________________________________

int (*pc)[3];    //定义了一个指针数组

int j;

pc=new int[2][3];                            //申请一个二维数组空间

for (i=0;i<2;i++)

for(j=0;j<3;j++)

*(pc[i]+j)=1;

cout<<"*(pc[1]+1)= "<<*(pc[1]+1)<<endl;//1

delete[] pc;    //也可以是delete[3] pc; 不可以是delete[2]p[3]

pc = NULL;

}

● malloc()/free()与new/delete的区别

malloc/free是C/C++语言的标准库函数,new/delete是C++的运算符

● 缓冲区操纵管理(Buffer manipulation function)

常用函数是: memset() ,memcpy() ,memmove(), 使用它们必须在源文件声明区包含头文件string.h, 函数原型分别是:

void * memset ( void * ptr, int value, size_t num );

void * memcpy ( void * destination, const void * source, size_t num );

● 缓冲区,缓冲器:用于临时存储被写入到文件和从文件读取的数据,并以与接收时不同的速度传输数据. 缓冲区根据其对应的是输入设备还是输出设备,分为输入缓冲区和输出缓冲区。

● 用malloc()等函数分配的堆空间就是缓冲区; 声明一个字符数组, 也相当于开辟了一个缓冲区

● 缓冲区位于内存, 但不确定是在堆区还是在栈区, 例如:

//下面分配了一个栈内的缓冲区

void func()

{

char buffer[1024];

}

//下面分配了一个堆内的缓冲区

//下面局部(指针)变量buffer存储在栈上, 由于它是局部变量, 自然存储在栈区; 但这个变量是一个指向分配在堆区的一块内存空间的指针

void func()

{

char *buffer = malloc(1024);

}

memset()        将内存的前n个字节设置为特定的值

memset()函数原型

void * memset ( void * ptr, int value, size_t num );

在以ptr为起始地址、长度为num个字节的内存区间内, 把每个字节的值都设值成value。

ptr--要填充的内存块的指针(Pointer to the block of memory to fill)。

value--为要设置的值。参数 value 虽声明为 int,但必须是 unsigned char,所以范围在0 到255 之间。你既可以向 value 传递 int 类型的值,也可以传递 char 类型的值,int 和 char 可以根据 ASCII 码相互转换。

num --为 ptr 的前 num 个字节,size_t 就是unsigned integral type。

返回值--ptr is returned.

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

int main()

{

char str[] = "http://c.biancheng.net";

//字符数组是可以被修改的,字符串是只读的,不能被修改,而 memset() 又必须修改 str,所以不能将 char str[] = "http://c.biancheng.net"; 声明为 char *str = "http://c.biancheng.net";,否则运行时会报错。

memset(str, '-', 7);

puts(str);

system("pause");    //包含在头文件stdlib.h中, 作用是在命令行上输出"Press any key to exit",等待用户按一个键,然后退出

return EXIT_SUCCESS; //使用这一返回语句需要加头文件stdlib.h, 和return 0一样表示程序无错误退出

}

memcpy()    复制内存内容 & strcpy()

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#define N (10)

int main()

{

char* p1 = "hel\0lo";

char* p2 = (char*)malloc(sizeof(char) * N);

char* p3 = (char*)memcpy(p2, p1, N);

printf("p2 = %s\np3 = %s\n", p2, p3);

char str1[]="hel\0lo";

char str2[40];

strcpy (str2,str1);

printf("str2=%s\n",str2);

free(p2);

p2 = NULL;

p3 = NULL;

system("pause");

return 0;

}

内存重叠出现的情况

char *p = NULL;        //也可以先定义一个野指针, 然后在末尾加p=NULL, 这样p就不是一个野指针了

p = (char *)malloc(10);

memcpy(p,"1234679",strlen("1246789"));

printf("before p = %s/n", p);

strcpy(p+1,p);//这里重叠了; 这个时候如果使用strcpy函数则程序会崩溃; 使用memcpy的话程序会等到错误的结果; 原因就是memcpy(),strcpy()这两个函数没有对内存重叠进行处理

printf("after p = %s/n", p);

free(p);

memmove()    复制内存内容(可以处理重叠的内存块)

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

int main ()

{

char str[] = "memmove can be very useful......";

个字节"very useful, 目的地是str后的第20*sizeof(char)个字节

puts (str);

system("pause");

return 0;

}