c语言-结构体

1.结构体

(个人理解,结构体跟java中的类差不多)

可以理解为结构体是类的原形,毕竟c比java早诞生。

类似于类,但是不是类,因为c是面向过程编程,java是面向对象编程,两者之间是有差别的

可以在main函数中也可以在 main函数外(全局的)

1.1结构体的声明

结构体声明只是进行一个框架的描绘,并不会在内存中分配存储空间,直到真正定义一个结构体变量的时候。

案例:

1.2结构体变量

struct 结构体名称 结构体变量名

#include<stdio.h>

struct Book
{
	char title[128] ;
	char author[48] ;
	float price ;
	unsigned int date ;
	char publisher[40] ;
} book ; // 方式1 :也可以在这里定义book,是全局变量 


int main()
{
	struct Book book ; //方式二 : book为结构体的变量 
}

1.3访问结构体变量

#include<stdio.h>

struct Book
{
	char title[128] ;
	char author[48] ;
	float price ;
	unsigned int date ;
	char publisher[40] ;
} ; //也可以在这里定义book,是全局变量


int main()
{
	struct Book book ; //book为结构体的变量
	printf("书名:") ;
	scanf("%s",book.title) ;
	printf("作者: ") ;
	scanf("%s",book.author) ;
	printf("售价:") ;
	scanf("%f",&book.price) ;
	printf("日期:") ;
	scanf("%u",&book.date) ;
	printf("出版社: ") ;
	scanf("%s",book.publisher) ;

	printf("\n======录入完毕=======\n") ;


	printf("书名:%s\n",book.title) ;
	printf("作者:%s\n",book.author) ;
	printf("售价:%.2f\n",book.price) ;
	printf("日期:%u\n",book.date) ;
	printf("出版社:%s\n",book.publisher) ;
}

1.4初始化结构体变量

1.5初始化结构体的制定成员值

1.6 字节对齐

#include<stdio.h>

int main()
{
	struct A
	{
		char a ;
		int b ;
		char c ;
	} d = {'x',520,'0'} ;
	
	printf("size of a = %d\n",sizeof(d)) ;
 } 

char占一个字节,int四个字节,所以应该是6,但是答案并不是6! 正确答案为12

因为所有的数据都回进行内存对齐

可以观察到对齐后的大小是12 个字节。

结构体的大小是最大对齐数的整数倍,这里最大时4,所以没个都应该为4,一共12

但是如果进行修改,将c的char值放在int的上面

此时的结果为为8字节

	struct A
	{
		char a ;
		char c ;
		int b ;

	} d = {'x',520,'0'} ;

	printf("size of a = %d\n",sizeof(d)) ;
}

可以看出,这样的话,c就节省了4个内存空间

2.结构体嵌套

#include<stdio.h>

struct Date
{
	int year ;
	int month ;
	int day ;
} ;  

struct Book
{
	char title[128] ;
	char author[48] ;
	float price ;
	struct Date date ;
	char publisher[40] ;
} book = {
	"《c学习笔记》 ",
	"ljh",
	49.9,
	{2023,04,27},
	"清华大学出版社"  
} ;


int main()
{

	printf("书名:%s\n",book.title) ;
	printf("作者:%s\n",book.author) ;
	printf("售价:%.2f\n",book.price) ;
	printf("日期:%d-%d-%d\n",book.date.year,book.date.month,book.date.day) ;
	printf("出版社:%s\n",book.publisher) ;
}

3.结构体数组

方法1:在声明结构体的时候进行定义

方法2:

3.1初始化结构体数组

4.结构体指针

指向结构体的指针,称为结构体指针

结构体数组,数组名指向的第一个元素的地址,所以可以将数组赋值给结构体指针。

但是结构体数组和数组有差别,不一样的,要用取址符,取数组的地址再赋值给结构体指针。

4.1结构体指针访问结构体成员

int main()
{
	
	struct Book *pt ;
	pt = &book ;
	
	//方法1 
	printf("书名:%s\n",(*pt).title) ;
	printf("作者:%s\n",(*pt).author) ;
	printf("售价:%.2f\n",(*pt).price) ;
	printf("日期:%d-%d-%d\n",(*pt).date.year,(*pt).date.month,(*pt).date.day) ;
	printf("出版社:%s\n",(*pt).publisher) ;
	
	//方法2 推荐!!
	printf("书名:%s\n",pt->title) ;
	printf("作者:%s\n",pt->author) ;
	printf("售价:%.2f\n",pt->price) ;
	printf("日期:%d-%d-%d\n",pt->date.year,pt->date.month,pt->date.day) ;
	printf("出版社:%s\n",pt->publisher) ;
	
}

5.传递结构体变量

5.1两个结构体变量之间是否可以传递


int main()
{
	struct Test
	{
		int x ;
		int y ;
	}t1,t2;
	
	t1.x = 3 ;
	t1.y = 4 ;
	
	t2 = t1 ;
	
	printf("t2.x = %d, t2.y = %d\n",t2.x,t2.y ) ;
	 
}

通过案例发现,两个结构体变量之间是可以传递的,可以对其进行赋值,前提是同一类型的才可以!

5.2 结构体作为函数类型,并返回值,并传递参数

一个demo

#include<stdio.h>

struct Date
{
	int year ;
	int month ;
	int day ;
} ;

struct Book
{
	char title[128] ;
	char author[48] ;
	float price ;
	struct Date date ;
	char publisher[40] ;
} ;

struct Book getInput(struct Book book); // 结构体类型函数的声明
void printBook(struct Book book) ; // 声明函数 


struct Book getInput(struct Book book)  //定义了结构体类型的函数,并传递结构体类型的形式参数和返回值
{
	printf("书名:") ;
	scanf("%s",book.title) ;
	printf("作者: ") ;
	scanf("%s",book.author) ;
	printf("售价:") ;
	scanf("%f",&book.price) ;
	printf("日期:") ;
	scanf("%d-%d-%d",&book.date.year,&book.date.month,&book.date.day) ;
	printf("出版社: ") ;
	scanf("%s",book.publisher) ;

	return book ;
}

void printBook(struct Book book)
{
	printf("书名:%s\n",book.title) ;
	printf("作者:%s\n",book.author) ;
	printf("售价:%.2f\n",book.price) ;
	printf("日期:%d-%d-%d\n",book.date.year,book.date.month,book.date.day) ;
	printf("出版社:%s\n",book.publisher) ;
}

int main()
{
	struct Book b1 ,b2 ;

	printf("请录入第一本书的信息。。。\n") ;
	b1 = getInput(b1) ;
	putchar('\n') ;
	printf("请录入第二本书的信息。。。\n") ;
	b2 = getInput(b2) ;

	printf("====录入完毕,打印验证====\n") ;

	printf("打印第一本书的信息\n") ;
	printBook(b1) ;
	
	putchar('\n') ;  // 换行
	printf("打印第二本书的信息\n") ; 
	printBook(b2) ;
}

传递结构体变量会增加计算机的运行负担,所以作为开发者,不会这样用,会使用指针来。

6.传递指向结构体变量的指针

#include<stdio.h>

struct Date
{
	int year ;
	int month ;
	int day ;
} ;

struct Book
{
	char title[128] ;
	char author[48] ;
	float price ;
	struct Date date ;
	char publisher[40] ;
} ;

void getInput(struct Book *book); // 传递指向结构体变量的指针
void printBook(struct Book *book) ; // 声明函数


void getInput(struct Book *book)  //传递指向结构体变量的指针,因为是直接再指针上修改的,所以不需要返回值
{
	printf("书名:") ;
	scanf("%s",book->title) ;
	printf("作者: ") ;
	scanf("%s",book->author) ;
	printf("售价:") ;
	scanf("%f",&book->price) ;
	printf("日期:") ;
	scanf("%d-%d-%d",&book->date.year,&book->date.month,&book->date.day) ;
	printf("出版社: ") ;
	scanf("%s",book->publisher) ;

}

void printBook(struct Book *book)  // 传递结构体变量指针
{
	printf("书名:%s\n",book->title) ;
	printf("作者:%s\n",book->author) ;
	printf("售价:%.2f\n",book->price) ;
	printf("日期:%d-%d-%d\n",book->date.year,book->date.month,book->date.day) ;
	printf("出版社:%s\n",book->publisher) ;
}

