C enhance Day(三)

一. 回顾

< 1 > 指针强化部分

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

< 2 > 字符串部分

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

二. const 的使用

在这里插入图片描述
在这里插入图片描述

三. 二级指针

3.1 二级指针做参数输出特性

< 1 > 指针做函数参数,值传递

int getMem(char *p)
{
	p = (char *)malloc(sizeof(char *) * 100);
	if(p == NULL)
	{
		return -1;
	}
	strcpy(p, "abdakg");
	printf("p = %s\n", p);

	return 0;
}

int main(void)
{
	char *p = NULL;
	int ret = 0;

	ret = getMem(p);
	if(ret != 0)
	{
		printf("getMem err: %d\n", ret);
		return ret;
	}
	printf("p = %s\n",p);
	
	return 0;
}

打印结果如下:

在这里插入图片描述
为什么出来就是null呢,因为主函数里的p和形参里的p未构成任何联系,记住如果作为函数参数没有取地址的时候就是平值传递,结构图如下:
在这里插入图片描述
在这里插入图片描述

< 2 > 指针做函数参数,地址传递

int getMem2(char **p)
{
	if(p = NULL)
	{
		return -1;
	}
	char *tmp = (char *)malloc(sizeof(char *) * 100);
	if(p == NULL)
	{
		return -2;
	}
	strcpy(tmp, "abdakg");
	
	//间接赋值是指针存在的最大意义体现在下面
	*p = tmp;

	return 0;
}

int main(void)
{
	char *p = NULL;
	int ret = 0;

	ret = getMem2(&p);
	if(ret != 0)
	{
		printf("getMem err: %d\n", ret);
		return ret;
	}
	printf("p = %s\n",p);
	
	if(p != NULL)
	{
		free(p);
		p = NULL;
	}
	return 0;
}

结构图如下:

在这里插入图片描述

3.2 第一种内存模型:指针数组

< 1 > 指针数组的表示

在这里插入图片描述
结构图如下:
在这里插入图片描述

< 2 > 指针数组的选择排序

int main(void)
{
	char *p[] = {"11111111",
				 "00000000",
				 "bbbbbbbb",
				 "aaaaaaaa"};
	int n = sizeof(p) / sizeof(p[0]);
	int i = 0;
	int j = 0;
	char *tmp = NULL;

	printf("排序前:\n");
	for(i = 0; i < n; i++)
	{
		printf("%s, ",p[i]);
	}
	printf("\n");

	//选择排序法
	for(i = 0; i < n-1; i++)
	{
		for(j = i + 1; j < n; j++)
		{
			if(strcmp(p[i],p[j]) > 0)
			{
				tmp = p[i];
				p[i] = p[j];
				p[j] = tmp;
			}
		}
	}

	printf("\n排序后:\n");
	for(i = 0; i < n; i++)
	{
		printf("%s, ",p[i]);
	}
	printf("\n");

	return 0;
}

< 3 > 指针数组的选择排序函数版

在这里插入图片描述

int sort_array(char **p, int n)
{
	int i,j;
	char * tmp;
	for(i = 0; i < n-1; i++)
	{
		for(j = i + 1; j < n; j++)
		{
			if(strcmp(p[i],p[j]) > 0)
			{
				tmp = p[i];
				p[i] = p[j];
				p[j] = tmp;
			}
		}
	}

	return 0;
}
int main(void)
{
	char *p[] = {"11111111",
				 "00000000",
				 "bbbbbbbb",
				 "aaaaaaaa"};
	int n = sizeof(p) / sizeof(p[0]);

	printf("排序前:\n");
	printf_array(p,n);

    sort_array(p, n);
    
	printf("\n排序后:\n");
	printf_array(p,n);	
	
	return 0;
}

在我自己不看上面的内容敲代码复原上述函数封装的版本时出现了以下问题,封装成函数时,形参的类型写错了,具体这个得好好反思一下,我的写法是:

void print_array(char* a,int n)

需要强化的概念是:传入形参的类型是指针数组,我上面错误的写法传入的是一个char类型的指针变量,同时注意数组作为函数参数的时候退化为指针,以下void print_array(char** a,int n)void print_array(char* a[ ],int n)这两种写法是等价的。

3.3 第二种内存模型:二维数组

< 1 > 二维数组的表示

在这里插入图片描述
打印结果:

在这里插入图片描述
内存模型如下:
在这里插入图片描述

  • 一维数组中,如char a[10];,其中a表示的是一维数组第一个元素的地址,&a表示的是整个一维数组的地址
    在这里插入图片描述
    验证结果如下:
    在这里插入图片描述
  • 二维数组里,如char a[4][30],其中a表示的是第0行整个一维数组的首地址,它跳一次加30个字节
    在这里插入图片描述
    验证结果如下:
    在这里插入图片描述

