静态局部变量和动态局部变量区别:
1.静态局部变量属于静态存储类别,在静态存储区分配存储单元,在整个运行期间都不释放。程序退出时才由系统回收。
2.而(动态局部变量属于动态存储类别,占动态存储空间,函数调用后释放。
静态局部变量在编译时赋初值,程序运次时已经有初值,以后每次调用函数不再重新赋初值而只是保留上次函数调用结束时的值。静态局部变量编译时赋初值默认是0;动态局部变量如果不赋初值则它的值是不确定的值。
4.静态局部变量在函数调用结束后仍然存在,但其他函数不能引用它。如果用static修饰全局变量,那么该全局变量不能被其他文件引用。不能再被extern(extern和static都是修饰符)
优点:如果用static修饰函数,那么该函数本文件内可以引用而其他文件不能引用。
静态函数:限制函数使用范围,只有该文件可以使用该函数,用static定义
关键字:
const:
关键字- const
只读
const int* a; int const *a:是一个指向const int型的指针, a所指向的内存单
元不可改写,所以(*a)+ +是不允许的,但a可以改写,所以a+ +是允许的。
int* const a;a是一个指向int型的const指针, *a是可以改写的,但a不允许改写。
int const * const a;a是一个指向const int型的const指针,因此*a和
a都不允许改写
只读,常量不允许改变
int *a a----指向的是地址,*a是取地址的内容
volatile不做优化,再程序运行中,有些代码是不能优化的,如硬件
extern 不做变量定义,只做声明。其他地方定义后,另一方可使用.只需声明则可以使用该变量了
/*fun1.h*/
int a= 5;
extern int b;
/*fun1.h*/
#include "funl .h"
void fun1()
{
print("fun1:a = %d\n" ,a);
printf("fun1:b = %d\n",b);
}
/*fun2.h*/
int b= 6;/
/*fun2.h*/
#include "fun2.h"
void fun2()
{
printf("fun2:a = %d\n" ,a);
printf("fun2:b = %d\n",b);
}
#include<stdio.h>
#include"fun1.h"
#include"fun2.h"
int main()
{
fun1();
fun2();
return 0;
}
file.c
int a;
void main()
{
.......
}
file2.c
extern int a;
int power()
{
.......
}
全局变量extern可以在其他源程序中使用;全局变量static只能在本程序中使用。
全局变量是使用相同的内存块在整个类中存储一个值。
如果static int a 则file2 .c不能引用,因为是静态
全局变量一般拿来坐标志,限制使用并不是不让用。
static int fac(int x)
{
......
}
extern int fac(int x)
{
......
}
上述两个都是定义函数,因为有完整的函数结构,函数头函数尾都有
struct结构体;内存空间只分配给变量不分配给类型,即struct不占空间
struct node
{
char a;
short b;
char c;
};
struct node link;
int count = sizeof(link);
printf( "%d\n" ,count);
struct node
{
char a;
int b;
char c;
};
struct node link;
int count = sizeof(link);
printf( "%d\n" ,count);
两式结果不一样,第一个是6,第二个是12;--------半字对齐和字对齐的不同,a,b,c是link的成员,sizeof求字节
a,b,c作用范围只在{}.只是成员,结构体后面的{}必须有;
使用注意
int main(int argc, char* argv[])
{
struct A instant1,instant2;
char C= 'a';
instant1.iMember = 1;
instant1.cMember =&c;
instant2 = instant1;
printf( "instant1.cMember = %c" ,*(instant1.cMembe));
*(instant2.cMember)='b';
printf( "instant2.cMember = %c" ,*(instant2.cMembe));
printf( "instant1.cMember = %c" ,*(instant1.cMembe));
return 0;
}
#include < stdio.h>
struct A
{
int iMember;
char DcMember;
};
第一个输出为a,第二个输出为b,第三个输出也为b;
分析:将c赋予a,将instant1.cMember=c ,则instant1.cMember为a。
*(instant2.cMember)='b';取地址的内容为赋值为b.则第二条输出语句为b
instant2 = instant1;地址相同,导致现在取地址内容仍然是b
关键字- struct总结:
①结构体名用作结构体类型的标志;
②花括弧内是该结构体中的各个成员,由它们组成一个结构体;在结构体内对各成员都应进行类型声明;
③成员表列”也称为域表。每个成员也称为结构体中的一个域,成员名命名规则与变量名一样;
④每个成员名前的类型标识符可以为已经定义了的任意类型,当然可以是结构体类型标识符,即结构意类型,当然可以是结构体类型标识符,即结构体成员也可以是另一个结构体变量。
⑤此处只是构造出一个新的类型,并没有定义该类型的变量,因此在内存中并没有开辟任何存储空间;
⑥在程序中可以定义多个结构体类型,不同结构体类型用不同的结构体名来区分。
可以把所有的结构体变量放在{}后面:
struct
{
int num;
char name[20];
char sex;
int age;
float score;
char addr[30];
};stu1,stu2,stu3
struct date
{
int year;
int month;
int day;
};
struct student
{
int num;
char name[20];
char sex;
struct date birthday;
char addr[30];
};
成员也可以是一个已经定义过的结构体的成员
struct student
{ int num;
char name[20];
float score;
struct student *next;
};
成员也可以是指向本结构体类型的指针变量:sizeof(struct student) 为4+20+4+8=36
结构体可以嵌套定义
结构体内部只能是自己结构体定义的指针
结构体变量名.成员名是成员运算符,在所有的运算符中优先级最高
-> 取结构体的指针则用该符号
“.”是成员运算符,在所有的运算符中优先级最高。
数组不能是左值:用strcpy(stu1.name, "Li Ming")做修改
结构体不能整体操作:如对上述结构体而言
printf("%d%s%f",stu1);
scanf("%d%s%f",&stu1);上述两句是错的
struct student
{
int num;
char name[20];
float score;
};
struct student stu,*p;
p=&stu;
p中存放着结构体变量stu在内存中的首地址
注意:不能用指向结构体变量的指针指向该结构体变量的某个成员
p =&stu.num; 是错的
int *ip;
ip=&stu.num; 对的,因为ip不是结构体变量的指针
指名初始化必须在{} 里面
初始化结构体数组的时候的格式:
应该注意与之前的格式的区别分号的位置,以及花括号{}出现的次数
数组大小的确定:
1.定义的时候给定大小
2.在定义的时候给出了所有的初始值则可省略大小
用struct的时候可能会造成内存空洞:
如当结构体成员有int 型的时候则以四个字节为单位,即字节对齐
如当结构体成员只有short 型的时候则以两个字节为单位,即半字节对齐
第一个和第二个都是普通的字节对齐
第三个需要注意的是也是字节对齐,但是第一个ch占4字节,num占4字节,ch1和ch2由于同类且不足4字节所以他们共享一个四字节。
double占八个字节,ch3和b共同占有4个
此时占八个
当把ch1改成ch1[10]的时候
ch占4,num占4,. 由于字对齐则应该为4的整数倍则ch1应该占12,实际只占10.所以剩下的由ch2占有。所以一共占12个字节。
字对齐看int 半字对其看short
union 联合体 多选一 通信协议 报文类型字段+报文内容的结构体
#include<stdio.h>
union gong
{
int a;
char c;
};
int main()
{
union gong test; //声明共用体
test.a=1;
if (test.c==0)
{
printf("this is bigendian!\n");
}
else
{
printf("this is littleendian!\n");
}
return 0;
}
可以看出我得电脑是小端字节序列。
枚举默认值为0,赋值则为赋的值。枚举常量后一个值是前一个的加一。如果后一个赋值了则就是赋的值
可以修改数据类型的名字
定义宏函数:
一般情况下,宏体的带参数的形参一般要加阔号
普通函数是会做语法分析的,但宏函数不会做
普通函数有流程转移.
宏定义的##:
如果宏定义以#开头,不展开参数,直接替换。
如果宏定义不以#开头,展开参数,直接替换,由外层向里层,如果碰到的是#开头的宏,不继续往里层展开,往外层展开。由外层向里层,如果碰到的是以非#开头的宏,继续往里层走,直至最里层,开始一层层往外层展开。
##在宏中定义,是字符连接符
如a##b##c 等同于 "abc"
#在宏开头出现,是表示宏展开的方式不同
布尔变量 0 是假 非0是真
提高循环体效率的基本方法是降低循环体的复杂性
{}里面的都是复合语句
a和b 的互换:
a=a+b; b=a-b; a=a-b;
a=a^b; b=a^b; a=a^b;
t=a; a=b; b=t;
在多重循环中,如果可以,应将最长的循环放在最内层,最短的循环放在最外层,以减少CPU跨切循环层的次数。如果循环体内存在逻辑判断,且循环次数很大,宜将逻辑判断移到循环体的外面
switch都要加上break;且就算不用写 defualt: break; 也最好写上
分析:输入1个数。然后往左移构造成100000(31个0)赋值给mask;循环32次,按照与运算输出1或0.因为位与运算,可以输出该num的第一位。然后在左移该num将第二位移到第一位,通过循环输出每一位。再每四位的时候用逗号隔开。且最后一个逗号的位置给出回退,消去此逗号。