本文涉及:
1. strcpy和strncpy使用心得
2. malloc和realloc使用心得
3. 指针的指针使用
整个源代码main.c:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
void study_strcpy();
void study_strncpy();
void *study_malloc(size_t size);
void study_free(void *resource);
void study_use_mistakes();
void study_strappend();
void study_strappend2();
u_char *strappend(u_char *dest, const u_char *append);
u_char *strappend2(u_char **dest, const u_char *append);
void malloc_memory_by_pointer(u_char *dest, size_t size);
void malloc_memory_by_pointer_pointer(u_char **dest, size_t size);
void study_memory_leak();
void study_memory_leak2();
int main(){
/*
strcpy 和 strncpy学习
study_strcpy();
study_strncpy();
//学习使用中的误区
study_use_mistakes();
*/
/*
realloc学习
study_strappend();
*/
/*
指针的指针学习
study_strappend2();
*/
/*
内存泄露学习
*/
//存在10个字节的内存泄露
//study_memory_leak();
//不再有内存泄露
//study_memory_leak2();
return 0;
}
void
study_memory_leak(){
u_char *dest;
size_t size;
dest = NULL;
size = 10;
malloc_memory_by_pointer(dest, size);
free(dest);
dest = NULL;
//整个方法执行完毕会有10个字节的内存泄露,具体见图
}
void
study_memory_leak2(){
u_char *dest;
size_t size;
dest = NULL;
size = 10;
//传递 &dest
malloc_memory_by_pointer_pointer(&dest, size);
free(dest);
dest = NULL;
}
void
malloc_memory_by_pointer(u_char *dest, size_t size){
dest = (u_char *)study_malloc(size);
}
void
malloc_memory_by_pointer_pointer(u_char **dest, size_t size){
*dest = (u_char *)study_malloc(size);
}
void
study_strappend(){
const u_char *src = "hello, lingyun";
const u_char *append = " , my name is c++";
u_char *dest;
dest = NULL;
dest = strappend(dest, src);
printf("value: %s\n", dest);
printf("after strappend\n");
//dest = strappend(dest, append);
//printf("value: %s\n", dest);
printf("value: %s\n", strappend(dest, append));
free(dest);
dest = NULL;
}
void
study_strappend2(){
const u_char *src = "hello, liyanhua";
const u_char *append = " , your baby 's name is lingyutian";
const u_char *append2 = " , your husband 's name is lingyun.";
u_char *dest;
dest = NULL;
//printf("pointer address: %d\n", &dest);
//可以直接打印
printf("value: %s\n", strappend2(&dest, src));
printf("value: %s\n", strappend2(&dest, append));
//可以赋值后再操作
dest = strappend2(&dest, append2);
printf("value: %s\n", dest);
free(dest);
dest = NULL;
}
void
study_free(void *resource){
if(resource){
free(resource);
resource = NULL;
}
}
void *
study_malloc(size_t size){
void *dest;
dest = malloc(size);
if(dest == NULL){
perror("malloc");
return NULL;
}
printf("malloc %d bytes\n", size);
return dest;
}
void
study_strcpy(){
const u_char *src = "hello, lingyun by using strcpy";
size_t src_len;
u_char *dest;
size_t dest_len;
src_len = strlen(src);
dest_len = src_len;
//这里比原字符串多分配一个byte的空间
dest = (u_char *)study_malloc(dest_len + 1);
if(dest == NULL){
return;
}
strcpy(dest, src);
printf("src: %s\n", src);
printf("dest: %s\n", dest);
study_free(dest);
}
void
study_strncpy(){
const u_char *src = "hello, lingyun by using strncpy";
size_t src_len;
u_char *dest;
size_t dest_len;
size_t malloc_size;
src_len = strlen(src);
dest_len = src_len;
malloc_size = dest_len + 1;
//这里比原字符串多分配一个byte的空间
dest = (u_char *)study_malloc(malloc_size);
if(dest == NULL){
return;
}
strncpy(dest, src, malloc_size);
printf("src: %s\n", src);
printf("dest: %s\n", dest);
study_free(dest);
}
void
study_use_mistakes(){
const u_char *src = "hello, study_use_mistakes";
size_t src_len;
u_char *dest;
size_t dest_len;
size_t malloc_size;
src_len = strlen(src);
dest_len = src_len;
//这里只分配元字符串大小的bytes空间
dest = (u_char *)study_malloc(dest_len);
//strcpy除了copy指定的字符串外,在结束时还会copy终止null字节
//如果在分配空间时,只分配了dest_len长度个字节,通过valgrind检测时strcpy会报一个违法写1个字节操作
//2013-10-31 再次重新理解如下:
//终止null字节没有空间写入,所以会报一个非法写1个字节操作的错误
strcpy(dest, src);
printf("src: %s\n", src);
//由于strcpy没有将终止null字节写入到dest字符串中
//所以这里在打印信息时,通过valgrind检测时,会报一个非法读一个字节操作
//2013-10-31 再次重新理解如下:
//由于只有原字符串长度的空间,导致终止null字节没有成功写入,再读取dest时,自然会报一个非法读1个字节错误
printf("dest: %s\n", dest);
//由于最近遇到另外一种情况,简单描述一下:
//string copy,假设字符串长度为10,分配空间时分配11个字节,但是在copy时,只copy10个字节
//在使用valgrind检测时,会报一个 “Conditional jump or move depends on uninitialised value(s)”错误
//详见下边的补充代码
//上述两个错误虽然不影响程序的正常执行,但是从程序逻辑的严谨性上来说,应该予以避免
study_free(dest);
//心得:
/*
1). 如果是纯粹的字符串复制,建议多分配一个字节,将终止null字符让程序自动加上
2). 如果是多个字符串的拼接,除了最后一个字符串增加终止null字符以外,其他的都不要加
3). 补充:malloc分配时,要多分配一个字节空间,使用strcpy拷贝字节时不需要处理,如果使用strncpy拷贝字符串时,n要设置为strlen + 1
*/
}
u_char *
strappend(u_char *dest, const u_char* append){
size_t append_len;
size_t size;
size_t src_len;
void *dest_new;
size_t dest_new_len;
if(append == NULL){
return NULL;
}
append_len = strlen(append);
if(dest == NULL){
size = append_len + 1;
dest = (u_char *)study_malloc(size);
if(dest == NULL){
return NULL;
}
strcpy(dest, append);
return dest;
}
src_len = strlen(dest);
dest_new_len = src_len + append_len;
size = dest_new_len + 1;
printf("dest address: %d\n", dest);
dest_new = realloc(dest, size);
if(dest_new == NULL){
return NULL;
}
printf("dest_new address: %d\n", dest_new);
if(dest_new != dest){
dest = dest_new;
}else if(dest_new == dest){
printf("dest_new equals dest\n");
}
strcpy(dest+src_len, append);
return dest;
}
u_char *
strappend2(u_char **dest, const u_char *append){
void *dest_ptr;
void *dest_new_ptr;
size_t src_len;
size_t append_len;
size_t dest_new_len;
size_t size;
if(append == NULL){
*dest = NULL;
return NULL;
}
append_len = strlen(append);
dest_ptr = *dest;
if(dest_ptr == NULL){
size = append_len + 1;
dest_ptr = (u_char *)study_malloc(size);
if(dest_ptr == NULL){
*dest = NULL;
return NULL;
}
strcpy(dest_ptr, append);
*dest = dest_ptr;
return dest_ptr;
}
src_len = strlen(dest_ptr);
dest_new_len = src_len + append_len;
size = dest_new_len + 1;
printf("dest address: %d\n", dest_ptr);
dest_new_ptr = realloc(dest_ptr, size);
if(dest_new_ptr == NULL){
return NULL;
}
printf("dest_new address: %d\n", dest_new_ptr);
if(dest_new_ptr != dest_ptr){
dest_ptr = dest_new_ptr;
}else if(dest_new_ptr == dest_ptr){
printf("dest_new equals dest\n");
}
strcpy(dest_ptr+src_len, append);
*dest = dest_ptr;
return dest_ptr;
}
Makefile文件:
test:
gcc -c main.c -g -o main.o
gcc -o main main.o
rm -f *.o
clean:
rm -f *.o main
all:clean test
使用 valgrind检测命令:
valgrind --tool=memcheck --leak-check=full -q --show-possibly-lost=no main
以下是各个函数主要执行结果:
study_use_mistakes()函数执行,valgrind检测结果:
主要体会strcpy 和 strncpy使用前,malloc多分配一个字节用来存储 ‘\0’,避免出现非法写和非法读
心得:strcpy和strncpy使用
在使用strcpy函数时,如果是copy原始字符串到目标,在分配bytes空间时,要多分配一个,用来存储终止null字节 '\0'
否则在strcpy结束时,使用valgrind检测时,会报一个 非法写1个字节错误
同样在使用printf函数打印目标字符串时,会报一个非法读1个字节错误
=======================================================
study_strappend2(),使用valgrind执行结果:(无任何内存泄露)
主要体会realloc和指针的指针的使用
study_strappend(),valgrind中检测的结果:(存在内存泄露和非法free执行)
心得: malloc和realloc使用和指针的指针使用
其中定义了strappend函数用来对字符串进行append,完成字符串的附加功能
注意两个版本的不同 strappend()和strappend2()
定义了strappend2函数,来解决通过参数动态获取到更改指针的值
其中strappend和strappend2返回u_char *类型是为了在printf这样的函数中直接调用函数就能处理
但是需要注意:
strappend实现中,形参使用的是字符指针,如果没有返回值,则在调用端是不可能打印出字符串信息的
==================================================================
study_memory_leak(),存在10个字节的内存泄露:
study_memory_leak2(),没有内存泄露
代码补充部分:
修改为红色标记部分,则代码使用valgrind检测时不会再有错误提示
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LEN 12
const u_char *data = "lingyun test";
size_t len;
u_char *data_str;
u_char data_array[LEN];
int main() {
len = strlen(data);
printf("data_str: %s\n", data_str);
printf("data_array: %s\n", data_array);
data_str = malloc(len);
//malloc时多分配1个字节可以解决
//data_str = malloc(len + 1);
strcpy(data_str, data);
printf("data_str: %s\n", data_str);
free(data_str);
data_str = malloc(len+1);
strncpy(data_str, data, len);
//使用strncpy时,n设置为strlen+1可以解决
//strncpy(data_str, data, len+1);
printf("data_str: %s\n", data_str);
free(data_str);
strcpy(data_array, data);
printf("data_array: %s\n", data_array);
strncpy(data_array, data, len);
printf("data_array: %s\n", data_array);
return 0;
}
valgrind具体报错如下: