c语言笔记第二部分

记录一下c语言的学习过程,笔记如有错误,欢迎指出!!!

文章目录

  • 10.结构体共用体和联合体:
    • 1.结构体(数组)的创建
    • 2.结构体的赋值
    • 3.结构体的嵌套
    • 4.结构体的偏移量
    • 5.结构体的内存对齐

10.结构体共用体和联合体:

1.结构体(数组)的创建

struct Person
{
	char name[64];
	int age;
};
typedef struct Person myPerson;

typedef struct Person
{
	char name[64];
	int age;
}myPerson;

void test01()
{
	struct Person p = { "Tom", 18 };
	myPerson p2 = { "Jerry", 20 };
}


struct Person2
{
	char name[64];
	int age;
}myPerson2 = { "aaa", 20 };


//匿名结构体, //只能定义一次该类型
struct 
{
	char name[64];
	int age;
}myPerson3 = { "bbb", 30 };  

结构体的创建

//创建在栈上
struct Person p = { "aaa", 10 };
printf("姓名:%s  年龄: %d\n", p.name, p.age);

//创建在堆区
struct Person * p2 = malloc(sizeof(struct Person));
strcpy(p2->name, "bbb");  //只有定义时,才可以采用字符串赋值,其他情况下,需要用strcpy, sprintf等函数,修改字符串的值。
p2->age = 20;


结构体数组的创建
//在栈上分配内存
struct Person persons[] =
{
    { "aaa", 10 },
    { "bbb", 20 },
    { "ccc", 30 },
    { "ddd", 40 },
};
int len = sizeof(persons) / sizeof(struct Person);
//printArray(persons, len);


//在堆区分配内存
struct Person * pArray = malloc(sizeof(struct Person) * 4);   // sizeof(pArray) = 4
for (int i = 0; i < 4;i++)
{
    sprintf(pArray[i].name, "name_%d", i + 1);
    pArray[i].age = 18 + i;
}
printArray(pArray, 4);
if (pArray != NULL)
{
    free(pArray);
    pArray = NULL;
}

2.结构体的赋值

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

struct Person p1 = { "Tom", 18 };
struct Person p2 = { "Jerry", 20 };
//浅拷贝,只拷贝,对应内存中存放的值
p1 = p2;
printf("p1的姓名:%s  年龄 :%d\n", p1.name, p1.age);
printf("p2的姓名:%s  年龄 :%d\n", p2.name, p2.age);

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

struct Person2 p1;
p1.name = malloc(sizeof (char)* 64);
strcpy(p1.name, "Tom");
p1.age = 18;
struct Person2 p2;
p2.name = malloc(sizeof (char)* 128);
strcpy(p2.name, "Jerry");
p2.age = 28;


	
//p1 = p2; //系统提供的赋值操作是简单的浅拷贝 ,我们需要做手动赋值,提供深拷贝, name中存放的是地址,若进行浅拷贝,则p1, p2指向相同的地址
// free(p1); free(p2) 会出错,因为name中的地址释放了两次,因此需要手动赋值

 手动赋值 
//先释放原来堆区的内容
if (p1.name != NULL)
{
	free(p1.name);
	p1.name = NULL;
}
//在堆区创建内存
p1.name = malloc(strlen(p2.name) + 1);
strcpy(p1.name, p2.name);
p1.age = p2.age;

printf("p1的姓名:%s  年龄 :%d\n", p1.name, p1.age);
printf("p2的姓名:%s  年龄 :%d\n", p2.name, p2.age);
if (p1.name != NULL)
{
	free(p1.name);
	p1.name = NULL;
}

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


3.结构体的嵌套

一级指针

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

struct Person ** allocateSpace()     //返回struct **person类型, 在堆区创建,函数结束后不会使用,可以使用同级指针接收开辟的空间
{
	struct Person ** temp = malloc(sizeof(struct Person *) * 3);
    for (int i = 0; i < 3;i++)
    {
        //创建结构体内存
        temp[i] = malloc(sizeof(struct Person));

        //将结构体姓名 创建在堆区 
        temp[i]->name = malloc(sizeof(char)* 64);

        //给姓名赋值
        sprintf(temp[i]->name, "name_%d", i + 1);

        temp[i]->age = 18 + i;
    }
    return temp;
}

void printPerson(struct Person ** pArray, int len)
{
	for (int i = 0; i < len;i++)
	{
		printf("姓名: %s 年龄: %d\n", pArray[i]->name, pArray[i]->age);
	}
}

void freeSpace(struct Person ** pArray , int len)   //由内向外逐步释放,由于接受的是同级指针,最外层指针需要在函数外面置NULL
{
	if ( pArray == NULL)
	{
		return;
	}
	if (len <= 0)
	{
		return;
	}
	for (int i = 0; i < 3;i++)
    {
        if (pArray[i]->name != NULL)
        {
            printf("%s被释放了\n", pArray[i]->name);
            free(pArray[i]->name);
            pArray[i]->name = NULL;
        }

        free(pArray[i]);
        pArray[i] = NULL;
    }

    free(pArray);
    pArray = NULL;
}

void test01()
{
	struct Person ** pArray = NULL;
	pArray = allocateSpace();
	//打印数组
	printPerson(pArray, 3);
	//释放内存
	freeSpace(pArray,3);
	pArray = NULL;
}

结构体二级指针嵌套

struct Teacher
{
	char * name;
	char ** students;
};