int main()
{
	struct Book b1 ,b2 ;

	printf("请录入第一本书的信息。。。\n") ;
	getInput(&b1) ; // 因为直接传的是地址,所以就不用赋值了,b1=.. 这一步就可以省略了 
	putchar('\n') ;
	printf("请录入第二本书的信息。。。\n") ;
	getInput(&b2) ;

	printf("====录入完毕,打印验证====\n") ;

	printf("打印第一本书的信息\n") ;
	printBook(&b1) ;

	putchar('\n') ;
	printf("打印第二本书的信息\n") ;
	printBook(&b2) ;
}

7.动态申请结构体

7.1使用malloc函数为结构体申请内存空间

其他不变,这里只写了修改了的部分同时需要多一个#include<stdlib.h>


int main()
{
	struct Book *b1 ,*b2 ;

	b1 = (struct Book *)malloc(sizeof(struct Book));
	b2 = (struct Book *)malloc(sizeof(struct Book)) ;
	if(b1 == NULL || b2 == NULL)
	{
		printf("内存分配失败!\n") ;
		exit(1) ; 
	}

	printf("请录入第一本书的信息。。。\n") ;
	getInput(b1) ; // 因为直接传的是地址,所以就不用赋值了,b1=.. 这一步就可以省略了 
	putchar('\n') ;
	printf("请录入第二本书的信息。。。\n") ;
	getInput(b2) ;

	printf("====录入完毕,打印验证====\n") ;

	printf("打印第一本书的信息\n") ;
	printBook(b1) ;

	putchar('\n') ;
	printf("打印第二本书的信息\n") ;
	printBook(b2) ;
	
	free(b1) ; //释放动态内存
	free(b2) ;
}

8.单链表

在数据结构与算法学习中,会更加详细的学习。

8.1单链表插入元素(头插法)

  • 1.先定义了结构体book,包含了信息域和该节点的指向
  • 2.定义了getInput函数,获取用户输入的图书信息
  • 3.定义了addbook函数,头插法,将用户输入的信息插入到链表中
  • 4.定义了printLibrary函数,用于遍历链表
  • 5.release函数,释放链表申请的内存空间
  • 主函数中初始化链表尾空链表,将链表头指向空
#include<stdio.h>
#include<stdlib.h>

struct Book
{
	char title[128] ;
	char author[40] ;
	struct Book *next ; //这里代表的是单链表的指向
} ;

void getInput(struct Book *book)
{
	printf("书名:\n");
	scanf("%s",book->title) ;
	printf("作者:\n") ;
	scanf("%s",book->author) ;
}

void addBook(struct Book **head)  // 这里的插入元素是在链表头插入,两层解引用,取next的地址的值,因为是头插法,可以将其理解为head  补充:因为单链表的创建是创建了一个结构体的指针,所以下面的操作应该为*head,该函数在传递参数的时候只能传递**head,如果传递*head,下面在*head,就直接得到的是head的值,并不是指针类型的了。
{
	struct Book *book , * tmp; // 结构体指针

	book = (struct Book *)malloc(sizeof(struct Book));


	getInput(book) ;// 填充信息域的内容。

	//将填好的结构体的信息域插进链表,链表头指向空值和不指向空值两种情况。
	if(*head != NULL)
	{
		tmp = *head ;// 先将原有结构体的指向tmp
		*head = book ;  //  指向这个新的结构体
		book ->next = tmp ;// 最后再将新的结构体book的指向指向之前的指向,因为是插在了他的前面。
	}
	else
	{
		*head = book  ; // next指向这个结构体book
		book -> next = NULL ; //  然后将新插入的结构体book的指向再改为null。
	}
}

void printLibray(struct Book *head)
{
	struct Book *book ;
	book = head ;
	int cnt = 1 ;

	while(book != NULL)
	{
		printf("book%d\n",cnt) ;//表示第几本书
		printf("书名:%s\n",book->title) ;
		printf("作者:%s\n",book->author) ;

		book = book->next ;
		cnt++ ;
	}
}

void release(struct Book *head) // 释放掉链表申请的内存空间
{
	while(head != NULL)
	{
		struct Book *next = head ->next ;
		free(head) ;
		head = next ;
	 } 
}

