1.關(guān)于c語言的結(jié)構(gòu)體:
首先我們?yōu)槭裁匆玫浇Y(jié)構(gòu)體,我們都已經(jīng)學(xué)了很多int char …等類型還學(xué)到了同類型元素構(gòu)成的數(shù)組,以及取上述類型的指針,在一些小應(yīng)用可以靈活使用,然而,在我們實(shí)際應(yīng)用中,每一種變量進(jìn)行一次聲明,再結(jié)合起來顯然是不太實(shí)際的,類如一位學(xué)生的信息管理,他可能有,姓名(char),學(xué)號(hào)(int)成績(jī)(float)等多種數(shù)據(jù)。如果把這些數(shù)據(jù)分別單獨(dú)定義,就會(huì)特別松散、復(fù)雜,難以規(guī)劃,因此我們需要把一些相關(guān)的變量組合起來,以一個(gè)整體形式對(duì)對(duì)象進(jìn)行描述,這就是結(jié)構(gòu)體的好處。
2、首先我們要了解一些小知識(shí)
2.1**只有結(jié)構(gòu)體變量才分配地址,而結(jié)構(gòu)體的定義是不分配空間的。**
2.2結(jié)構(gòu)體中各成員的定義和之前的變量定義一樣,但在定義時(shí)也不分配空間。
2.3結(jié)構(gòu)體變量的聲明需要在主函數(shù)之上或者主函數(shù)中聲明,如果在主函數(shù)之下則會(huì)報(bào)錯(cuò)
2.4c語言中的結(jié)構(gòu)體不能直接進(jìn)行強(qiáng)制轉(zhuǎn)換,只有結(jié)構(gòu)體指針才能進(jìn)行強(qiáng)制轉(zhuǎn)換
2.5相同類型的成員是可以定義在同一類型下的
列如
struct Student
{?
int number,age;//int型學(xué)號(hào)和年齡
char name[20],sex;//char類型姓名和性別
float score;
};
最后的分號(hào)不要忘了 有的編譯器會(huì)自動(dòng)加上,因此有的同學(xué)就會(huì)不注意。
3、關(guān)于結(jié)構(gòu)體變量的定義和引用
在編譯時(shí),結(jié)構(gòu)體的定義并不分配存儲(chǔ)空間,對(duì)結(jié)構(gòu)體變量才按其數(shù)據(jù)結(jié)構(gòu)分配相應(yīng)的存儲(chǔ)空間
?struct Book
?{?
?char title[20];//一個(gè)字符串表
示的titile 題目
char author[20];//一個(gè)字符串表示的author作者
?float value;//價(jià)格表示?
?};//這里只是聲明 結(jié)構(gòu)體的定義?
struct Book book1,book2;//結(jié)構(gòu)體變量的定義 分配空間
book1.value;//引用結(jié)構(gòu)體變量
定義結(jié)構(gòu)體變量以后,系統(tǒng)就會(huì)為其分配內(nèi)存單元,比如book1和book2在內(nèi)存中占44個(gè)字節(jié)(20+20+4)具體的長(zhǎng)度你可以在你的編譯器中使用sizeof關(guān)鍵字分別求出來。
列如
當(dāng)然,要注意一點(diǎn):用sizeof關(guān)鍵字求結(jié)構(gòu)體長(zhǎng)度時(shí),返回的最大基本類型所占字節(jié)的整數(shù)倍 比方說我們上面求得的為44 為 float(4個(gè)字節(jié))的整數(shù)倍,
但是我們把title修改為title[22]; 這時(shí)正常長(zhǎng)度為46 ,但是你會(huì)發(fā)現(xiàn)實(shí)際求得的為48,(4的整數(shù)倍)
這就涉及到結(jié)構(gòu)體的存儲(chǔ):
1結(jié)構(gòu)體整體空間是占用空間最大的成員(的類型)所占字節(jié)數(shù)的整數(shù)倍。
2.結(jié)構(gòu)體的每個(gè)成員相對(duì)結(jié)構(gòu)體首地址的偏移量(offset)都是最大基本類型成員字節(jié)大小的整數(shù)倍,如果不是編譯器會(huì)自動(dòng)補(bǔ)齊,
關(guān)于這個(gè)我們簡(jiǎn)單介紹下:
1.偏移量----偏移量指的是結(jié)構(gòu)體變量中成員的地址和結(jié)構(gòu)體變量首地址的差。即偏移字節(jié)數(shù),結(jié)構(gòu)體大小等于最后一個(gè)成員的偏移量加上他的大小,第一個(gè)成員的偏移量為0,
struct S1
{
? ? char a;
? ? int b;
? ? double c;
};
這里char a 偏移量為1 之后為int b 因?yàn)槠屏?不為int(4)的整數(shù)倍,所以會(huì)自動(dòng)補(bǔ)齊,而在 double c 時(shí),偏移量為8 是double(8)的整數(shù)倍,所以不用自動(dòng)補(bǔ)齊 最后求得結(jié)構(gòu)體得大小為 16
具體看下圖:
通過上面的代碼同學(xué)們應(yīng)該會(huì)有一個(gè)簡(jiǎn)單的認(rèn)知
4、結(jié)構(gòu)體變量的初始化
結(jié)構(gòu)體的初始化有很多需要注意的地方,這里我們說明下
首先是幾種初始化的方法
ps:在對(duì)結(jié)構(gòu)體變量初始化時(shí),要對(duì)結(jié)構(gòu)體成員一一賦值,不能跳過前面成員變量,而直接給后面成員賦初值,但是可以只賦值前面幾個(gè),對(duì)與后面未賦值的變量,如果是數(shù)值型,則會(huì)自動(dòng)賦值為0,對(duì)于字符型,會(huì)自動(dòng)賦初值為NULL,即‘’
4.1定義時(shí)直接賦值
struct Student
{?
char name[20];
char sex;
int number;
}stu1={"zhaozixuan",'M',12345};
//或者
struct Student
{?
char name[20];
char sex;
int number;
};
struct Student stu1={"zhaozixuan",'M',12345};
注意字符為‘ ’ 字符串為""
4.2定義結(jié)構(gòu)體之后逐個(gè)賦值
stu1.name="王偉";
stu1.sex='M';
stu1.number=12305;
//也可用strcpy函數(shù)進(jìn)行賦值
strcpy(stu1.name,"王偉");
4.3定義之后任意賦值
?struct Student stu1={
? .name="Wang",
? .number=12345,
? .sex='W',?
?};//可以對(duì)任意變量賦值
這樣寫的好處時(shí)不用按照順序來進(jìn)行初始化,而且可以對(duì)你想要賦值的變量直接進(jìn)行賦值,而不想賦值的變量可以不用賦值
需要注意的是如果在定義結(jié)構(gòu)體變量的時(shí)候沒有初始化,那么后面就不能全部一起初始化了;
等下結(jié)構(gòu)體數(shù)組初始化時(shí)我們還會(huì)有一個(gè)講解
這里我們順帶提一下typedef說明結(jié)構(gòu)體類型
這里的BOOK就相當(dāng)于struct book的一個(gè)別名一樣,用它來定義結(jié)構(gòu)體變量非常簡(jiǎn)便
主要也是考二級(jí)要用到,所以我們簡(jiǎn)單介紹下
5、結(jié)構(gòu)體變量的引用(輸出和輸入)
5.1結(jié)構(gòu)體變量的賦值用scanf賦值和printf輸出時(shí)跟其他變量操作一樣
但是有幾點(diǎn)需要注意
(1) .是運(yùn)算符,在所有運(yùn)算符優(yōu)先級(jí)中最高
(2)如果結(jié)構(gòu)體的成員本身是一個(gè)結(jié)構(gòu)體,則需要繼續(xù)用.運(yùn)算符,直到最低一級(jí)的成員。
struct Student
{char name[20];
char sex;
int number;
struct Date
{
int year;
?int month;
?int day;
}birthday;
}stu1;
printf("%d",stu1.birthday);//這樣子是錯(cuò)誤的,因?yàn)閎irthday也是一個(gè)結(jié)構(gòu)體變量
scanf("%d",&stu1.birthday.month);//正確
(3)可以引用接頭體變量成員的地址,也可以引用結(jié)構(gòu)體變量的地址:
printf("%o", student);(輸出student的首地址)(%o 按八進(jìn)制輸出)
6、結(jié)構(gòu)體數(shù)組及其初始化(重點(diǎn))
這里我們簡(jiǎn)單說下,具有相同類型的結(jié)構(gòu)體變量組成數(shù)組就是結(jié)構(gòu)體數(shù)組
結(jié)構(gòu)體數(shù)組與結(jié)構(gòu)體變量區(qū)別只是將結(jié)構(gòu)體變量替換為數(shù)組
struct Student
{?
char name[20];
char sex;
int number;
}stu1[5]={
{"zhaozixuan",'M',12345},
{"houxiaohong",'M',12306},
{"qxiaoxin",'W',12546},
{"wangwei",'M',14679},
{"yulongjiao",'W',17857}
};
stu1[3].name[3]//表示stu1的第三個(gè)結(jié)構(gòu)變量中姓名的第五個(gè)字符
//若初始化時(shí)已經(jīng)是結(jié)構(gòu)體數(shù)組全部元素[]中的數(shù)可以不寫如stu1[]=
注意結(jié)構(gòu)體數(shù)組要在定義時(shí)就直接初始化,如果先定義再賦初值是錯(cuò)誤的
比如:
struct Student stu1;
stu1[3]={
? {"zhaozixuan",'M',12345},
? {"houxiaohong",'M',12306},
? {"qxiaoxin",'W',12546}
? };
這樣子是錯(cuò)誤的,
這里我在寫的時(shí)候遇到一些問題,還是結(jié)構(gòu)體數(shù)組初始化的問題,折騰了下解決了,給大家分享下
對(duì)于數(shù)組初始化時(shí)
比如
char str[20];
str="I love you";/* 這樣會(huì)修改數(shù)組的地址,但是數(shù)組的地址分配之后是不允許改變的 */
在第一條語句中 str就已經(jīng)被定義成數(shù)組而在C99標(biāo)準(zhǔn)中不允許將字符串(實(shí)際上是一個(gè)指針變量) 賦值給數(shù)組,所以如果我們直接賦值是錯(cuò)誤的
那么怎么弄呢
這里提供3種方法
1.定義數(shù)組時(shí)直接定義
char str[20]=“I love you”;
2.用strcpy或者memset函數(shù)進(jìn)行復(fù)制
char str[20];
strcpy(str,“I love you”);
再用到memset函數(shù)時(shí),出現(xiàn)了一些問題
對(duì)于memcset函數(shù)簡(jiǎn)單介紹下
memset
void *memset(void *s,int c,size_t n)
作用:將已開辟內(nèi)存空間s的首n個(gè)字節(jié)的值設(shè)為值c。
char str[20];
memset(str,'a',20);
如果是字符類型數(shù)組的話,memset可以隨便用,但是對(duì)于其他類型的數(shù)組,一般只用來清0或者填-1,如果是填充其他數(shù)據(jù)就會(huì)出錯(cuò)
int str[10];
memset(str,1,sizeof(str));//這樣是錯(cuò)誤的
這里我們說下這個(gè)錯(cuò)誤,
首先我們要知道m(xù)emset在進(jìn)行賦值時(shí),是按字節(jié)為單位來進(jìn)行賦值的,每次填充的數(shù)據(jù)長(zhǎng)度為一個(gè)字節(jié),而對(duì)于其他類型的變量,比如int,占4個(gè)字節(jié) 所以sizeof(str)=40;而用memset賦值時(shí),將會(huì)對(duì)指向str地址的前40個(gè)字節(jié)進(jìn)行賦值0x01(00000001) 的操作,把0x00000000賦值4次0x01操作變?yōu)?x01010101(00000001000000010000000100000001)
相當(dāng)于給“前10個(gè)int”進(jìn)行了賦值0x01010101的操作 對(duì)應(yīng)十進(jìn)制的16843009
所以會(huì)出很大的錯(cuò)誤
這里請(qǐng)務(wù)必要注意,但是如果是清零一個(gè)數(shù)組用memset還是很方便的
簡(jiǎn)單使用的話同學(xué)們用strcmp函數(shù)就行
3用指針(注意內(nèi)存分配)
char *str;
str=“I love you”;
這兩句話的本質(zhì)是,在內(nèi)存中開辟一段內(nèi)存空間,把"I love you"放進(jìn)這段內(nèi)存空間,然后把這段內(nèi)存空間的地址交給str,由于str是變量,所以給它賦值是合法的。
請(qǐng)注意,在我們進(jìn)行數(shù)組初始化的時(shí)候如果定義的數(shù)組過長(zhǎng),而我們只初始化了一部分?jǐn)?shù)據(jù),對(duì)于未初始化的數(shù)據(jù)如果是數(shù)值型,則會(huì)自動(dòng)賦值為0,對(duì)于字符型,會(huì)自動(dòng)賦初值為NULL,即‘’ 即不足的元素補(bǔ)以默認(rèn)值
這里我們?cè)?小節(jié)中也提到了
比如
int str[10]={1};//這里只是把str的第一個(gè)元素賦值為1,其他元素默認(rèn)為0
7、結(jié)構(gòu)體與指針
我們知道,指針指向的是變量所占內(nèi)存的首地址,在結(jié)構(gòu)體中,指針指向的是結(jié)構(gòu)體變量的起始地址,當(dāng)然也可指向結(jié)構(gòu)體變量的元素
這里我們分為三部分
7.1指向結(jié)構(gòu)體變量的指針
定義形式一般為
struct 結(jié)構(gòu)體名* 指針名;
比如:struct Student* p;
struct Student
{
char cName[20];
?int number;
?char csex;??
}student1;
struct Student*p;
p=&student1;
//若為結(jié)構(gòu)體數(shù)組則
struct Student stu1[5];
struct Student*p;
p=stu1;//因?yàn)閟tu1為結(jié)構(gòu)體數(shù)組而p=stu1直接是指向stu1的首地址,就不用再加&符
用結(jié)構(gòu)體指針變量訪問結(jié)構(gòu)體變量成員有以下兩種方式:
(*p).cName //這里的括號(hào)不能少,在5.1中有提到
p->cName
簡(jiǎn)單來說以下三種形式是等價(jià)的
p->cName
(*p).cName?
student1.cName
p->cName //可以進(jìn)行正常的運(yùn)算
p->number++; 是將結(jié)構(gòu)體變量中number的值進(jìn)行運(yùn)算,然后再加一,
這里要注意下,等下在7.2中會(huì)有比較
7.2指向結(jié)構(gòu)體數(shù)組的指針
7.1中我們已經(jīng)提到結(jié)構(gòu)體數(shù)組指針的命名,這里我們僅對(duì)一些知識(shí)點(diǎn)做下介紹
這里我們接著來說結(jié)構(gòu)體數(shù)組指針
在我們想要用指針訪問結(jié)構(gòu)體數(shù)組的第n個(gè)數(shù)據(jù)時(shí)可以用
struct Student stu1[5];
struct Student*p;
p=stu[n];
(++p).number//是指向了結(jié)構(gòu)體數(shù)組下一個(gè)元素的地址
7.3結(jié)構(gòu)體成員是指針類型變量
比如
struct Student
{
?char* Name;//這樣防止名字長(zhǎng)短不一造成空間的浪費(fèi)
?int number;
?char csex;??
}student1;
在使用時(shí)可以很好地防止內(nèi)存被浪費(fèi),但是注意在引用時(shí)一定要給指針變量分配地址,如果你不分配地址,結(jié)果可能是對(duì)的,但是Name會(huì)被分配到任意的一的地址,結(jié)構(gòu)體不為字符串分配任何內(nèi)存存儲(chǔ)空間具有不確定性,這樣就存在潛在的危險(xiǎn),
struct Student
{
?char* Name;
?int number;
?char csex;??
}stu,*stu;
stu.name=(char*)malloc(sizeof(char));//內(nèi)存初始化
這里我們說一下,同學(xué)們看書的時(shí)候一般不會(huì)看到,
如果我們定義了結(jié)構(gòu)體指針變量,他沒有指向一個(gè)結(jié)構(gòu)體,那么這個(gè)結(jié)構(gòu)體指針也是要分配內(nèi)存初始化的,他所對(duì)應(yīng)的指針類型結(jié)構(gòu)體成員也要相應(yīng)初始化分配內(nèi)存
struct Student
{
?char* Name;
?int number;
char csex;??
}stu,*stu;
stu = (struct student*)malloc(sizeof(struct student));./*結(jié)構(gòu)體指針初始化*/
? stu->name = (char*)malloc(sizeof(char));/*結(jié)構(gòu)體指針的成員指針同樣需要初始化*/??
7.4二叉樹遍歷算法
二叉樹的二叉鏈表類型定義如下:
typedef struct btnode {
datatype data;
struct btnode *lchild,*rchild;
};
這里我們僅僅提出以下,因?yàn)樯婕暗芥湵恚信d趣的同學(xué)可以去學(xué)習(xí)下(二級(jí)要用),
7.5結(jié)構(gòu)體作為函數(shù)參數(shù)
首先我們要注意的一點(diǎn),使用結(jié)構(gòu)體變量作為函數(shù)參數(shù)的時(shí)候,采取的是值傳遞的方式,將結(jié)構(gòu)體所占內(nèi)存單元的內(nèi)容全部傳遞給形參,并且形參必須也要是同類型的結(jié)構(gòu)體變量,在使用時(shí),會(huì)自動(dòng)創(chuàng)建一個(gè)結(jié)構(gòu)體變量作為原變量的副本,并且也需要占內(nèi)存,并且在調(diào)用期間如果修改(形參)結(jié)構(gòu)體中成員的值,修改值是無效的,
而如果用指針作為實(shí)參,傳遞給函數(shù)的形參,這時(shí)候傳遞的是結(jié)構(gòu)體的地址,形參所指向的地址就是結(jié)構(gòu)體變量的地址,這時(shí)候進(jìn)行修改的話是可以修改的,這正是指針的精華所在
在這里我們?cè)偬峁追N互換兩個(gè)結(jié)構(gòu)體的方法
struct Student
{
?char cName[20];
?int number;
?char csex;??
}student1,student2;
struct Student student1={"Wang",12345,'W'};
struct Student student2={"Zhao",54321,'M'};?
struct Student*stu1=&student1;
struct Student*stu2=&student2;
struct Student *student3;
student3=stu1;
stu1=stu2;
stu2=student3;//互換地址
2對(duì)于同類型結(jié)構(gòu)體直接互換值就行
struct stu student3;
student3=student1;
student1=student2;
student2=student3;
//這里也可以寫成應(yīng)strcmp函數(shù)互換
3用memcpy()函數(shù)進(jìn)行互換
4比較笨的方法:用for循環(huán)互換
最后提下memset清空結(jié)構(gòu)體
struct Student
{
?char cName[20];
?int number;
?char csex;??
}stu1;
一般情況下,清空str的方法:
str.cName[0]='';
str.csex='0';
str.number=0;
但是我們用memset就非常方便:
memset(&str,0,sizeof(struct Student));
如果是數(shù)組:
struct Student stu[10];
就是
memset(stu,0,sizeof(struct Student)*10);
審核編輯:湯梓紅
?
評(píng)論
查看更多