映月读书网 > C语言解惑 > 18.3 动态内存 >

18.3 动态内存

数组、指针和动态内存也是密切相关的。容易出现的错误仍然是边界和初始化问题。

18.3.1 非数组的指针

【例18.8】下面程序将数组t和s中的内容赋给指针变量p,但输出结果并没有包括s的全部内容。找出错误之处并改正之。


#include <stdio.h>
#include <string.h>
int main 
()
{
     int i=0
,j=0
;
     char t="abcdefghij"
,s="klmnopqrstuvwxyz"
,*p
;
     p=t
;
     i=strlen
(t
);
     while 
(( p[i+j] = s[j]
) 
!='\0' 
)
           j++
;
     printf
("%s\n"
,p
);
     return 0
;
}
  

原因是用数组t初始化指针的想法是想利用超出t的存储空间来存储s,这是危险的做法。越界之后,并不能保证有连续的有效存储空间用以存储字符串s。

可以另外定义一个大于s和t总长度的字符数组。例如


char st[30]
;
p=st
;
  

然后使用如下两个循环完成赋值:


while 
(( p[i] = t[i]
) 
!='\0' 
)
           i++
;
while 
(( p[i+j] = s[j]
) 
!='\0' 
)
          j++
;
p[i+j]='\0'
;
  

一般采用申请动态内存的方法,即为指针变量申请足够的存储空间。


p=
(char*
)malloc 
( strlen
(t
)+strlen
(t
)+1
)
  

strlen函数计算的是实际字符串长度,所以要增加一个结束位。实际使用时,需要判别申请是否成功。这块内存虽然是非数组的指针,但却可以像数组那样使用下标。程序中演示了两种反序输出的方法,特别是演示下标为负值的使用方法,以便更好地理解动态内存的特点及指针的灵活使用方法。


//
完整的程序
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main 
()
{
       int i=0
,j=0
;
       char t="abcdefghij"
,s="klmnopqrstuvwxyz"
,*p
;
       if 
( 
(p=
(char*
)malloc 
( strlen
(t
)+strlen
(t
)+8
)) == NULL 
)  {
             printf 
( "
内存分配错误!\n" 
);
             exit
(1
);
       }
       while 
(( p[i] = t[i]
) 
!='\0' 
)
           i++
;
       while 
(( p[i+j] = s[j]
) 
!='\0' 
)
           j++
;
       p[i+j]='\0'
;
       printf
("%s\n"
,p
);
       for
(i=25
; i>-1
; i--
)
             printf
("%c"
,p[i]
);
       printf
("\n"
);
       p=p+25
;
       for
(i=0
; i>-26
; i--
)
             printf
("%c"
,p[i]
);
       printf
("\n"
);
       p=p-25
;
         free
(p
);
       return 0
;
}
  

程序输出结果如下:


abcdefghijklmnopqrstuvwxyz
zyxwvutsrqponmlkjihgfedcba
zyxwvutsrqponmlkjihgfedcba
  

释放内存,必须保证指针指向申请的动态内存的开始位置,否则会出错。所以程序中执行“p=p-25;”。申请内存时多申请了6个,是为了保证free可靠执行。

数值数组的使用方法与此类似,不再赘述。

18.3.2 NULL指针

在语句


if 
( 
(p=
(char*
)malloc 
( strlen
(t
) + strlen
(t
) + 8
)) == NULL 
)
  

中使用了空指针。空指针的表示为:


p=NULL
;
  

有时在赋值或比较运算的情况下会使用NULL指针,但在其他情况不能使用NULL指针。因为NULL指针并不指向任何对象,而且空指针也不是空字符串,所以对空指针p而言,使用如下两个语句会得到什么结果呢?


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

为了代码的文档化,常采取如下定义:


#define NULL 0
  

由此可见,p的行为没有定义,这两条语句在不同的机器上可能有不同的效果。

在禁止读取内存0地址的机器上,语句


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

将会执行失败。在允许的机器上,则会以十进制方式输出内存位置0中存放的字符内容。

要注意的是,空指针并不是空字符串。无论使用0还是NULL,效果都是相同的。当将0赋值给一个指针变量时,绝对不能企图使用该指针所指向的内存中存储的内容。

有些C语言实现对内存位置0只允许读,不允许写。在这种情况下,NULL指针指向的也是垃圾信息,所以也不能错用NULL指针。

所以,对指针进行递增和递减操作必须预防越界。在达到最后一个边界时,要特别小心谨慎。释放不用的内存时,必须保证指针指向所申请内存的首地址,否则就会出错。在某些场合,为了保证释放,甚至需要多申请部分内存区域。