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
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 #
}
int *fun2(int num)
{
return #
}
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)) ; // 与内存对齐有关系。
}
12.1定义共用体类型变量
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) ;
}
}