< 2 > 二维数组的排序

void printf_array(char a[][30], int n)
{
	int i = 0;
	for(i = 0; i < n; i++)
	{
		printf("%s, ",a[i]); //首行地址,和首行首元素的值是一样的
	}
	printf("\n");
}

void sort_array(char a[][30], int n)
{
	int i = 0;
	int j = 0;
	char tmp[30];

	for(i = 0; i < n-1; i++)
	{
		for(j = i+1; j < n; j++)
		{
			if(strcmp(a[i],a[j]) > 0)
			{
				// 交换的是内存块
				strcpy(tmp, a[i]);
				strcpy(a[i], a[j]);
				strcpy(a[j], tmp);
			}
		}
	}
}
int main(void)
{
	char a[][30] = {"2222222222",
					"1111111111",
					"bbbbbbbbbb",
					"aaaaaaaaaa"};
	int n = sizeof(a) / sizeof(a[0]);

	printf("before sort:\n");
	printf_array(a, n);
	
	sort_array(a, n);

	printf("after sort:\n");
	printf_array(a, n);

	return 0;
}

以下为错误的二维数组传参方式,此处当做指针传入进去的话是4个字节,而二维数组里的一维数组大小是30个字节,一次加不够,所以这种方式会报错:

在这里插入图片描述

3.4 第三种内存模型:动态打造堆区的二维空间

< 1 > 基本使用

首先搞清白以下三种动态分配内存

	char* p0 = NULL;
	p0 = (char *)malloc(100);
	strcpy(p0, "asasfafa");

	//10个char *,每一个的值都是空
	//注意这里的10个char *指针变量是在栈区的,它的每一个元素的指向是在堆区开辟的
	int i = 0;
	char* p[10] = { 0 };
	for (i = 0; i < 10; i++)
	{
		p[i] = malloc(100);
		strcpy(p[i], "asdsf");
	}
	  
	int a[10];
	int* q = (int*)malloc(10 * sizeof(int)); //q[10]

然后对比第三种分配内存的方式,理解下面这种在堆区分配二维的动态内存:

	// 动态分配一个数组,每个元素都是char *
	// 注意这里分配了一个数组,含有三个元素,这里的数组都是在堆区开辟的,其中开辟都每个元素又单独指向一个30个字节的空间
	// char *ch[10]
	int n = 3;
	char** buf = (char**)malloc(n * sizeof(char*)); //char *buf[3]
	if (buf == NULL)
	{
		return -1;
	}
	for (i = 0; i < n; i++)
	{
		buf[i] = (char*)malloc(30 * sizeof(char));
		char str[30];
		sprintf(str, "test%d%d", i, i);
		strcpy(buf[i], str);
	}

	for (i = 0; i < n; i++)
	{
		printf("%s ", buf[i]);
	}

	//释放,先释放里面的,再释放外面的
	for (i = 0; i < n; i++)
	{
		free(buf[i]);
		buf[i] = NULL;
	}

	if (buf != NULL)
	{
		free(buf);
	}

结构图如下:

在这里插入图片描述

< 2 > 函数封装版

char** getMem(int n)
{
	int i = 0;
	char** buf = (char**)malloc(n * sizeof(char*)); //char *buf[3]
	if (buf == NULL)
	{
		return NULL;
	}
	for (i = 0; i < n; i++)
	{
		buf[i] = (char*)malloc(30 * sizeof(char));
		char str[30];
		sprintf(str, "test%d%d", i, i);
		strcpy(buf[i], str);
	}

	return buf;
}

void printf_buf(char** buf,int n) //普通的值传递
{
	int i = 0;
	for (i = 0; i < n; i++)
	{
		printf("%s, ", buf[i]);
	}
	printf("\n");
}

void free_buf(char **buf, int n)
{
	int i = 0;
	//释放,先释放里面的,再释放外面的
	for (i = 0; i < n; i++)
	{
		free(buf[i]);
		buf[i] = NULL;
	}

	if (buf != NULL)
	{
		free(buf);
		buf = NULL;
	}
}

int main(void)
{
	char** buf = NULL;
	int n = 3;

	buf = getMem(n);
	if (buf == NULL)
	{
		printf("getMem err\n");
		return -1;
	}

	printf_buf(buf,n);
	free_buf(buf, n);
	buf = NULL;

	return 0;
}

未加入释放环节的结构图如下:

在这里插入图片描述
加入释放函数部分的结构图:

在这里插入图片描述

四. 作业

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

发布了22 篇原创文章 · 获赞 5 · 访问量 466

猜你喜欢

转载自blog.csdn.net/RedValkyrie/article/details/105326789
今日推荐