int main()
{
	struct Book *head = NULL ; //结构体指针,刚开始指向null,代表空节点
	char ch ;

	while(1)
	{
		printf("请问是否需要录入书籍信息(y/n)");
		do
		{
			scanf("%c",&ch) ;
		}
		while(ch != 'y' && ch !='n') ;  //这里表示的是只有不是y或者n就一直输入,知道是y或者n的时候才结束跳出循环,这样的写法很不错!

		if(ch == 'y')
		{
			addBook(&head) ;
		}
		else
		{
			break ;
		}
	}

	printf("请问是否需要打印图书信息(y/n)") ;
	do
	{
		scanf("%c",&ch) ;
	}
	while(ch != 'y' && ch !='n') ;  //这里表示的是只有不是y或者n就一直输入,知道是y或者n的时候才结束跳出循环,这样的写法很不错!

	if(ch == 'y')
	{
		printLibray(head) ;
	}
	
	release(head) ; 	
	
}

8.2尾插法

其他的代码没有改动,只需要改一下这里就可以了。

void add_end(struct Book **head)  // 这里的插入元素是在链表头插入,两层解引用,取next的地址的值,因为是头插法,可以将其理解为head
{
	struct Book *book , * tmp; // 结构体指针

	book = (struct Book *)malloc(sizeof(struct Book));


	getInput(book) ;// 填充信息域的内容。

	//将填好的结构体的信息域插进链表,链表头指向空值和不指向空值两种情况。
	if(*head != NULL)
	{
		tmp = *head ;
		//定位单链表的尾部位置
		while(tmp ->next != NULL)
		{
			tmp = tmp->next ;
		}
		//直到尾节点,会跳出上面的循环,插入数据
		tmp->next = book ;
		book->next = NULL ;
	}
	else
	{
		*head = book  ; // next指向这个结构体book
		book -> next = NULL ; //  然后将新插入的结构体book的指向再改为null。
	}
}

经过观察可以看出,头插和尾插,遍历的结果是不一样的。

但是上面的方法,效率较低,每次尾插都有遍历链表,可以设一个指针,指向单链表的末尾。

8.2.1指针的尾插法

void add_end(struct Book **head)  // 这里的插入元素是在链表头插入,两层解引用,取next的地址的值,因为是头插法,可以将其理解为head
{
	struct Book *book ; // 结构体指针
	static struct Book *tail ; // 用来记录链表的尾节点


	book = (struct Book *)malloc(sizeof(struct Book));


	getInput(book) ;// 填充信息域的内容。

	//将填好的结构体的信息域插进链表,链表头指向空值和不指向空值两种情况。
	if(*head != NULL)
	{
		tail->next = book ;
		book->next = NULL ; 
	}
	else
	{
		*head = book  ; // next指向这个结构体book
		book -> next = NULL ; //  然后将新插入的结构体book的指向再改为null。
	}
	tail = book ; //上面的if 和 else 不管执行谁,tail都是链表的尾节点指向book 
}

8.3搜索单链表

搜索单链表的方法

struct Book *searchBook(struct Book *head,char *target)
{
	struct Book *book ;

	book = head ;
	while(book != NULL)
	{
		if(!strcmp(book->title,target) || !strcmp(book->author,target)) //相等返回的是0,代表false,取反
		{
			break ;
		}
		book = book -> next ;
	}
	return book ;//返回的是找到的这个节点的指针
}
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct Book
{
	char title[128] ;
	char author[40] ;
	struct Book *next ; //这里代表的是单链表的指向
} ;
void getInput(struct Book *book) ;
void add_end(struct Book **head) ;
void printLibray(struct Book *head);
struct Book *searchBook(struct Book *head,char *target) ;
void printBook(struct Book *book);
void release(struct Book *head) ;

void getInput(struct Book *book)
{
	printf("书名:\n");
	scanf("%s",book->title) ;
	printf("作者:\n") ;
	scanf("%s",book->author) ;
}

void add_end(struct Book **head)  // 这里的插入元素是在链表头插入,两层解引用,取next的地址的值,因为是头插法,可以将其理解为head
{
	struct Book *book ; // 结构体指针
	static struct Book *tail ; // 用来记录链表的尾节点


	book = (struct Book *)malloc(sizeof(struct Book));


	getInput(book) ;// 填充信息域的内容。

	//将填好的结构体的信息域插进链表,链表头指向空值和不指向空值两种情况。
	if(*head != NULL)
	{
		tail->next = book ;
		book->next = NULL ;
	}
	else
	{
		*head = book  ; // next指向这个结构体book
		book -> next = NULL ; //  然后将新插入的结构体book的指向再改为null。
	}
	tail = book ; //上面的if 和 else 不管执行谁,tail都是链表的尾节点指向book
}

void printLibray(struct Book *head)
{
	struct Book *book ;
	book = head ;
	int cnt = 1 ;

	while(book != NULL)
	{
		printf("book%d\n",cnt) ;//表示第几本书
		printf("书名:%s\n",book->title) ;
		printf("作者:%s\n",book->author) ;

		book = book->next ;
		cnt++ ;
	}
}

struct Book *searchBook(struct Book *head,char *target)
{
	struct Book *book ;

	book = head ;
	while(book != NULL)
	{
		if(!strcmp(book->title,target) || !strcmp(book->author,target)) //相等返回的是0,代表false,取反
		{
			break ;
		}
		book = book -> next ;
	}
	return book ;//返回的是找到的这个节点的指针
}

void printBook(struct Book *book)  // 输出搜索的结果的方法
{
	printf("书名: %s",book->title ) ;
	printf("作者: %s",book->author) ;
}

void release(struct Book *head) // 释放掉链表申请的内存空间
{
	while(head != NULL)
	{
		struct Book *next = head ->next ;
		free(head) ;
		head = next ;
	}
}

int main()
{
	struct Book *head = NULL ; //结构体指针,刚开始指向null,代表空节点
	char ch ,input[128]; // input是查找接受的字符数组
	struct Book *over; // 接收搜索链表返回的指针
	while(1)
	{
		printf("请问是否需要录入书籍信息(y/n)");
		do
		{
			scanf("%c",&ch) ;
		}
		while(ch != 'y' && ch !='n') ;  //这里表示的是只有不是y或者n就一直输入,知道是y或者n的时候才结束跳出循环,这样的写法很不错!

		if(ch == 'y')
		{
			add_end(&head) ;
		}
		else
		{
			break ;
		}
	}

	printf("请问是否需要打印图书信息(y/n)") ;
	do
	{
		scanf("%c",&ch) ;
	}
	while(ch != 'y' && ch !='n') ;  //这里表示的是只有不是y或者n就一直输入,知道是y或者n的时候才结束跳出循环,这样的写法很不错!

	if(ch == 'y')
	{
		printLibray(head) ;
	}

	printf("\n请输入书名或者作者:") ;
	scanf("%s",input) ;
	over = searchBook(head,input) ;
	if(over == NULL)
	{
		printf("很抱歉,没能找到!") ;
	}
	else
	{
		do
		{
			printf("已找到符合条件的图书。。\n") ;
			printBook(over) ; // 因为不止一本,所以要一直往下找 
		}while((over = searchBook(over->next,input)) != NULL) ; //没有到达链表尾部就一直找 
	}

	release(head) ;

}

8.4链表中插入元素

8.4.1插入元素的方法

void insertNode(struct Node **head,int value)  //如果插入第一个节点,我们要修改head指针的值,所以我们要传递指向node结构体的指针的指针,我们需要从内存空间的值修改,详情可以参考指针那一篇笔记
{
	struct Node *previous ; // 节点之前的指向
	struct Node *current ;  // 节点现在的指向
	struct Node *new ;  //可以参考图

	current = *head ;
	previous = NULL;

	while(current != NULL && current->value < value) // 从链表的头节点就开始查找,并且如果新传进来的值大于当前节点的值,就一直让他往后放,
//	当当前节点的值大于或者等于新传进来的值的时候,就会跳出循环了。
	{
		previous = current ;
		current = current->next ;
	}

	new = (struct Node *)malloc(sizeof(struct Node )) ;
	if(new == NULL)
	{
		printf("内存分配空间失败\n") ;
		exit(1) ;
	}
	new->value = value ;
	new->next = current ;
	if(previous == NULL)  //只有当传进来的head是空的单链表时,才会有这种情况,没有执行上面的while循环
	{
		*head = new ;
	}
	else
	{
		previous->next = new ; //指向新插入的节点,看图会更加明白
	}
}

解释该方法:

  • 该方法insertNode,用于向链表插入一个新节点,并且是按数值从小到大排序的,插入节点时,需要传递链表头节点的地址,以及需要插入的值

  • 在函数中传入的head是一个指向struct Node 的指针变量,因为我们想要在函数内部修改它指向的内存空间的值,所以需要使用指向指针的指针,即两个*号,

  • 在函数中,新节点插入到链表头部时,实际上是修改了head所指内存空间的值,将其指向新节点,因此,为了能够实现这样的操作,需要将head的地址作为参数传递给函数,也就是指针struct Node ** 类型的变量,这样,我们就可以通过修改指向指针所指的内存空间的值来改变head的指向了。

8.4.2完整代码

#include<stdio.h>
#include<stdlib.h>

struct Node
{
	int value ;
	struct Node *next ;
} ;

void insertNode(struct Node **head,int value)  //如果插入第一个节点,我们要修改head指针的值,所以我们要传递指向node结构体的指针的指针,我们需要从内存空间的值修改,详情可以参考指针那一篇笔记
{
	struct Node *previous ; // 节点之前的指向
	struct Node *current ;  // 节点现在的指向
	struct Node *new ;  //可以参考图

	current = *head ;
	previous = NULL;

	while(current != NULL && current->value < value) // 从链表的头节点就开始查找,并且如果新传进来的值大于当前节点的值,就一直让他往后放,
//	当当前节点的值大于或者等于新传进来的值的时候,就会跳出循环了。
	{
		previous = current ;
		current = current->next ;
	}

	new = (struct Node *)malloc(sizeof(struct Node )) ;
	if(new == NULL)
	{
		printf("内存分配空间失败\n") ;
		exit(1) ;
	}
	new->value = value ;
	new->next = current ;
	if(previous == NULL)  //只有当传进来的head是空的单链表时,才会有这种情况,没有执行上面的while循环
	{
		*head = new ;
	}
	else
	{
		previous->next = new ; //指向新插入的节点,看图会更加明白
	}
}
void printNode(struct Node *head)
{
	struct Node *current ;

	current = head ;
	while(current != NULL)
	{
		printf("%d ",current->value) ;
		current = current->next ;
	}
	putchar('\n') ;// 换行
}
int main()
{
	struct Node *head = NULL ;
	int input ;

	while(1)
	{
		printf("请输入一个整数:(输入-1结束)") ;
		scanf("%d",&input) ;
		if(input == -1) break ;
		insertNode(&head,input) ;
		printNode(head) ;
	}
}

8.5删除链表的节点

8.5.1删除链表的方法


void deleteNode(struct Node **head , int value)
{
	struct Node *previous ;
	struct Node *current ;
	
	current = *head ;
	previous = NULL ;
	
	while(current != NULL && current->value != value)
	{
		previous = current ;
		current = current->next ;
	}
    
	if(current == NULL)
	{
		printf("找不到匹配的节点\n") ;
		return ; // 整个函数结束 
	}
	else
	{
		if(previous == NULL)  // 特殊情况,该节点在第一个节点的位置,就没有指向上面的while循环 
		{
			*head = current->next ; //删除当前节点,在第一个节点的位置,就是让头节点head指向,该节点的下一个位置
		}
		else
		{
			previous->next = current->next ; 
		}
		
		free(current) ; // 删除了该节点,就释放其空间。	
	} 
 } 
 

8.5.2完整代码,参考8.4.2

10.基础typedef

相比宏定义的直接替换,typedef是对类型的封装

案例:

#include<stdio.h>
#define integer int // 意思是用integer 代替 int

typedef int integer ;

int main()
{
	integer a ;
	int b ;
	
	a = 520 ;
	b = a ;
	
	printf("a = %d\n",a ) ;
	printf("b = %d\n",b) ;
	printf("size of a = %d\n",sizeof(a)) ; 
}

可能会有疑惑,这跟宏定义不是差不多嘛,对的,可以用宏定义来代替,但是用宏定义和用typedef是有差别的

看下方案例

#include<stdio.h>

//#define integer int  // 这个不会报错 ,但是输出的结果不对 
typedef int integer ; //会报错 

int main()
{
	unsigned integer a ; // 无符号整形
	
	a = -1 ; 
	printf("a = %u\n",a) ;  // 输出的是-1的补码他的类型是无符号整形	,所以他的补码被当做无符号的正数输出 
	
}
#include<stdio.h>

