(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;
}