(1).malloc和free:
0).正經步驟;
malloc申請:記得判斷p是否爲空
使用內存:只是用申請的,別超過範圍;不要改變p的值
free釋放內存:還要記得p=NULL,避免野指針;
1). void *指針指向類型不確定,在需要時可以轉換爲任意類型。int *p=(int *)malloc(1000*sizeof(int))返回一個void *可以在前面加上一個int(*)做轉換;若申請失敗,返回NULL;所以在用這段內存前一定要檢查(NULL == p).
2).在malloc和free期間不要改變p的值,否則程序會吃了內存(內存泄露),知道當前進程死掉,內存纔會被回收;在free後,事實上你申請的那段內存還能用,但是堆管理器告訴你不該使用了,free後最後來p =NULL;
3).gcc中malloc是以最小單位16字節的方式分配內存,當小於16字節(包括malloc(0))都會返回給你16字節內存;事實上假如你malloc了n個字節,超過返回地址的n字節仍能被操作,例如:int *p=(int *)malloc(30*sizeof(int));*(p+100)=10;printf("用的比申請的多,但第一百個仍能用,結果爲:\n",*(p+100));但這是極其危險的。
(2).編譯器在編譯程序時將程序分爲各個段:
代碼段:程序的可執行部分,直觀理解爲多個函數組成。(不可被改變)
數據段(.data):存放程序中的數據,存放那些被顯示初始化爲非0的全局變量和靜態局部變量(static)(普通局部變量不能算程序的數據,只能叫函數的數據,這放在棧裏面)
bss(ZI)段:存放那些被顯示初始化爲0或未被初始化的全局變量(沒被初始化的放在bss段裏就會被初始化爲0);本質上也屬於數據段。
補充:一些特殊的數據會放在代碼段,比如 char* p="badmer";
比如const常量在單片機編譯器被放在代碼段;但是在gcc下還是被放在數據段,由編譯器做檢查。
(3).字符串:
1).字符串的兩種存儲方式:
字符串:char* p="badmer"; printf("p=%s\n",p);
字符數組:char a[]="bademr";printf("a=%s\n",a);
2)字符串與字符數組在存儲上本質區別:
字符串:一個字符指針(棧裏,四字節),他指向內存中(代碼段裏)的一個字符串;字符串用法比較靈活,可定義在棧,堆上。
字符數組:定義一個數組,編譯器用字符串將其初始化然後捨棄掉。
3)他們在sizeof(), strlen()方面區別:
char *p="badmer";
sizeof(p); //4
strlen(p); //6 strlen()函數裏的參數是個字符指針
char a[]="badmer";
sizeof(a); //7
strlen(a); //6
char a[3]="badmer";
sizeof(a); //3
strlen(a); //>=3;;;;;;20
char d[6]="badmer";
printf("%d\n",sizeof(d)); //6
printf("%d\n",strlen(d)); //後面的可能是結束字符,也可能不是。>=6;;;;;;;12
char a[10]="badmer";
sizeof(a); //10
strlen(a); //後面默認初始化爲0,ascll對應的就是'\0',所以是6
strlen():這個函數是檢測遇到了'/0',就結束。對那些你不造後面是啥的,結果也不確定。
int mystrlen(const *p)
{
int cnt=0;
while(*p++ !='\0')
cnt++;
return cnt;
}
(4).結構體對齊:
1).對齊指令:
a. #pragma pack(n) (n=1/2/4/8) #pragma pack() //這兩個是成對出現的,前一個設置n字節對齊,後一個取消對齊,中間位作用域;這個廣泛支持C環境,但gcc下不建議。
b. __attribute__((packed)) //使用時直接放在要進行內存對齊的類型定義的後面,然後它起作用的範圍只有加了這個東西的這一個類型 ;
__attribute__((aligned(n)))使用時直接放在要進行內存對齊的類型定義的後面,然後它起作用的範圍只有加了這個東西的這一個類型。它的作用是讓整個結構體變量整體進行n字節對齊(不是結構體內各元素也要n字節對齊)
2)結構體對齊要考慮:
結構體整體本身必須安置在4字節對齊處,結構體對齊後的大小必須4的倍數(編譯器設置爲4字節對齊時,如果編譯器設置爲8字節對齊,則這裏的4是8)
結構體中每個元素本身都必須對其存放,而每個元素本身都有自己的對齊規則,int型必須放在4倍數字節整數處(short爲2)。
文檔:http://www.cnblogs.com/dolphin0520/archive/2011/09/17/2179466.html
http://blog.csdn.net/sno_guo/article/details/8042332
3)測試代碼:
#include<stdio.h>
#define offsetof(type,member) ((int)&((type *)0)->member)
#define container_of(ptr,type,member) ({const typeof(((type *)0)->member) *__mptr=(ptr);(type *)((char *)__mptr-offsetof(type,member));})
//#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
struct mystruct1
{ //四字節對齊
short a; //4
int b; //4
char c; //4
};
typedef struct
{ //四字節對齊
short a; //2
short b; //2
char c; //2 若爲int c ;那麼就是4
}ms2;
typedef struct
{ //128字節對齊
short a; //2
short b; //2
int c; //124
}__attribute__((aligned(128)))ms3;
int main(void)
{
//測試兩個宏;測試ms1的對齊。
struct mystruct1 my1;
printf("the offset of b is %d\n",offsetof(struct mystruct1,b));
printf("the head pointer is %p\n",container_of(&(my1.c),struct mystruct1,c));
printf("mystruct1 address is %p\n",&my1);
//測試ms2的對齊。
printf("the offset of b is %d\n",offsetof(ms2,b));
printf("the offset of c is %d\n",offsetof(ms2,c));
printf("the sizeof of ms2 is %d\n",sizeof(ms2));
//測試ms3對齊
printf("the offset of b is %d\n",offsetof(ms3,b));
printf("the offset of c is %d\n",offsetof(ms3,c));
printf("the sizeof of ms3 is %d\n",sizeof(ms3));
return 0;
}
(5).結構體相關宏
1).結構體成員的兩種訪問方式本質上都是用指針訪問(./->)。
代碼:struct.c
2)offsetof:
定義: #define offsetof(TYPE,MEMBER) ((int)&((TYPE *)0)->MEMBER)
作用:用宏來計算結構體中某個元素和結構體首地址的偏移量
原理:我們虛擬一個type類型結構體變量,然後用type.member的方式來訪問那個member元素,繼而得到member相對於整個變量首地址的偏移量。
3).container_of
定義:#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
作用:知道一個結構體中某個元素的指針,反推這個結構體變量的指針。有了container_of宏,我們可以從一個元素的指針得到整個結構體變量的指針,繼而得到結構體中其他元素的指針。
原理:先用typeof得到member元素的類型定義成一個指針,然後用這個指針減去該元素相對於整個結構體變量的偏移量(偏移量用offsetof宏得到的),減去之後得到的就是整個結構體變量的首地址了,再把這個地址強制類型轉換爲type *即可
(6).共用體union
1).共用體內的成員對應的是相同的一塊內存單元,不同類型的成員意味着對這一塊內存中內容的不同解析方式;本質上例如:int a=123;printf("%f\n",*((float *)&a));
2).sizeof(union xx)的大小與xx內最大變量的大小相同;其內成員的首地址都相同;
3).共用體的定義,操作方法與結構體類似;
4).大小端模式(只是方式不同):
a.最初是在計算機通信中,由於串口一次只能傳8位,所以通信是按byte 0,1,2,3發送,還是按byte 3,2,1,0發送
b.現在用於存儲時 按高地址存高字節(小端,現在PC,arm一般用這個)還是按高地址存低字節(大端,c51用這個)方式存儲。
兩種方法鑑別大小端:
#include<stdio.h>
typedef union my_union
{
int a;
char b;
}mu;
//返回值爲1時表示是小端模式;爲0時是大端模式;
static int identify_little_endian()
{
mu mu1;
mu1.a=1;
return mu1.b;
}
int main(void)
{
//方法一,,,,本質方法
int j=0;
mu mu2;
mu2.a=1;
j=(int)*((char *)&mu2.b);
if(1==j)
printf("little endian mode.\n");
else
printf("big endian mode .\n");
//方法二,,,,
int i=0;
i=identify_little_endian();
if(1==i)
printf("little endian mode.\n");
else
printf("big endian mode .\n");
return 0;
}
補充:利用位運算移位和位與是不行的,他們始終是操作高/低字節。十分有趣的東西:
typedef union MyTest
{
int a;
double b;
}test;
int main() {
test c;
c.a = 14;
c.b = 12.13;
printf("%d\n",c.a); //1546188227
printf("%d\n", c.b); //1546188227
printf("%lf\n", c.a); //0.000000
printf("%lf\n", c.b); //12.130000
printf("-----------\n");
test d;
d.b = 12.13;
d.a = 14;
printf("%d\n", d.a); //14
printf("%d\n", d.b); //14
printf("%lf\n", d.a); //0.000000
printf("%lf\n", d.b); //12.129997
return 0;
}
還可以嘗試分別賦給a,b與上面結果對比。
printf("-----------\n");
test e;
e.a = 14;
printf("%d\n", e.a); //14
printf("%d\n", e.b); //14
printf("%lf\n", e.a); //0.000000
printf("%lf\n", e.b); //-92559592117432153556203825612673749385392370145622614280765440.000000
printf("-----------\n");
test e;
e.b = 12.13;
printf("%d\n", e.a); //1546188227
printf("%d\n", e.b); //1546188227
printf("%lf\n", e.a); //0.000000
printf("%lf\n", e.b); //12.130000
似乎int是決不可能用%lf解析出來,但是double類型的可用%d解析出來。。。上面僅有先賦double,再賦int能打出兩個值。猜測double能完全覆蓋int的內存空間,int只能部分覆蓋,因爲損失部分精度。
1).枚舉在c語言中就是一個符號常量集(成員間用的是,,不是;了);其中的常量是int類型;一個枚舉變量值爲成員中的某個值;
2).枚舉值常量是全局變量,你可以自己賦值(值不要相同,編譯不出錯,程序會錯),也可以是默認值(默認從0開始。。的異世界,若有自定義的,從自定義的值開始)
3).多個枚舉間,若是有相同的成員(名字)是不可以的。但是宏定義可以,取最後一次值。
4).enum的定義是可以沒有類型直接來變量的;不可存在與成員同樣的全局變量,局部變量卻沒關係(gcc下都沒關係)。(一開始說了咯,成員是全局變量)
5).當我們定義一個常量的可取值是一個有限集合,存在多選一的關係,辣麼就用枚舉;沒關係的、不存在多選一的還是用宏定義吧。
#include<stdio.h>
enum
{
SUN,
MON,
TUE,
WEN,
THU,
FRI,
SAT,
}today,yesterday;
//int SUN=5; // error: ‘SUN’ redeclared as different kind of symbol
int a=12;
int main(int argc,char **argv)
{
// int SUN=10; //沒警告,沒錯誤,值取10
today=SUN; //值取0
int a=13;
printf("today is %d\n",SUN);
printf("%d\n",a);
return 0;
}