typedef int INTEGER , *PTRINT;  // 可以这样写,一次封装多个 
//typedef int *PTRINT ;

//#define PTRINT int*  // 如果用宏定义,就会警告报错,因为,宏定义替换的,只是将b替换为int类型的指针,c并不是指针 

int main()
{
	INTEGER a = 520 ;
	PTRINT b, c ;

	b = &a ;
	c = b ;

	printf("addr of a = %p\n",c) ;
}

10.1typedef和struct

没有typedef:

#include<stdio.h>
#include<stdlib.h>

struct Date
{
	int year ;
	int month ;
	int day ;
} ;

int main()
{
	struct Date *date ;

	date = (struct Date *)malloc(sizeof(struct Date)) ;
	if(date == NULL)
	{
		printf("分配内存失败") ;
		exit(1) ;
	}
	
	date->year = 2023 ;
	date->month = 5 ;
	date->day = 1 ;
	
	printf("%d-%d-%d",date->year , date->month , date->day) ; 
}

有了typedef:

 #include<stdio.h>
#include<stdlib.h>

typedef struct Date
{
	int year ;
	int month ;
	int day ;
} DATE, *PDATE;  // 需要用到该结构体的,和该结构的指针,统一格式,加一个*P 


int main()
{
	//struct Date *date ;
	PDATE date ;
    
	date = (PDATE)malloc(sizeof(DATE)) ;
	......
        ....
        ....
    ......以下内容同上,省略
}

可以用typedef封装结构体。

注意:封装结构体时,一般在下面写两个 一个是结构体的大写名字,一个是加了*P+结构体名字,代表该结构体的指针,后面在使用的时候就不要写struct Date 或者 struct Date *了, 只需要写 DATE , PDATE, 这是在封装结构体时,起的别名。

11.进阶typedef

11.1数组指针和typedef

#include <stdio.h>

typedef int (*PTR_TO_ARRAY)[3] ;

int main()
{
	int array[3] = {1,2,3} ;
	PTR_TO_ARRAY ptr_to_array = &array ;

	int i ;
	for(i = 0 ; i < 3 ; i++)
	{
		printf("%d\n",(*ptr_to_array)[i]) ;
	}
}

11.2函数指针和typedef

#include<stdio.h>

typedef int (*PTR_TO_FUN)(void) ;

int fun()
{
	return 520 ;
}

int main()
{
	PTR_TO_FUN ptr_to_fun = &fun ;
	
	printf("%d\n",(*ptr_to_fun)()) ; //这里注意要在后面加上一个括号(),表示指针解引用之后是一个函数的调用 
	
}

(*)ptr_to_fun()是指调用了该函数,后面的那个括号是用来传递参数的,如果不用传递参数,就只写一个()就好了,有参数的话,把参数写进去。

11.3指针数组和typedef

#include<stdio.h>

typedef int *(*PTR_TO_FUN)(int) ;

int *fun1(int num)
{
	return &num; 
} 
int *fun2(int num)
{
	return &num;
}
int *fun3(int c )
{
	return &c ; 
}
int main()
{
	PTR_TO_FUN array[3] = {&fun1,&fun2,&fun3} ; //指针数组,存放指向每个函数的指针
	
	for(int i = 0 ; i < 3 ; i++)
	{
		printf("add of num :%p\n",(*array[i])(i)) ;// array的元素是一个地址,指针数组,所以解引用,解引用之后,是这个函数的地址,调用函数后面要加小括号 ,相当于调用该函数,如果有形式参数,需要在小括号里写上 
	 } 
	
}

上面这样写是不合法的,会有报错,只是用来举一个案例。

12.共用体

共用体的所有成员共享同一个内存地址

#include<stdio.h>
#include<string.h>

union Test
{
	int i ;
	double pi ;
	char str[6] ;
} ;

int main()
{
	union Test test ;// 共用体的变量名
	test.i = 520 ;
	test.pi = 3.14 ;
	strcpy(test.str,"FishC") ;

	printf("addr of test.i: %p\n",&test.i) ;
	printf("addr of test.pi: %p\n",&test.pi) ;
	printf("addr of test.str: %p\n",&test.str) ;

	printf("test.i: %d\n",test.i) ;
	printf("test.pi: %.2f\n",test.pi) ;
	printf("test.str: %s\n",test.str) ;
	//如果想要同时打印出共用体的值,只有最后一个打印的是正确的,因为会被覆盖,只有最后一个赋值的是正确的
	
	printf("size of test %d",sizeof(test)) ; // 与内存对齐有关系。 
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bsqCshn5-1683818932185)(null)]

