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 };
typedef int(ARRAY_TYPE)[5];
ARRAY_TYPE myarray;
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;
printf("%d\n", *(*pArray + 1));
typedef int(*ARRAY_POINTER)[5];
ARRAY_POINTER pArr = &arr;
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(*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 };
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);
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()
{
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;
}