结构体-C语言

结构体属于用户自定义的数据类型,允许用户存储不同的数据类型。

1. 结构体的声明

1.1 结构的基础知识

结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。

1.2 结构的声明

struct tag {
	member-list;//成员列表
} variable-list;//变量列表(全局的结构体变量)

例如描述一个学生:

typedef struct Stu {
	char name[20];//名字
	int age;//年龄
	char sex[5];//性别
	char id[20];//学号
} Stu;//分号不能丢

注:一个汉字算两个字符。

1.3 结构成员的类型

结构的成员可以是标量、数组、指针,甚至是其他结构体。

1.4 结构体变量的定义和初始化

在 C 语言中,变量和对象是一个概念。

有了结构体类型,如何定义变量?

struct Point {
	int x;
	int y;
}p1;			//声明类型时定义变量p1
struct Point p2;//定义结构体变量p2

初始化:定义变量的同时附初值/定义变量后逐个赋值。

struct Point p3 = {x, y};
struct Point_3D {
	Point pt;
	int depth;
	char name[10];
}pt1 = {{5, 6}, 10, "pt_1"};

struct Point_3D pt2 = {{7, 8}, 9, "pt_2"};

struct Point_3D pt3;
pt3.pt = {1, 2};
pt3.depth = 4;
pt3.name = "pt_3";

结构体的嵌套:
作用:结构体中的成员可以是另一个结构体。
初始化

struct Node {
	int data;
	struct Point p;
	struct Node* next;
} n1 = {10, {4, 5}, NULL};

struct Node n2 = {20, {7, 8}, NULL};

struct Node n3;
n3.data = 30;
n3.p.x = 3;
n3.p.y = 4;
n3.next = NULL;

printf("%d %d", n3.p.x, n3.p.y);

2. 结构体成员的访问

  • 结构体变量访问成员

    结构体变量的成员是通过点操作符(.)访问的。点操作符接受两个操作数。

    例如:

    struct Stu {
    	char name[20];
    	int age;
    };
    
    struct Stu s1;
    

    我们可以看到 s1 有成员 name 和 age;那我们如何访问 s1 的成员?

    strcpy(s.name, "zhangsan");
    s.age = 20;
    
  • 结构体指针访问指向结构体变量的成员
    有时我们得到的是一个结构体变量的指针,那如何访问成员?

    结构体变量的指针可以通过 -> 操作符 或者 *操作符+.操作符 来访问指向结构体变量的成员(属性)。

    struct Stu {
    	char name[20];
    	int age;
    };
    
    void print(struct Stu* ps) {
    	printf("name = %s   age = %d\n", (*ps).name, (*ps).age);
    	printf("name = %s   age = %d\n", ps->name, ps->age);
    }
    
    int main() {
    	struct Stu s = {"zhangsan", 20};
    	print(&s);
    	
    	return 0;
    }
    

. 操作符 优先级高于 * 解引用操作符。

3. 结构体传参

作用:将结构体作为参数向函数中传递
传递方式有两种:

  • 值传递
  • 地址传递

直接上代码:

struct Stu {
	char name[20];
	int age;
};
	
struct Stu s = {"zhangsan", 20};

void print1(struct Stu s) {//结构体传参
	printf("name = %s   age = %d\n", s.name, s.age);
}

void print2(const struct Stu* ps) {//结构体地址传参
	printf("name = %s   age = %d\n", ps->name, ps->age);
}

int main() {
	print1(s);//传结构体
	print2(&s);//传地址
		
	return 0;
}

上面的两个函数哪个好些?

答案是:首选 print2() 函数。
原因:

函数传参的时候,参数是需要压栈的。
如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,所以会导致性能的下降。

结论:结构体传参的时候,要传结构体的地址。

补充:加入 const 使得指针指向的结构体变量无法被修改,防止函数体中的误操作。

4. 练习

代码 1:

假设 p 的值是 0x100000。如下表达式的值分别为多少?
已知,结构体 Test 类型的变量大小是 20 个字节。

#include <stdio.h>

struct Test {
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}*p = (struct Test*)0x100000;

int main() {
	printf("%p\n", p + 0x1);//00100014
	printf("%p\n", (unsigned long)p + 0x1);//00100001
	printf("%p\n", (unsigned int*)p + 0x1);//00100004

	return 0;
}

  • 0x100000 为整型,赋给指针时最好进行强制类型转换。
  • 地址需要以 十六进制的形式 进行计算,因此 结构体地址 + 20 <===> 结构体地址 + 0x00000014。
  • 整数按地址形式输出需要转换为十六进制。
  • 打印地址需要打印 8 位 十六进制数。

代码 2:

#include <stdio.h>

int main() {
	int a[4] = { 1, 2, 3, 4 };
	int* ptr1 = (int*)(&a + 1);//4
	int* ptr2 = (int*)((int)a + 1);//2000000
	printf("%x,%x", ptr1[-1], *ptr2);

	return 0;
}

%x:按 16 进制形式打印。

代码 3:

#include <stdio.h>

int main() {
	int a[3][2] = { (0,1),(2,3),(4,5) };//{ 1, 3, 5, 0, 0, 0 };
	int* p;
	p = a[0];
	printf("%d", p[0]);//1

	return 0;
}

a[0] 是第一行的数组名,表示首元素的地址,即 a[0][0] 的地址(&a[0][0])。

注意 逗号表达式 和 数组初始化 的区别。

代码 4:

#include <stdio.h>

int main() {
	int a[4][5] = { 0 };
	int(*p)[4];
	p = a;
	printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);//FFFFFFFC,-4

	return 0;
}
  • 地址 - 地址 是地址之间元素的个数,有正负之分。
  • %p 打印内存中的值,可以认为是打印无符号数。

代码 5:

#include <stdio.h>

int main() {
	char* a[] = { "work", "at", "alibaba" };
	char** pa = a;

	pa++;
	printf("%s\n", *pa);//at

	return 0;
}

%s 打印地址向后的内容,传入的参数是一个地址。

代码 6:

#include <stdio.h>

int main() {
	char* c[] = { "ENTER", "NEW", "POINT", "FIRST" };
	char** cp[] = { c + 3,c + 2,c + 1,c };
	char*** cpp = cp;

	printf("%s\n", **++cpp);//POINT
	printf("%s\n", *-- * ++cpp + 3);//ER
	printf("%s\n", *cpp[-2] + 3);//ST
	printf("%s\n", cpp[-1][-1] + 1);//EW

	return 0;
}
  • ++cpp 会改变 cpp 的值(或者叫 cpp 指向的地址/位置)。
  • cpp[-2] 不会改变 cpp 的值。
  • –*cpp 会改变 cp 中相对应的指针的值,也需要注意(⚠)。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值