天天看点

strcpy, strncpy, malloc, realloc以及memory leak等注意问题

本文涉及:

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, malloc, realloc以及memory leak等注意问题

心得:strcpy和strncpy使用

在使用strcpy函数时,如果是copy原始字符串到目标,在分配bytes空间时,要多分配一个,用来存储终止null字节 '\0'

否则在strcpy结束时,使用valgrind检测时,会报一个 非法写1个字节错误

同样在使用printf函数打印目标字符串时,会报一个非法读1个字节错误

=======================================================

study_strappend2(),使用valgrind执行结果:(无任何内存泄露)

主要体会realloc和指针的指针的使用

strcpy, strncpy, malloc, realloc以及memory leak等注意问题

study_strappend(),valgrind中检测的结果:(存在内存泄露和非法free执行)

strcpy, strncpy, malloc, realloc以及memory leak等注意问题

心得: malloc和realloc使用和指针的指针使用

其中定义了strappend函数用来对字符串进行append,完成字符串的附加功能

注意两个版本的不同 strappend()和strappend2()

定义了strappend2函数,来解决通过参数动态获取到更改指针的值

其中strappend和strappend2返回u_char *类型是为了在printf这样的函数中直接调用函数就能处理

但是需要注意:

strappend实现中,形参使用的是字符指针,如果没有返回值,则在调用端是不可能打印出字符串信息的

==================================================================

study_memory_leak(),存在10个字节的内存泄露:

strcpy, strncpy, malloc, realloc以及memory leak等注意问题

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具体报错如下:

strcpy, strncpy, malloc, realloc以及memory leak等注意问题