為什么要用結構體?
在實際問題中,一組數據往往具有不同的數據類型。例如,在學生登記表中,姓名應為字符型;學號可為整型或字符型;年齡應為整型;性別應為字符型;成績可為整型或實型。顯然不能用一個數組來存放這一組數據。因為數組中各元素的類型和長度都必須一致,以便于編譯系統處理。為了解決這個問題,C語言中給出了另一種構造數據類型——“結構(structure)”或叫“結構體”。 它相當于其它高級語言中的記錄。“結構”是一種構造類型,它是由若干“成員”組成的。每一個成員可以是一個基本數據類型或者又是一個構造類型。結構既是一種“構造”而成的數據類型,那么在說明和使用之前必須先定義它,也就是構造它。如同在說明和調用函數之前要先定義函數一樣。讓編程序的人自定義一個數據類型。
結構體的定義:
struct Datas //結構體的定義用關鍵字struct,模板的作用
{int a;char c;float f;char*p;int array[2];
};
結構體的使用一:
#include <stdio.h>
#include <stdlib.h>
struct Student //結構體的定義用關鍵字struct,模板的作用,相當于自定義的類型
{int score; //特征:分數char name[128];//特征;名字,這個是定義了可以存儲128個字節大小的數組,空格占一個字節void (*pintroduce)(char* pname);//這里是函數指針
};
int main()
{//結構體的使用://類型 變量名 初始值struct Student stu1 ={98,"feng nan nan"};//如何訪問結構體:printf("結構體中的score=%d\n",stu1.score);printf("結構體中的name=%s\n",stu1.name);struct Student stu2;stu2.score=99;/*stu2.name="馮楠楠" 在給結構體中的字符串賦值時,C++、java可以這樣寫C語言必須用字符串拷貝函數strcpy進行賦值*/char* str="馮楠楠";strcpy(stu2.name,str);printf("結構體中的score=%d\n",stu2.score);printf("結構體中的name=%s\n",stu2.name);system("pause");return 0;
}
結構體使用二:
#include <stdio.h>
#include <stdlib.h>void func(int data)
{printf("函數:data=%d\n",data);
}
struct Datas
{char*p1;//這里p1是指針int a;char c;float f;double d;char str[128];void (*p)(int a);
};
int main()
{/*char *str="hello";這樣寫可以char str[];str="hello!";這樣寫就不行了要寫為strcpy(str,"hello!");還可以這樣寫:char *str=NULl;//定義指針時不往指針里寫東西就不用=NULL//如果要是寫還要malloc并且memsetstr="hello!";//可以直接賦值,但最好都加*/char* str1=NULL;//定義一個指針沒有malloc不能往里面寫東西str1=(char*)malloc(128);memset(str1,'\0',128);strcpy(str1,"hello!");char str[]="你很帥!";struct Datas d1;d1.a=100;d1.c='d';d1.f=3.14;d1.d=123.2323;strcpy(d1.str,str);d1.p=func;d1.p1=(char*)malloc(128);//p1在結構體中沒有開辟空間是個野指針,不能指直接寫入,需要開辟空間memset(d1.p1,'\0',128);strcpy(d1.p1,"注意指針這是否開辟空間");printf("結構體輸出:%d\n",d1.a);printf("結構體輸出:%c\n",d1.c);printf("結構體輸出:%f\n",d1.f);printf("結構體輸出:%lf\n",d1.d);puts(d1.str);puts(d1.p1);d1.p(10);//結構體函數調用system("pause");return 0;
}
結構體數組:
#include <stdio.h>
#include <stdlib.h>struct Student
{int score;char *name;//這里盡量用指針,減小結構體的大小//結構體太大,傳參數占空間
};
int main()
{int i;struct Student stu[3];struct Student maxStudent;//找最高分最低分找的是人,也就是詳細的信息,用結構體struct Student minStudent; for(i=0;i<sizeof(stu)/sizeof(stu[0]);i++){printf("請輸入第%d個學生的名字:\n",i+1);stu[i].name=(char*)malloc(128);scanf("%s",stu[i].name);//注意結構體內的指針使用前要開辟空間printf("請輸入第%d個學生的分數:\n",i+1);scanf("%d",&stu[i].score); }for(i=0;i<sizeof(stu)/sizeof(stu[0]);i++){printf("%s:%d\n",stu[i].name,stu[i].score);}//maxStudent.score=stu[0].score;//minStudent.score=stu[0].score;minStudent=maxStudent=stu[0];//相同類型的結構體可以對等for(i=0;i<sizeof(stu)/sizeof(stu[0]);i++){if(maxStudent.score<stu[i].score)maxStudent=stu[i];if(minStudent.score>stu[i].score)minStudent=stu[i];}printf("最高分是:%s:%d\n最低分是:%s:%d\n",maxStudent.name,maxStudent.score,minStudent.name,minStudent.score);system("pause");return 0;
}
結構體指針:
#include <stdio.h>
#include <stdlib.h>
//1、如果用結構體指針,就不能用點運算符訪問結構體中的變量應該用->
//2、注意結構體指針是否是野指針或者NULL,若是則會出現段錯誤
struct Student
{int score;char name[128];
};
int main()
{struct Student stu1;stu1.score=100;strcpy(stu1.name,"馮楠楠");printf("姓名:%s\n分數:%d\n",stu1.name,stu1.score);int* a;//整型定義指針struct Student *p=NULL;//同樣的道理也可以定義結構體指針//這里這樣定義屬于野指針,對野指針進行寫操作要出現段錯誤p=(struct Student*)malloc(sizeof(struct Student));//不管是野指針還是等于NULL,//都不能對這個非法的內存訪問,否則出現段錯誤 p->score=150;//如果用結構體指針,就不能用點運算符訪問結構體中的變量,要用->strcpy(p->name,"馮楠楠");printf("姓名:%s\n分數:%d\n",p->name,p->score);free(p);//空間不用了就free掉防止內存泄漏p=&stu1;//指針是存放地址的變量,之前指向malloc那片空間,現在存放的是stu1的地址printf("姓名:%s\n分數:%d\n",p->name,p->score); printf("地址是;%p\n",p++);//因為結構體大小為128+4=132,所以指針++偏移132個字節(十進制),結果:0060FE68printf("加加后地址是;%p\n",p);//結果:0060FEEC/*總結來說指針++,要看指針指向的對象是誰,并不是+1*/system("pause");return 0;
}
結構體指針操作學生成績表:
#include <stdio.h>
#include <stdlib.h>
/*
結構體指針訪問結構體內部元素方法:
結構體指針 ->成員名;如addr->country;
(*結構體指針).成員名;(*addr).country;//很少去進行使用,注意必須去使用(),,因為.優先級大于*
*/
struct Student
{int score;char*name;//4,linux 8
};
int main()
{int i;int len=2;struct Student stu[2];struct Student *p=stu;//結構體指針指向數組的頭,其實和整型數是一樣的for(i=0;i<sizeof(stu)/sizeof(stu[0]);i++){printf("請輸入名字;\n");p->name=(struct Student*)malloc(128);scanf("%s",p->name);printf("請輸入分數:\n");scanf("%d",&(p->score));p++;}p=stu;for(i=0;i<sizeof(stu)/sizeof(stu[0]);i++){printf("姓名:%s 分數::%d\n",p->name,p->score);p++;}struct Student *p2=(struct Student *)malloc(len*sizeof(struct Student));//上一行代碼是直接定義了結構體指針,并給他開辟5*sizeof(struct Student)怎么大的空間//相當于有5個結構體數組,可以存儲5個人的信息for(i=0;i<len;i++){printf("請輸入名字;\n");p2->name=(struct Student*)malloc(128);scanf("%s",p2->name);printf("請輸入分數:\n");scanf("%d",&(p2->score));p2++;}p2-=len;for(i=0;i<len;i++){printf("姓名:%s 分數::%d\n",p2->name,p2->score);p2++;}system("pause");return 0;
}
結構體指針函數綜合處理學生成績:
#include <stdio.h>
#include <stdlib.h>
//malloc在堆上面開辟空間,函數調用結束后空間不會被釋放
struct Student
{int score;char* name;
};
struct Student* initStuScores(int* len)//初始化函數,獲取用戶輸入完成初始化
{int i;printf("請輸入總人數:\n");scanf("%d",len);struct Student *p2=(struct Student *)malloc((*len)*sizeof(struct Student));//上一行代碼是直接定義了結構體指針,并給他開辟5*sizeof(struct Student)怎么大的空間//相當于有5個結構體數組,可以存儲5個人的信息//malloc開辟的空間不會消失,在函數內部定義的指針變量,不會對main函數中的指針有影響//p2是局部變量,返回的是p2的內容,不是p2for(i=0;i<(*len);i++){printf("請輸入名字;\n");p2->name=(struct Student*)malloc(128);scanf("%s",p2->name);printf("請輸入分數:\n");scanf("%d",&(p2->score));p2++;}return p2-*len;
}
void printMes(struct Student* p2,int len)
{int i;for(i=0;i<len;i++){printf("姓名:%s 分數::%d\n",p2->name,p2->score);p2++;}//p2=p2-len;
}
struct Student* findMaxStu(struct Student* p,int len)
{int i=0;struct Student* maxStudent;maxStudent=p;for(i;i<len;i++){if((p->score)>(maxStudent->score)){maxStudent=p;}p++;}return maxStudent;
}
struct Student* findMinStu(struct Student* p,int len)
{int i=0;struct Student* minStudent;minStudent=p;for(i;i<len;i++){if((p->score)<(minStudent->score)){minStudent=p;}p++;}return minStudent;}
float getAverage(struct Student*p,int len)
{int i;float toal=0;for(i=0;i<len;i++){toal=toal+p->score;p++;}return (float)toal/len;
}
int findSome(struct Student*p,int len,char*name)
{int i;for(i=0;i<len;i++){if(strcmp(name,p->name)==0){return 1;}p++;}return -1;
}
int main()
{int len;struct Student *pstus=initStuScores(&len);printMes(pstus,len);struct Student* max=NULL;struct Student* min=NULL;max=findMaxStu(pstus,len);min=findMinStu(pstus,len);//函數傳參其實就是將地址值拷貝一份給函數//函數內的指針變量不會對main函數中的指針有影響//除非用二級指針printf("最高分:%d,姓名:%s\n",max->score,max->name);printf("最低分:%d,姓名:%s\n",min->score,min->name);printf("平均分是:%f\n",getAverage(pstus,len));if(findSome(pstus,len,"馮楠")==1){printf("找到此人\n");}else{printf("沒有此人\n");}system("pause");return 0;
}
結構體大小如何計算:
#include <stdio.h>
#include <stdlib.h>
/*由于存儲變量地址對齊的問題,結構體大小計算必須滿足兩條原則:一、結構體成員的偏移量必須是成員大小的整數倍(0被認為是任何數的整數倍)二、結構體大小必須是所有成員(數組和結構體除外)大小的整數倍三、對齊方式確實很浪費空間,可是按照計算機的訪問方式,這種對齊方式提高了效率*/
//簡單結構體
struct s1
{char ch1;//1 ch1相對于整個結構體的偏移量就是0,因為他是結構體的第一項char ch2;//1 ch2相對于整個結構體的偏移量就是1,因為結構體第一項是1個字節int i;//4 i相對于整個結構體的偏移量就是2,2不是4的倍數,邏輯偏移2,//實際按照對齊規則,要偏移4個字節,這樣ch2和i之間就右空余了兩個字節,//所以一共是8個字節
};
//簡單結構體
struct s2{char ch1;//1 ch1偏移量是0,int i;//4 i的邏輯偏移值是1,要滿足第一條規則,所以偏移4//這樣ch1和i之間就有3個字節char ch2;//1 邏輯偏移量是8滿足條件一,但是結構體總大小為九//不滿足條件二,所以ch2要向后偏移3個字節,所以總大小是12
};
//成員包含數組的結構體
struct s3{char ch;//偏移值1int i;// 邏輯偏移值是1,實際偏移值4,ch和i之間有三個字節char str[10];//邏輯偏移值8,實際偏移值10,所以總大小是20//這個char類型的數組,只需要把它看做十個char連在一起即可
};
//成員包含結構體的結構體,若結構體內的結構體僅僅是聲明不占空間則可忽略
struct s4{char ch;//偏移量是1int i;//實際偏移量是1+3+4=8struct s{char ch1;int j;};//這個結構體大小是8,但是沒有定義所以忽略float f;//邏輯偏移量是8,實際偏移量是8,所以整個結構體大小為8+4=12//滿足條件一二
};
//成員包含結構體的結構體,若結構體內的結構體有定義,則占空間要計算
struct s5{char ch;//偏移量是1int i;//實際偏移量是1+3+4=8struct ss{char ch1;int j;}stemp;//這個結構體大小是8,滿足條件一float f;//邏輯偏移量是16,實際偏移量是16,所以整個結構體大小為16+4=20//滿足條件一二
};
//成員包含聯合體的結構體
struct s6{char ch;int i;union{//聯合體按照最大的計算就是4char ch1;int j;};
};
//指定對齊值:對齊值小于最大類型成員值
//如果最大成員超過了pack的要求,就按pack來對齊
//如果最大成員沒有超過pack,結構體總大小按最大成員開對齊
#pragma pack(4) //指定向4對齊 最大是8
struct s7{char ch;int i;float f;double d;
};
//對齊值大于最大類型成員值,當指定對齊值大于自身對齊值時,向自身對其值對齊,大小是24.#pragma pack(10)
struct s8{char ch;int i;float f;double d;
};
int main()
{printf("char:%d\n",sizeof(char));//1printf("float:%d\n",sizeof(float));//4printf("int:%d\n",sizeof(int));//4printf("double:%d\n",sizeof(double));//8printf("s1:%d\n",sizeof(struct s1));//8printf("s2:%d\n",sizeof(struct s2));//12printf("s3:%d\n",sizeof(struct s3));//20printf("s4:%d\n",sizeof(struct s4));//12printf("s5:%d\n",sizeof(struct s5));//20printf("s6:%d\n",sizeof(struct s6));//12printf("s7:%d\n",sizeof(struct s7));//20printf("s8:%d\n",sizeof(struct s8));//24system("pause");return 0;
}
typedef關鍵字
#include <stdio.h>
#include <stdlib.h>
/*typedeftypedef關鍵字,作用是為一種數據類型定義一個新的名字這里的數據類型包括(int、char等等)和自定義的數據類型(struct等)
*//*在單片機開發中,寄存器有8位 16位 32位int data=0x1234;int是4個字節32位,如果是8位單片機的話可能裝不下所以有了 char data=0x11這樣的定義,因為數據類型的表示范圍都是有重合的地方的,比如0~128 int float double char 這四個數據類型都表示但是char表示數字就有了以下的寫法typedef unsigned char u_int8;表示(0~255這個區間的數)typedef unsigned short int u_int16;typedef unsigned short int u_int32;這樣定以后就可以用u_int8來代替unsigned charu_int16 nsigned shortu_int32 unsigned short然后就可以:u_int8 data=10;u_int16 data2=20;u_int32 data3=30;這種定義方式了
*//*
typedef struct Student
{int score;char* name;void (*p)(struct Student stu1);//定義一個函數指針要求參數類型是結構體類型的//可以用struct Student但是不可以用STU stu1 //因為STU定義在結構體外邊
}STU,*PSTU;//通常使用typedef重命名結構體時,會命名名稱和指針
*/
/*
typedef struct Student STU,*PSTU;也可以這樣重命名結構體
*/
typedef struct//也可以將Student去掉,直接給結構體命名
{int score;char* name;void (*p)(struct stu);
}STU,*PSTU;//通常使用typedef重命名結構體時,會命名名稱和指針int main()
{STU stu1;//直接可以用命名后的STU代替struct Studentstu1.score=100;printf("%d\n",stu1.score);PSTU stu2;//這個stu2就代表結構體指針,這行代碼等同于struct Student* stu2//PSTU就等同于struct Student*stu2=(PSTU)malloc(sizeof(STU));stu2->score=99;printf("%d\n",stu2->score);system("pause");return 0;
}