void allocateSpace(struct Teacher*** teachers)   //形参为三级指针
{
	if (teachers == NULL)
	{
		return;
	}

    //开辟内存
    struct Teacher ** ts =  malloc(sizeof(struct Teacher *) * 3);

    //给每个老师分配内存
    for (int i = 0; i < 3;i++)
    {
        ts[i] = malloc(sizeof(struct Teacher));
         //给老师的姓名分配内存
        ts[i]->name = malloc(sizeof(char)* 64);

        //给老师起名称
        sprintf(ts[i]->name, "Teacher_%d", i + 1);
         //给学生的数组分配内存
        ts[i]->students = malloc(sizeof(char *)* 4);

        //给学生的姓名开辟内存 以及赋值
        for (int j = 0; j < 4;j++)
        {
            ts[i]->students[j] = malloc(sizeof(char)* 64);
            sprintf(ts[i]->students[j], "%s_Student_%d", ts[i]->name, j + 1);
        }
	}
	*teachers = ts;
}

void printTeachers(struct Teacher** pArray)
{
	if (pArray == NULL)
	{
		return;
	}
	for (int i = 0; i < 3;i++)
    {
        printf("%s\n", pArray[i]->name);
        for (int j = 0; j < 4;j++)
        {
           printf("      %s\n", pArray[i]->students[j]);
       }
    }
}



void freeSpace(struct Teacher ** pArray)
{
	if (pArray == NULL)
	{
		return;
	}
	for (int i = 0; i < 3;i++)
    {
        //先释放老师姓名
        if (pArray[i]->name != NULL)
     	{
	       free(pArray[i]->name);
            pArray[i]->name = NULL;
        }

        //释放学生姓名
        for (int j = 0; j < 4;j++)
        {
            if (pArray[i]->students[j] != NULL)
            {
                free(pArray[i]->students[j]);
                pArray[i]->students[j] = NULL;
           }
        }
        //释放学生的数组
       if (pArray[i]->students != NULL)
       {
            free(pArray[i]->students);
            pArray[i]->students = NULL;
       }

       //释放老师
       if (pArray[i] != NULL)
        {
            free(pArray[i]);
            pArray[i] = NULL;
        }

    }
    //释放老师数组
    if (pArray != NULL)
    {
        free(pArray);
        pArray = NULL;
   }
}
	
void test01()
{
	struct Teacher ** pArray = NULL;
	//开辟内存
	allocateSpace(&pArray);
	//打印数组
   printTeachers(pArray);

    //释放数组
    freeSpace(pArray);
    pArray = NULL;
}

4.结构体的偏移量

struct Teacher
{
	char a; //0 ~ 3
	int b;  //4 ~ 7
};

struct Teacher t1;
struct Teacher *p = &t1;

//两种求偏移量的方式
printf("b的属性偏移量为:%d\n", (int)&(p->b) - (int)p);
printf("b的属性偏移量为:%d\n", offsetof(struct Teacher, b));  // offsetof 在stddef.h中

//通过偏移量访问结构体元素
struct Teacher t1 = { 'a', 10 };
printf("t1.b = %d\n", *(int *)((char *)&t1 + offsetof(struct Teacher, b)));
printf("t1.b = %d\n", *(int *)((int *)&t1 + 1 ));


//结构体嵌套求偏移量
struct Teacher2
{
	char a;
	int b;
	struct Teacher c;
};


int offset1 = offsetof(struct Teacher2, c);
int offset2 = offsetof(struct Teacher, b);
printf("%d\n", *(int*)((char*)&t1 + offset1 + offset2));
printf("%d\n",   (( struct Teacher * )((char*)&t1 +offset1))->b  );

共用体

union test
{
	char ch;
	short sh;
	int var;
};
	联合体,内部所有成员变量地址一致。等同于整个联合体的地址。

		联合体的大小,是内部成员变量中,最大的那个成员变量的大小。(对齐)

		修改其中任意一个成员变量的值,其他成员变量会随之修改。

枚 举:

enum  color { 枚举常量 };

enum  color { red, green, blue, black, pink, yellow };

	 枚举常量: 是整型常量。不能是浮点数。可以是负值。 默认初值从 0 开始,后续常量较前一个常量 +1.

		   可以给任意一个常量赋任意初值。后续常量较前一个常量 +1

5.结构体的内存对齐

Cpu按块读取内存,一块是2^n,假设一块是4个字节
读取一个int型的变量,若变量存放在4的整数倍内存处,只需要读取一次,若存放在奇地址处,则需要读取两个块,即读取两次
因此内存对齐可以提高访问效率,以空间换时间。
结构体中存在对齐操作, char类型占四个字节

结构体内存对齐规则:
1.结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处

2.其他成员变量要对⻬到某个数字(对⻬数)的整数倍(0,1,2…)的地址处。
对⻬数=编译器默认的⼀个对⻬数与该成员变量⼤⼩的较⼩值。

  • VS 中默认的值为 8 (查看当前对齐(模)数 : #pragma pack(show) 修改对齐(模)数 : #pragma pack(1))
  • Linux中 gcc 没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩

3.结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的整数倍。

4.如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

为了解决上面问题,函数的调用方和被调用放对于函数如何调用必须有一个明确约定,这样的约定成为调用惯例

cdecl,C/C++默认调用惯例, C++也可使用stdcall(标准调用惯例)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

C语言中,栈顶是低地址,栈底是高地址,高位数据放高地址,低位数据放低地址 –小端对齐

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

main函数中定义普通变量如 int a = 9; int b = 10 时,int类型占的字节不是4,因为还要存放上下地址,两个int的地址不一定连续

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值