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