🌿前言:在我看来,书应该越读越薄的,所以我的笔记尽量记录了每一个点,可供复习与查阅,但没有详细的解释。我的C语言的笔记是在谭浩强老师的C程序设计(第五版)的基础上总结归纳的,结合了一些我自己的见解。如果是有其他见解 ,也欢迎大家提出。
一、定义和使用结构体变量
1.建立结构体类型
(1)结构体:用户自己建立的由不同类型数据组成的组合型的数据结构
(2)结构体类型:一个关键字struct和结构体名构成的
(3)声明一个结构体类型的一般形式为:
struct 结构体名
{成员表列};
· 花括号内,即成员表列中,要对各成员进行类型声明即
类型名 成员名
· 成员表列:又名域表,每一个成员是结构体中的一个域。
2.定义结构体类型变量
(1)先声明结构体类型,再定义该类型的变量
struct Student
{ int num;
char sex;
int age;
}
struct Student student1,student2;
(2)在声明的同时定义
struct 结构体名
{成员表列
}变量名表列;
(3)不指定类型名而直接定义结构体类型变量
struct
{成员表列
}变量名表列;
Ps:
· A.结构体类型和结构体变量区别:
可以对变量赋值、存取或运算,但类型不可以
编译时对变量分配空间,而类型无
· B.结构体类型中的变量可以与程序中的变量名相同
· C.对结构体变量中的成员可以单独使用
3.结构体变量的初始化和引用
(1)结构体变量的初始化
struct Student
{ int num;
char sex;
int age;
}student1={100,‘M’,16};
Ps : 对某一成员初始化:
struct Student student1={.num=16};
(2)引用结构体变量中成员的值
结构体变量名 . 成员名
·“.”是成员运算符,在所有运算符中优先级最高,所以可以直接把“结构体变量名 . 成员名”当做一个整体
· 在程序中可以对变量的成员赋值
· 不能通过输出结构体变量名来输出结构体变量所有成员的值,只能对其中各个成员分别进行输入和输出。
(3)如果成员本身又属于一个结构体类型,则要用若干个成员运算符,一级一级找到最低级的成员。只能对于最低级的成员进行赋值存取或运算
如:student1.birthday.month
(4)对结构体成员可像普通变量一样进行各种运算
(5)同类结构体变量可以相互赋值
二、使用结构体数组
结构体数组不同于数值型数组之处在于每个元素都是一个结构体类型的数据,它们都分别包括各个成员项。
1.定义结构体数组
(1)一般形式
struct 结构体名
{成员表列} 数组名[数组长度];
(2)也可先声明结构体类型,再用此类型定义结构体数组
结构体类型 数组名[数组长度];
(3)初始化,直接在数组后面加上
={初值表列};
2.应用
三、结构体指针
1.指向结构体变量的指针
指向结构体对象的指针变量既可以指向结构体变量,也可以指向结构体数组中的元素。但是指针的基类型必须与结构体变量的类型相同
如:
struct Student student1;
struct Student *p;
p=&student1;
· 输出时用的是如(*p).num,因为成员运算符优先级较高
也可以用p->num代替,“->”称为指向运算符
2.指向结构体数组的指针
3.用结构体变量和结构体变量的指针作函数参数
将一个结构体变量的值传递给另一个函数:
(1)用结构体变量的成员作参数
(2)用结构体变量作实参
(3)用指向结构体变量(或数组元素)的指针作实参,将结构体变量(或数组元素)的地址传给形参
四、用指针处理链表
1.可变数组
#include<stdio.h>
#include<stdlib.h>
typedef struct
{
int *array;
int size;
}Array;
Array array_create(int init_size)//创建
{
Array a;
a.array=(int*)malloc(sizeof(int)*init_size);
a.size=init_size;
return a;
}
void array_free(Array *a)//回收
{
free(a->array);
a->array=NULL;
a->size=0;
}
//封装
int array_size(const Array *a)//可用单元
{
return a->size;
}
int* array_at(Array *a,int index)//访问单元
{
return &(a->array[index]);
}
void array_inflate(Array *a,int more_size)//数组增加
{
int *p=(int*)malloc(sizeof((int)(a->size+more_size)));
int i;
for(i=0;i<a->size;i++)
{
p[i]=a->array[i];
}
free(a->array);
a->array=p;
a->size+=more_size;
}
int main()
{
Array a=array_create(100);
printf("%d\n",array_size(&a));
*array_at(&a,0)=10;
printf("%d\n",*array_at(&a,0));
int number;
int cnt=0;
while(1){
scanf("%d",array_at(&a,cnt++));
}
array_free(&a);
return 0;
}
· 可变数组的缺陷:不够灵活高效,在内存受限(如单片机)的情况下不够友好
2.什么是链表
(1)链表根据需要开辟内存单元。
(2)链表有一个头指针变量,它存放一个地址,该地址指向一个元素。链表中每一个元素称为结点,每个结点都应包括两个部分,一是用户需要用的实际数据,二是下一个节点的地址。最后一个元素则称为表尾,它的地址部分放一个“NULL”表示空地址,链表到此结束。
链表中各元素在内存中的地址可以是不连续的。
(3)链表这种数据结构必须利用指针变量才能实现,即一个结点中应包含一个指针变量,用它存放下一个结点的地址。
3.建立静态链表
静态链表:所有结点都是在程序中定义的,不是临时开辟的,也不能用完后释放;
#include<stdio.h>
#include<stdlib.h>
typedef struct _nude
{
int value;
struct _nude *next;
}Node;
typedef struct _list
{
Node* head;
}List;
void add(List* pList,int number);
void print(List *pList);
int main()
{
List list;
int number;
list.head=NULL;
do
{
scanf("%d",&number);
if(number!=-1)
{
add(&list,number);
}
}while(number!=-1);
Node *p;//链表的搜索
print(&list);
scanf("%d",&number);
Node *q;
int isFound=0;
for(p=list.head;p;p=p->next)
{
if(p->value==number)
{
printf("找到了\n");
isFound=1;
break;
}
}
if(!isFound)
{
printf("没找到\n");
}
for(q=NULL,p=list.head;p;q=p,p=p->next)
{
if(p->value==number)
{
if(q)
{
q->next=p->next;
}
else
{
list.head=p->next;
}
free(p);
break;
}
}
return 0;
}
void add(List* pList,int number)//链表主体
{
//add to linked-list
Node *p=(Node*)malloc(sizeof(Node));
p->value=number;
p->next=NULL;
//find the last
Node *last=pList->head;
if(last)
{
while(last->next)
{
last=last->next;
}
//attach
last->next=p;
}
else
{
pList->head=p;
}
}
void print(List *pList) //输出链表
{
Node *p;
for(p=pList->head;p;p=p->next)
{
printf("%d\t",p->value);
}
printf("\n");
}
4.建立动态链表
建立动态链表是指在程序执行过程中从无到有地建立起一个链表,即一个一个地开辟结点和输入各结点数据,并建立起前后相链的关系。
#include<stdio.h>
#include<stdlib.h>
#define LEN sizeof(struct Student)
struct Student
{
long num;
float score;
struct Student *next;
} ;
int n;
struct Student *creat(void)
{
struct Student *head;
struct Student *p1,*p2;
n=0;
p1=p2=(struct Student*)malloc(LEN);
scanf("%ld,%f",&p1->num,&p1->score);
head=NULL;
while(p1->num!=0)
{
n=n+1;
if(n==1)
head=p1;
else
p2->next=p1;
p2=p1;
p1=(struct Student*)malloc(LEN);
scanf("%ld,%f",&p1->num,&p1->score);
}
p2->next=NULL;
return(head);
}
int main()
{
struct Student *pt;
pt=creat();//函数返回链表第一个结点的地址
printf("\nnum:%ld\nscore:%5.lf\n",pt->num,pt->score);
return 0;
}
5.输出链表
五、共用体类型
1.什么是共用体类型
(1)几个不同的变量共享同一段内存的结构,也就是使用覆盖技术,使后一个数据覆盖了前面的数据,这就是共用体类型的结构。
(2)定义共用体类型变量的一般形式
union 共用体名
{成员表列
}变量表列;
(也可以将类型声明与变量定义分开。也可以不定义共用体类型名。)
(3)共用体与结构体的区别
结构体变量所占内存长度是各成员占的内存长度之和,每个成员分别占有其自己的内存单元。共用体变量所占的内存长度等于最长的成员的长度。
2.引用共用体变量的方式
先定义再引用,而且不能引用共用体变量,而只能引用共用体变量中的成员。
引用方法同结构体变量。
3.共用体类型数据的特点
(1)同一个内存段可以用来存放几种不同类型的成员,但在每一瞬间只能存放其中一个成员,而不是同时存放几个。
(2)可以对共用体变量初始化,但初始化表中只能有一个常量
(3)共用体变量中起作用的成员是最后一次被赋值的成员,在对共用体变量中的一个成员赋值后,原有变量储存单元中的值就被取代。
(4)共用体变量的地址和它各成员的地址都是同一地址。
(5)不能对共用体变量名赋值,也不能企图引用变量名来得到一个值。
(6)以前的c规定不能把共用体变量作为函数参数,但可以使用指向共用体变量的指针作为函数参数。c99允许用共用体变量作为函数参数。
(7)共用体类型可以出现在结构体类型定义中,也可以定义共用体数组,反之结构体也可以出现在共用体类型定义中,数组也可以作为共用体的成员。
六、使用枚举类型
(1)如果一个变量只有几种可能的值,就可以定义为枚举类型。
(2)声明枚举类型的一般形式
enum [枚举名] {枚举元素列表};
(3)定义变量
enum weekday w,y;
w,y是枚举变量
花括号里的sun,mon,tue等是枚举元素或枚举常量。
枚举变量与其他数值型量不同,它的值只限于花括号中一定的值之一。
也可以不声明有名字的枚举类型而直接定义枚举变量,如
enum {sun,mon,tue,wed,thu,fri,sat} w,y;
· 不能对枚举元素或枚举常量赋值,但可以引用和输出的。
· 每个枚举元素都代表一个整数,编译是按定义时的顺序,默认他们的指为0,1,2,3,4以此类推。
· 但可以人为地指定枚举元素的数值,在定义枚举类型时显式地指定
enum weekday{sun=7,mon=1,tue,wed,thu,fri,sat} w,y;
以后的顺序加1,sat为6
七、使用typedef声明新类型名
用typedef指定新的类型名来代替已有的类型名,有下面两种情况
(1)简单的用一个新的类型名代替原有的类型名。
(2)命名一个简单的类型名代替复杂的类型表示方法。
简单的说,就是按定义变量的方法,把变量名换成新类型名,并且在最前面加typedef,就声明了新类型名代表原来的类型。
如typedef float Real
· 习惯上,常把用typedef声明的类型名的第一个字母用大写表示,以便与系统提供的标准类型标识符相区别。
· typedef实际上是为特定的类型指定了一个同义字,没有创造新的类型。
· 当不同源文件中用到同一类型数据(尤其是像指针,数组,结构体,共用体等类型数据)时,常用typedef声明一些类型数据。