CLASS1 基本数据类型:
1.数据类型:固定内存大小的别名。是创建变量的模子。用于声明变量(4个字节大小的数据类型为int )
Char:一个字节
Short:两个字节
Int:四个字节
2.变量:一段实际连续存储空间的名字。通过变量来申请并且命名存储空间。变量的内存取决于其所属的数据类型。(3000-3004的存储空间的名字为变量i)知道存储空间名字之后,便可方便使用指定的内存空间。
3.指针:也是变量,存储的是另一个变量的地址
4.变量与类型的区别:
Sizeof(i/int):得到变量/数据类型所占的内存大小
打印出来:4,4
例2:typedef定义数据类型的关键字:
#include typedef int INT32;//INT32是新的数据类型,由int定义,即可代替int typedef unsigned char BYTE;//同理BYTE也是新的数据类型,由unsigned char定义 typedef struct _tag_ts//_tag_ts是啥不了解,等复习了结构体再更改。 { BYTE b1; INT32 i; short s; }TS;//TS代表上述结构体
int main() { INT32 i32; BYTE b32; TS ts;//声明ts变量为结构体 printf("%d,%d\n",sizeof(INT32),sizeof(i32)); printf("%d,%d\n",sizeof(BYTE),sizeof(b32)); printf("%d,%d\n",sizeof(TS),sizeof(ts)); getchar(); return ; } |
CLASS2有符号和无符号
符号位:数据类型的最高位,用于标识数据的符号。
最高位为1,数为负数
最高位为0,数为正数
练习:
#include int main() { int sign=0; char i=-5;//char一个字节的内存,8位,数字二进制存储,-5记为11111001,规则看下一知识点 short j=5; int k=-1; sign=(i&0x80);//0x为十六进制,转化为二进制10000000,&按位与,即i为负数,则结果不为零,i为正数,结果为0 printf("%d",sign); getchar(); return 0; } |
有符号数在计算机内部用补码表示:正数的补码是本身,负数补码是负数的各位取反后加1。
例2,表示7或-7:
7->111->00000111
-7->11111000+1->11111001
无符号数在计算机内部用原码表示,因为默认为正数,没有符号位。
无符号数最小为0。最大值受他所占的位数决定。
所以MAX_VALUE+1=MIN_VALUE或MIN=MAX-1
C语言中,变量默认为有符号数,unsigned声明变量为无符号数,signed表示有符号数。
只有整数类型(int,short,long)能够声明unsigned变量,浮点数不行。
#include int main() { int i;//默认为带符号整形 unsigned int j;//声明变量为无符号整形 signed int k;//声明变量为有符号整形 return 0; } |
例:求结果
#include int main() { unsigned int i=5; int j=-10; if((i+j)>0) { printf("i+j>0"); } else { printf("i+j>0"); } getchar(); return 0; } |
结果:!!!!!
无符号数和有符号数进行运算,有符号数自动转化为无符号数进行运算,结果为无符号数,一定大于零(-10为int(4字节,32位),转为32位2进制,同时负数首位为1,数字愈发大,为111111111。。。。。。。)
例:
#include int main() { unsigned int i=0; for(i=9;i>=0;i--) { printf("%u\n",i); } } |
结果:无限个该些值
原因:无限:for的判断条件i>=0,无符号数最小值为0,无限满足条件。
为4294-----等数字,无符号数变为0后-1就变为最大值(最大值为4个字节(32位)的全1值)因为判断条件的设定,导致错误的使用了unsigned。因此需要慎重考虑有符号和无符号数。
CLASS3浮点数
浮点数在内存中的存储方式:符号位,指数,尾数,与int(整数)内存表示不同
|
符号位 |
指数 |
尾数 |
Float(4字节) |
1位 |
8位 |
23位 |
Double(8字节) |
1位 |
11位 |
52位(0-51) |
Float和double,分别表示不同的数值范围和精度
浮点数的转换:
注意:计算指数时需要加上偏移量,
例:对于指数6,偏移后的值:
Float :127(固定值)+6——>133
Double:1023+6——1029
例:实数8.25在内存中的float表示:
8.25二进制表示:1000.01(0.25——4的-1次)科学记数法表示——>1.00001*(2^3),二进制,后退几个小数点,2的几次。得到指数3.
符号位:0(为正数)
指数:127+3——>130——>10000010(转成8位2进制)
小数00001
所以在内存中的存储为;
例:编程实验:10进制浮点数的内存表示
#include int main() { float f=8.25; unsigned int *p=(unsigned int *)&f;//定义指针p,指向f的首地址 printf("0x%08X\n",*p);//16进制的8位,p指针指向的 getchar(); return 0; } |
注意:int类型范围[-2^31,2^31-1]
Float类型范围[-3.4*10^38,3.4*10^38]
Int和float都占4个字节内存,但是float比int范围大(浮点数表示范围更大):
原因:
例验证float的不精确性
#include int main() { float f=3.1415f; float f1=123456789; printf("%0.10f\n",f);//打印到小数点后10位 printf("%0.10f\n",f1); getchar(); return 0; } |
总结:
CLASS4类型转换
Long l=800; Int i=(int) l; |
(1)语法:(Type)name
(2)转化的结果:目标类型能够容纳目标值:结果不变(short转化为int,
两个字节转为四个字节)
目标类型不能容纳目标值:结果产生截断(int转为char,四个字节转一个字节,把int的高三个字节扔掉了,只剩最低一字节)
(3)实例分析:
#include struct TS { int i; int j; };
struct TS ts;
int main() { short s=0x1122; char c=(char)s;//s为二字节,char一字节,保留低位,16进制占4位0.5个字节,所以保留两位16进制,c的结果0x22 int i=(int)s;//不会截断,0x1122 int j=(int)3.1415;//float类型,转为int,直接为3 unsigned int p=(unsigned int)&ts;//把ts的地址转为无符号整数,64位电脑64位的地址,8位转为4位,产生截断 long l=(long)ts;//编译错误,无法将结构体转为基本类型 ts=(struct TS)l;//编译错误,无法将基本类型转化为结构体 return 0; } |
Short s=800; Int i=s; |
(安全的转换方向char,short—int—unsigned int—long—unsigned long—float—double)牢记何种转换安全!!
算术运算中,低类型转为高类型(如char和short进行运算,先char转short)
赋值表达,表达式的值转为左边变量的类型
函数调用,实参转化为形参的类型
函数返回值,return表达式转化为返回值
#include
int main() { char c='a'; int i=c; unsigned int j=0x11223344; short s=j; printf("c=%c\n",c); printf("i=%d\n",i);//从char到int,是安全的,值为a printf("j=%x\n",j);//该数为4个字节,转化为4个字节的无符号整数,不会产生截断 printf("s=%x\n",s);//4字节到2字节,发生截断,3344 getchar(); return 0; } |
结果:
总结
转换可能产生截断
转换不区分类型的高低,但是可能产生结果的错误
转换不成功,编译器给出错误信息
低类型转高类型安全,反之不安全
CLASS5变量属性
1.变量的属性:定义变量时,加上属性关键字
2.属性关键字指名变量特有意义
使用语法
#include int main() { auto char i; register int j; static long k; extern double m;
return 0; } |
3.关键字的分类:
(1)Auto关键字:auto是c语言中局部变量的默认属性,存储在栈上。
示例:
#include int main() { int i;//局部变量默认属性为auto auto int j;//声明auto属性 return 0; } |
(2)register关键字:将局部变量存储于寄存器中,即为寄存器变量。
1)为什么要用寄存器变量:寄存器比内存速度要快的多多多。
2)Register只是请求寄存器变量,但不一定请求成功(寄存器有限)。
3)不能用&云算法获取register变量的地址,&只能获得内存的地址,不能获得寄存器的地址
例:
#include register int g_v;//错误,在intmain外,全局变量,register只可用于声明局部变量 int main() { register char var;//可以成为寄存器变量,具体要看是否有寄存器 print("0x%08X",&var); return 0; } |
(3)static关键字:指名变量的“静态”属性,将局部变量存储在程序静态区(从栈移到静态区,与全局变量的生命周期相同,但只是工作在局部)
补充:全局变量与静态变量:
#include int g_v;//全局变量,(1)程序任意地方均可访问(2)存储在程序的静态区,生命周期为程序运行到结束 static int g_vs;//全局变量,设为静态,只有当前文件中可以访问
int main() { int var;//局部变量,auto关键字,在栈上分配空间 static int svar;//局部变量,从栈移到静态区,生命周期改变 return 0; } |
1)static还具有“作用域限定符”的意义(static修饰的全局变量作用域只是在声明的文件中,修饰的函数只是在声明的文件中)
2)实例分析:
#include int f1() { int r=0;//r是auto的局部变量,在栈上分配空间,调用返回之后,局部变量消失 r++; return r; } int f2() { static int r=0;//唯一不同,r是static的静态局部变量,在静态区分配空间,调用返回之后,局部变量不消失 r++; r++; return r; }
int main() { auto int i=0;//声明i为auto,i为栈变量 static int k=0;//局部变量k的存储区在静态区,作用域在main register int j=0;//向编译器申请将j存储在寄存器中
printf("%p,",&i); printf("%p,",&k); printf("%p,",&j);//error,&只能返回内存的地址
for(i=0;i<5;i++)// { printf("%d,",f1()); } for(i=0;i<5;i++) { printf("%d,",f2()); } getchar();
return 0; } |
分析;(1)I和k相邻定义,但是地址相差远:一个存在栈上,一个存在静态区。若存在同一个位置,则存储在相邻位置 。
(2)输出的11111,12345差异: 第一个r是auto的局部变量,在栈上分配空间,调用返回之后,局部变量消失。第二个r是static的静态局部变量,在静态区分配空间,调用返回之后,局部变量不消失,生命周期是程序的运行周期。
(4)extern关键字
1)声明“外部”定义的变量和函数(在文件的其他地方分配空间,定义)
2)用于告诉编译器用c方式编译
extern "c" { int f(int a,int b) { return a+b; } } |
3)编程实验:static和extern使用
#include
extern int g_i;//告诉程序g_i在其他位置 int main() { printf("%d\n",g_i); return 0; }
int g_i;//全局变量定义在main后,需要extern提前声明
|
分析:如果全局变量在main后定义,会报错,需要在main前声明为extern
总结:1.auto变量存储在程序的栈,为默认属性
2.static存在程序静态区,作用域限定符,作用域缩小在文件中
3.register存储在寄存器中
4.extern的变量在程序其他位置分配空间,可以指示c++程序中,以c方式编译程序。
CLASS6分支语句
1.if语句:
1)if语句可独自存在
2)else不能单独存在,且与其最近的if相匹配
3)else语句后可以连接其他if语句
4)if语句中与0点的注意点:bool类型的变量直接出现在条件中,不要进行比较(if(b));
变量和0比较,0出现在符号左边,if(0==b),防止写成0=b;float变量不能直接进行0值比较,需要定义精度,因为float浮点数不精确。#define e 0.00001,float f=0.0 , if((f>=-e)&&(f<=e))
2.switch:
1)switch语句对应,单个条件,多个分支的情况。
2)case语句分支下必须有break,否则会造成分支重叠
3)最后必须加上default.
switch(expression) { case CONST_1: //code break;//不加break则将后续Case内容都输出 case CONST_2: //code break; default: //code } |
Case语句中的值只能是整形或字符型
Case语句的排列顺序:按数字或字母顺序排列,正常放前面,异常放后面。
实例:
#include void f1(int i) { if(i<6) { printf("failed\n"); } else if((i>=6)&&(i<8)) { printf("good\n"); } else//与上一个if匹配,相邻最近 { printf("error"); } }
void f2(char i) { switch(i) { case'c'://只能为常量,不可为变量,字母用单引号 printf("correct\n"); break; case'e': printf("error\n"); break; case'r': printf("run\n"); break; default: printf("unknow"); break; } }
int main() { f1(5); f1(7); f2('c'); getchar(); return 0; } |
结果:
编程:将上面的switch和if对调
If块修改为,有点绕
switch(i<6) { case(1): printf("failed\n"); break; default: switch((i>=6)&&(i<8)) { case(1): printf("good\n"); break; default: printf("error"); break; } } |
Switch块修改为
void f2(char i) { if('c'==i)//注意写法,常量写在左边,bug的主要来源,反写可能误写为赋值语句 { printf("correct\n"); } else if('e'==i) { printf("error\n"); } else { printf("unknow"); } } |
说明if和switch可以互换
小结:
CLASS7循环语句
(1)do——while:
格式:(先do在判断条件,至少使用一次)
do { //loop } while(condition) |
(2)while
while(condition) { //loop } |
(3)for
for(i=0;condition;i++) { //loop } |
4.分别用三个办法实现累加从0到n的功能
考虑问题的全面性!!!!!
考虑正负问题:正数正常操作,负数输出
(1)do-while
#include int f1(int n) { int ret=0; if(n>0)//n大于0才执行 { do { ret=ret+n; n--; } while(n>=0);//注意为小括号,非大括号 } return ret; }
int main() { printf("%d",f1(-2)); getchar(); return 0; } |
结果:-2输入, ,也正确
发现,在do外还需要添加if语句,较为复杂
(2)while
int f1(int n) { int ret=0; while(n>0) { ret=ret+n; n--; } return ret; } |
无需插入if语句,while相对于do-while更合适
(3)for循环
int f1(int n) { int ret,i; for(ret=0,i=0;i<=n;i++) { ret=ret+i; } return ret; } |
5.break和continue的区别
1)Break表示终止循环的执行
Continu表示终止本次循环,进入下一次循环
Switch能否用continue?:不可以!!只能用在循环语句中
2)实例分析:do与break的妙用,工程中重要作用
#include #include
int func(int n) { int i=0; int ret=0; int *p=(int*)malloc(sizeof(int)*n);//指针p动态的指向一堆空间,堆空间大小为传进来的n
do { if(NULL==p) break; if(n<5) break; if(n>100) break; for(i=0;i<n;i++) { p[i]=i; printf("%d\n",p[i]); } ret=1; }while(0);//循环条件为0,表示只执行一次循环体!do-while循环的作用:有些break之后,强制执行free,释放空间
free(p);//break后进入,释放p指针 return ret; }
int main() { if(func(10)) { printf("ok"); } else { printf("error"); } getchar(); return 0; } |
与下一代码进行比较!!
#include #include
int func(int n) { int i=0; int ret=0; int *p=(int*)malloc(sizeof(int)*n);//指针p动态的指向一堆空间,堆空间大小为传进来的n
//do //{ if(NULL==p) ret; if(n<5) return ret; if(n>100) return ret; for(i=0;i<n;i++) { p[i]=i; printf("%d\n",p[i]); } ret=1; //}while(0);//循环条件为0,表示只执行一次循环体!do-while循环的作用:有些break之后,强制执行free,释放空间 printf("free p"); free(p);//break后进入,释放p指针 return ret; }
int main() { if(func(1)) { printf("ok"); } else { printf("error"); } getchar(); return 0; } |
输入1,结果:
发现没有printf(“free p”),没有释放指针空间,造成内存的泄露!!!
所以说:do-while是很好的防止内存泄露的方法。
小结: