C语言核心知识点Day04

1.一维数组

元素类型角度:数组是相同类型的变量的有序集合
内存角度:连续的一大片内存空间
1.1 数组名
	在数组名作为函数参数传递的时候,这个时候数组名会退化为指针,
表示指向数组第一个元素的地址,因此,在使用数组名作为函数参数的时候
必须要有另外一个参数传递进来,那就是数组的长度。

	在绝大多数的情况下,数组名的值是一个指针常量,也就是数组第一个
元素的地址。它的类型取决于数组元素的类型:如果他们是int类型,那么
数组名的类型就是“指向int的常量指针”;如果它们是其他类型,那么数组
名的类型也就是“指向其他类型的常量指针”。同时,数组名是一个常量指
针,指针的指向是不可以被修改的。
	而在以下两种情况下,数组名不会指向首地址
		1.sizeof 
		2.对数组名取地址&arr
	此时数组名表示的是数组这个整体,即表示为数组指针类型。
	
1.2数组和指针
	指针和数组并不是相等的,二者在一些场合下代表的意义不同。同样
的,数组指针类型和数组首元素指针类型是不同的:
	1.数组指针类型表示的是指向数组全体的指针
	2.数组首元素指针表示的是指向数组首元素的指针
	
	定义一个数组指针的方法,与上述的两种情况有关
		1.我们先定义数组类型,再定义数组指针类型
		2.直接定义数组指针类型
		3.直接定义数组指针变量
void test02()
{
	int arr[] = { 1,2,3,4,5 };
	
	//1.我们先定义数组类型,再定义数组指针类型
	typedef int(ARRAY_TYPE)[5];

	ARRAY_TYPE myarray;// 等价于int myarray[5];
	for (int i = 0; i < 5; ++i)
	{
		myarray[i] = i;
	}
	for (int i = 0; i < 5; ++i)
	{
		printf("%d\n", myarray[i]);
	}
	//对数组名取地址代表指向整个数组的指针
	ARRAY_TYPE* pArray = &myarray;
	pArray = &arr;

	//	1.*pArray 表示拿到pArray指针指向的整个数组
	//	2.*pArray 此时表示的就是数组名,指向数组的首元素地址。

	//*pArray此时是指向数组首元素的指针,加一后表示指向数组第二个元素的指针,解引用后表示第二个元素的值
	printf("%d\n", *(*pArray + 1));


	//2.直接定义数组指针类型
	typedef int(*ARRAY_POINTER)[5];
	ARRAY_POINTER pArr = &arr;

	//3.直接定义数组指针变量
	int(*pArrParam)[5] = &arr;
}

2.二维数组

如果某个数组的维数不止1个,它就被称为多维数组。

2.1 数组名、定义方式
	一维数组名的值是一个指针常量,它的类型是“指向元素类型的指针”,它
指向数组的第1个元素。多维数组也是同理,多维数组的数组名也是指向第
一个元素,只不过第一个元素是一个数组。例如:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

void pritnTwoArray(int(*pArr)[3],int len1,int len2)
{
	for (int i = 0; i < len1; ++i)
	{
		for (int j = 0; j < len2; ++j)
		{
			printf("%d ", *(*(pArr + i) + j));
		}
		printf("\n");
	}
}

void test01()
{
	//定义二维数组的三种方式
	int arr[3][3] = {
		{1,2,3},
		{4,5,6},
		{7,8,9}
	};
	//int arr[3][3] = { 1,2,3,4,5,6,7,8,9 };
	//int arr[][3] = { 1,2,3,4,5,6,7,8,9 };
	
	//二维数组同一维数组一样,除了sizeof(),或者对数组名取地址外,其余情况下都是指向数组首元素的指针
	int(*pArray)[3] = arr;//数组名就是指向首元素的指针,在二维数组里,数组名就是指向首个一维数组的指针

}

int main()
{
	test01();
	return 0;
}
2.2二维数组与选择排序
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

void selectSort(char** arr,int len)//选择排序
{
	for (int i = 0; i < len; ++i)
	{
		int min = i;//用于存储下标
		for (int j = i + 1; j < len; ++j)
		{
			if (strcmp(arr[min],arr[j])>0)
				min = j;
		}
		//交换
		if (min != i)
		{
			char* temp = arr[min];
			arr[min] = arr[i];
			arr[i] = temp;
		}
	}
}

void printArray(char** arr, int len)
{
	for (int i = 0; i < len; ++i)
	{
		printf("%s ", arr[i]);
	}
	printf("\n");
}

void test()
{
	char* arr[] = { "bbb","fff","ccc","ddd","zzz","hhh","mmm" };
	int len = sizeof(arr) / sizeof(char*);
	printArray(arr,len);
	selectSort(arr, len);//排序后
	printArray(arr, len);

}

int main()
{
	test();
	return 0;
}
2.3 柔性数组
	1.定义
	柔性数组既数组大小待定的数组, C语言中结构体的最后一个元素可以
是大小未知的数组,也就是所谓的0长度,所以我们可以用结构体来创建柔
性数组。
	2.柔性数组有什么用途 ?	
	它的主要用途是为了满足需要变长度的结构体,为了解决使用数组时
内存的冗余和数组的越界问题。
	3.用法 :在一个结构体的最后 ,申明一个长度为空的数组,就可以使
得这个结构体是可变长的。对于编译器来说,此时长度为0的数组并不占
用空间.本身不占空间,它只是一个偏移量,数组名这个符号本身代表了一
个不可修改的地址常量 .
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct _Info {
	char name[10];
	int age;
}Info;

typedef struct Group {
	int num;
	int counter;
	Info staff[];//组员的数组
}Group;

int main()
{
	int Len = 10;
	int Size = sizeof(Group) + Len * sizeof(Info);
	
	Group* org = (Group*)malloc(Size);//开辟空间存放数组
	memset(org, 0, Size);

	org->num = Len;
	for (int i = 0; i < org->num; i++) {//赋值操作
		org->counter++;
		org->staff[i].age = i + 1;
		strcpy(org->staff[i].name, "zhangsan");
	}
	//打印操作
	for (int j = 0; j < org->counter; j++)
		printf("name is %s, age is %d\n", org->staff[j].name, org->staff[j].age);
	//释放空间
	if (org != NULL)
	{
		free(org);
		org = NULL;

	}
	return 0;
}

3数组总结

3.1 编程提示
	源代码的可读性几乎总是比程序的运行时效率更为重要
	只要有可能,函数的指针形参都应该声明为const
	在多维数组的初始值列表中使用完整的多层花括号提供可读性
3.2 内容总结
	在绝大多数表达式中,数组名的值是指向数组第1个元素的指针。这个
规则只有两个例外,sizeof和对数组名&。
	指针和数组并不相等。当我们声明一个数组的时候,同时也分配了内
存。但是声明指针的时候,只分配容纳指针本身的空间。
	当数组名作为函数参数时,实际传递给函数的是一个指向数组第1个元
素的指针。
	我们不单可以创建指向普通变量的指针,也可创建指向数组的指针。

4.结构体

	注:定义结构体类型时不要直接给成员赋值,结构体只是一个类型,
编译器还没有为其分配空间,只有根据其类型定义变量时,才分配空间,
有空间后才能赋值。
4.1 结构体数组和结构体赋值
	在开辟了空间后,进行直接赋值操作的时候,会出现两个问题:
	1.首先,第一个问题是因为指针都指向同一片内存,因此可能会导致
之后释放内存时,重复释放
	2.其次,第二个问题是因为两个指针都指向同一片内存,因此,之前
存储数据的一片空间的地址就会丢失,从而导致内存泄漏。
	因此,需要自己手动操作赋值操作。
	
	也就是深浅拷贝的问题:
	1.浅拷贝只复制指针,也就是说只改变指针的指向,因此会导致多个
指针指向同一片内存空间,这样做的后果就是在之后释放空间或者是空间
销毁的时候,同一片空间连续的销毁,导致程序down掉。
	2.深拷贝:则会独立的开辟空间存储数据,使得指针指向为这片空间,
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

struct Person
{
	char name[64];
	int age;

};