12.1定义共用体类型变量

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EssN9N3h-1683818932142)(null)]

12.2初始化共用体

13.枚举类型

如果一个变量只有几种可能的值,那么就可以将其定义为枚举类型

13.1声明枚举变量

13.2定义枚举变量

#include<stdio.h>
#include<time.h> //加载时间库,获取周几 

int main()
{
	enum Week {sun,mon,tue,wed,thu,fri,sat} ;  //声明枚举变量
	enum Week today ;          //定义枚举变量

	struct tm *p ; // time函数返回的值为指针
	time_t t ;  //  time_t 类型变量的指针

	time(&t) ;  // 需要传入time_t类型的变量,获取当前系统的秒数
	p = localtime(&t) ; //localtime的返回值是tm类型的指针

	today = p->tm_wday ; //tm_day返回值是0-6,从周一开始,和java的calendar函数一样。
	//today得到tm_wday的返回值之后去枚举变量中找从0-6开始的序号,一一对应的,然后得到一个值
	// 再去传递给switch函数,就得到了正确的结果了。 

	switch(today)
	{
		case mon :
		case tue :
		case wed :
		case thu :
		case fri :
			printf("学习\n") ;
			break ;
		case sat :
		case sun:
			printf("周末\n") ;
			break ;
		default:
			printf("Error") ; 
	
	}

}

案例:

有指定值,指定值后面的值就会加一,依次累加,如果指定值前面还有没有指定的值,那么那些值将从0开始

#include<stdio.h>

int main()
{
	enum Color {red = 10 , green , blue} ; // 有指定值,指定值后面的值就会加一,如果指定值前面还有没有指定的值,那么那些值将从0开始 
	enum Color rgb ;
	
	for(rgb = red ; rgb <= blue ;rgb++)
	{
		printf("rgb is %d\n",rgb) ;
	 } 
	
 } 

3x4a4QE-1683818928806)]

[外链图片转存中…(img-4emGDmDF-1683818928806)]

[外链图片转存中…(img-IowK4tdc-1683818928806)]

12.2初始化共用体

[外链图片转存中…(img-RCK6q4gv-1683818928807)]

13.枚举类型

如果一个变量只有几种可能的值,那么就可以将其定义为枚举类型

13.1声明枚举变量

[外链图片转存中…(img-AuTxXlJd-1683818928807)]

13.2定义枚举变量

[外链图片转存中…(img-jgtPwyPR-1683818928807)]

#include<stdio.h>
#include<time.h> //加载时间库,获取周几 

int main()
{
	enum Week {sun,mon,tue,wed,thu,fri,sat} ;  //声明枚举变量
	enum Week today ;          //定义枚举变量

	struct tm *p ; // time函数返回的值为指针
	time_t t ;  //  time_t 类型变量的指针

	time(&t) ;  // 需要传入time_t类型的变量,获取当前系统的秒数
	p = localtime(&t) ; //localtime的返回值是tm类型的指针

	today = p->tm_wday ; //tm_day返回值是0-6,从周一开始,和java的calendar函数一样。
	//today得到tm_wday的返回值之后去枚举变量中找从0-6开始的序号,一一对应的,然后得到一个值
	// 再去传递给switch函数,就得到了正确的结果了。 

	switch(today)
	{
		case mon :
		case tue :
		case wed :
		case thu :
		case fri :
			printf("学习\n") ;
			break ;
		case sat :
		case sun:
			printf("周末\n") ;
			break ;
		default:
			printf("Error") ; 
	
	}

}

案例:

有指定值,指定值后面的值就会加一,依次累加,如果指定值前面还有没有指定的值,那么那些值将从0开始

#include<stdio.h>

int main()
{
	enum Color {red = 10 , green , blue} ; // 有指定值,指定值后面的值就会加一,如果指定值前面还有没有指定的值,那么那些值将从0开始 
	enum Color rgb ;
	
	for(rgb = red ; rgb <= blue ;rgb++)
	{
		printf("rgb is %d\n",rgb) ;
	 } 
	
 } 
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值