void test01()
{
	struct Person p1 = { "aaa",20 };
	struct Person p2 = { "bbb",30 };

	//赋值操作
	//p1 = p2;

	printf("Person->p1.name = %s,p1.age = %d\n", p1.name, p1.age);
	printf("Person->p2.name = %s,p2.age = %d\n", p2.name, p2.age);
}

struct Teacher
{
	char* name;
	int age;
};

void test02()
{
	struct Teacher t1;
	t1.name = malloc(sizeof(char) * 64);
	memset(t1.name, 0, 64);
	strcpy(t1.name, "aaa");
	t1.age = 20;

	struct Teacher t2;
	t2.name = malloc(sizeof(char) * 128);
	memset(t2.name, 0, 128);
	strcpy(t2.name, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
	t2.age = 30;

	printf("Name:%s Age:%d\n", t1.name, t1.age);
	printf("Name:%s Age:%d\n", t2.name, t2.age);

	//赋值操作
	//t1 = t2;//直接赋值操作的时候,会出现两个问题
	//首先,第一个问题是因为指针都指向同一片内存,因此可能会导致之后释放内存时,重复释放
	//其次,第二个问题是因为两个指针都指向同一片内存,因此,之前存储数据的一片空间的地址就会丢失,从而导致内存泄漏。
	//因此,需要自己手动操作赋值操作
	
	if (t1.name != NULL)
	{
		free(t1.name);
		t1.name = NULL;
	}
	
	t1.name =(char* )malloc(sizeof(char)*128);
	memset(t1.name, 0, sizeof(t2.name) + 1);
	strcpy(t1.name, t2.name);
	t1.age = t2.age;

	
	printf("------------------------------\n");
	printf("Name:%s Age:%d\n", t1.name, t1.age);
	printf("Name:%s Age:%d\n", t2.name, t2.age);

	//释放内存
	if (t1.name != NULL)
	{
		free(t1.name);
		t1.name = NULL;
	}

	if (t2.name != NULL)
	{
		free(t2.name);
		t2.name = NULL;
	}
}

int main()
{
	//test01();
	test02();
	return 0;
}
4.2 结构体嵌套指针
4.2.1 结构体嵌套一级指针
	当结构体中嵌套有指针的时候,因为在开始内存分配的时候,仅仅为
指针变量开辟的空间用于存储自身大小,因此,当向结构体中嵌套的指针
进行赋值等操作的时候,就会导致程序出错,因此,我们因通过malloc等
函数,在堆区中开辟相应的内存空间,用于存储指针中的数据。
	案例:为一个结构体指针数组开辟空间
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

struct Person
{
	char* name;
	int age;
};

struct Person** openSpace(struct Person** p1)//开辟空间以及赋值操作
{
	p1 = (struct Person**)malloc(sizeof(struct Person*) * 3);
	memset(p1, 0, 3);
	for (int i = 0; i < 3; i++)
	{
		p1[i]= malloc(sizeof(struct Person));
		p1[i]->name = malloc(sizeof(char) * 64);
		sprintf(p1[i]->name, "Name:name_%d ", i + 1);
		p1[i]->age = 20 + i;
	}
	return p1;
}
//打印
void printPerson(struct Person** p1)
{
	for (int i = 0; i < 3; i++)
	{
		printf("Name:%s,Age:%d\n", p1[i]->name, p1[i]->age);
	}
}
//释放堆空间
void freeSpace(struct Person** p1)
{
	if (NULL == p1)
	{
		return;
	}
	for (int i = 0; i < 3; i++)
	{
		if (NULL == p1[i])
		{
			continue;
		}
		if (p1[i]->name != NULL)
		{
			free(p1[i]->name);
			p1[i]->name = NULL;
		}
		free(p1[i]);
		p1[i]= NULL;
	}
	free(p1);
	p1 = NULL;
}

void test()
{
	struct Person** p1 = NULL;
	//开辟堆空间
	p1 = openSpace(p1);
	//打印操作
	printPerson(p1);
	//释放堆空间
	freeSpace(p1);
}

int main()
{
	test();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值