一.数据溢出
思考下面的代码
#include <stdio.h>
int main(int argc,char **argv)
{
//无符号整型数
unsigned char a = 256; //0~255(0~2^8-1)
printf("a = %d\n",a);
unsigned char b = -1; //0~255(0~2^8-1)
printf("b = %d\n",b);
return 0;
}
结果:
发现结果和预期的不一样
1.概念
由于数据超出了规定的范围,溢出;导致输出的数据异常。
为了计算数据溢出所得到的值,可以通过图示两种方法之一来计算;平时写代码的时候不能这样赋值。
方法一:画个钟(送个钟)
方法二:手动画出范围 数数
2.char类型(1B=8bit)
用上述两种方法理解
unsigned char 的范围:0~255 0~2^8-1
unsigned char a = 256
printf("a=%d\n",a); //0
unsigned char a = -1
printf("a=%d\n",a); //255
signed char 的范围:-128~127 -2^7~2^7-1
signed char a = 128 //超出了最大值127一个数,就往左边数一个数
printf("a=%d\n",a); //-128
signed char a = -130 //超出了最小值-128两个数,就往右边数两个数
printf("a=%d\n",a); //126
说明:
年龄、车速(120)、分数;像这种数据就可以用一个char类存储,没必须用int来存储;目的就是省空间
3.short类型(2B=16bit)
unsigned short的范围:0~65535 0~2^16-1
unsigned short e = 65538;
printf("e=%d\n",e); //2
signed short的范围:-32768~32767 -2^15~2^15-1
signed short f = -32770;
printf("f=%d\n",f); //32766
说明:
如果数据类型前面没有加那个修饰符的时候,都默认为有符号类型
char a=110等价于signed char a=110
讲解代码
#include <stdio.h>
/*
1-char溢出.c:6:23: warning: large integer implicitly truncated to unsigned type [-Woverflow]
unsigned char a = 256;
256超出了a的值的范围;导致输出的结果异常:数据溢出(是一种错误的写法)
*/
int main(int argc,char **argv)
{
/*
char
*/
//无符号整型数
unsigned char a = 256; //0~255(0~2^8-1)
printf("a = %d\n",a); //0
unsigned char b = -1; //0~255(0~2^8-1)
printf("b = %d\n",b); //255
signed char c = 128; //-128~127(-2^7~2^7-1)
printf("c = %d\n",c); //-128
signed char d = -130; //-128~127(-2^7~2^7-1)
printf("d = %d\n",d); //126
int e = 110;//e占用了4个字节
char f = 110;//f占用了1个字节 节省了内存空间(这种定义也OK)
/*
short
*/
unsigned short g = 65538;//(0~65535: 0~ 2^16-1)
printf("g=%d\n",g); //2(超过了65535一共3个数 往回数3个数)
signed short h = -32770;//(-32768~32767: -2^15 ~2^15-1)
printf("h=%d\n",h); //32766(超过了-32768一共2个数 往前数2个数)
/*
不加修饰符
*/
//前面没有加修饰的时候默认的就是signed有符号的
char i = 128; //等价于 signed char i = 128也是数据溢出了
printf("i=%d\n",i);
return 0;
}
练习:
char a=155;
计算过程:
char的范围:-128~127
155-127 = 28超过了28
(x-(-128))/1+1 = 28 --- >x=-101 等差算法计算最终值
unsigned char b=-8;
unsigned char: 0~255
计算过程:求x值
(255-x)/1+1 = 8 --->x = 248 (等差算法)
short c= 32780; (没有加修饰符signed 默认就是signed有符号)
short:-32768~+32767
计算过程:求x值 (超过了32767一共13个)
(x-(-32768))/1+1 = 13--->x = -32756
unsigned short d=-8;
unsigned short:0~65535
计算过程:求x值 (超过了0一共8个)
(65535-x)/1+1 = 8--->x = 65528
说明:计算int类型的时候没有必要把它转换成十进制来算
unsigned int e = -5; (int类型的数据溢出比较特殊%u)
计算过程:求x值 (超过了0一共5个)
0~2^32-1
(2^32-1 -x)/1+1 = 5---->x = 2^32-5等差算法计算最终值
二、浮点型----(格式控制符:%f)
1.分类
float:单精度浮点型(4B)
double:双精度浮点型(8B)
说明:
小数点后面默认是6位
printf("a=%f\n",a); //小数默认是6位
printf("a=%.2f\n",a); //小数点后面2位
#include <stdio.h>
int main(int argc,char **argv)
{
float f = 3.14; //单精度浮点数
printf("f = %f\n",f); //f = 3.140000 打印的时候小数点后面默认是6位
printf("f = %.2f\n",f); //保留小数点后面两位
double f1 = 1.2345; //双精度浮点数
printf("f1 = %f\n",f1); //f1 = 1.234500
printf("f1 = %.2f\n",f1);//保留小数点后面两位
float f2 = 123.456;
//右边对齐 整个数占10个字节,小数点后面保留2位
printf("f2 = %10.2f\n",f2); //保留小数点后2位
//左边对齐 整个数占10个字节,小数点后面保留2位
printf("f2 = %-10.2f\n",f2); //保留小数点后2位
return 0;
}
练习:字节对齐
打印如下对其的格式运用上述方法(都是int类型数据)
11 123
123 1234
1234 12345
11 123
123 1234
1234 12345
参考代码
#include <stdio.h>
int main(int argc,char **argv)
{
//%-10d左对齐10个字节
printf("%-10d%-10d\n",11,123);
printf("%-10d%-10d\n",123,1234);
printf("%-10d%-10d\n",1234,12345);
//%10d右对齐10个字节
printf("%10d%10d\n",11,123);
printf("%10d%10d\n",123,1234);
printf("%10d%10d\n",1234,12345);
//-10整个浮点数10个字节 小数点后2位
//%-10.2f
//10 整个浮点数10个字节 小数点后2位
//%10.2f
return 0;
}
拓展
#include <stdio.h>
int main(int argc,char **argv)
{
//左对齐,\t的用法相当与Tab键
printf("%d\t%d\t\n",11,123);
printf("%d\t%d\t\n",123,1234);
printf("%d\t%d\t\n",1234,12345);
return 0;
}
2.浮点型的精度范围
三、字符型 ---- (%c)-----》linux C语言
1.字符型数据在计算机是如何存储的
计算机里面所有的数据都是以0和1来存储的(字符也不例外),将字符型数据转换成二进制的这一个映射的过程称之为ascii表
2.ascii表(一共有128个字符0~127)
ascii表的常规表现形式(最常见)
八进制 十进制 十六进制 字符
ascii表的另一种表现形式
(横向60和纵向5组成65 来查找'A'这个字符)
说明:ascii表中小写和大写不是同一个字符
141 97 61 a (记住)
101 65 41 A (记住)
4.格式控制符(%c)
char c = 'a'; //字符型数据用''单引号
printf("c=%c\n",c); //'a'
printf("c=%d\n",c); //97
练习:
字符型数据和整形数据混合运算
char c1 = 'a';
char c2 = 10;
char c3 = 'b';
%c+%c = %c c1+c3=%c
用这种语句来表示上面的表达式printf("%c+%c = %c\n",c1,c2,(c1+c2));
%c+%c = %d c1+c3=%d
%c+%d = %c c1+c2=%c
%c+%d = %d c1+c2=%d
说明:
字符型运算的时候,切记值的范围要在0~127之间,才能打印出相应的字符
参考代码
#include <stdio.h>
int main(int argc,char **argv)
{
//定义变量
char c1 = 'a'; //97
char c2 = 10; //10在ascii表中是换行符
char c3 = 'b'; //98
//a+b = 乱码(超出了范围)
printf("%c+%c = %c\n",c1,c3,(c1+c3));
//a+10 = 107
printf("%c+%c = %d\n",c1,c2,(c1+c3));
//a+10 = k
printf("%c+%c = %c\n",c1,c2,(c1+c2));
return 0;
}
结果
四、字符串(C语言里面没有字符串类型数据C++里面是有)---%s
1.字符串类型数据定义的两种方式
1)字符数组:
char s[] = "hello hesuan";
char s[20] = "hello hesuan";
说明:
字符数组定义的时候可以为空,系统同根据你的赋值来确定它的大小(建议使用这种)
如果定义的时候,规定大小;那么赋值的时候字符串中字符的个数一定要小于规定的大小,否则越界警告
2)字符指针:
char *s= "hello gz2273";
3)格式控制符---%s
printf("s=%s\n",s);
#include <stdio.h>
int main(int argc,char **argv)
{
/*
方法一:字符数组(最常用 最保险的方法)
*/
//s[]系统自动识别字符串的大小
char s[] = "hello bling";
//按照数组的形式来定义如下:(很麻烦:注意结尾)
char s3[] = {'h','e','l','l','o',' ','b','l','i','n','g','\0'};
printf("s3 = %s\n",s3);
//s1[20]自己固定了字符串的大小20个字节(注意:赋值的时候不能超过20个字节)
char s1[20] = "hello gz2425";
printf("s = %s\n",s);
printf("s1 = %s\n",s1);
/*
方法二:字符指针(本质上字符串是一个指针)(建议少用这种定义的方法)
*/
char *s2 = "hello 123456";
printf("s2 = %s\n",s2);
return 0;
}
2.字符串在内存中所占的大小和有效长度
去man手册里面搜索strlen的用法查找它的头文件 man 3 strlen
strlen:计算字符串的有效长度
sizeof:计算存储空间的大小
教学代码
#include <stdio.h>
#include <string.h>
int main(int argc,char **argv)
{
//采用优先级1
char s1[] = "hello world"; //此处定义的时候空间就给了12个字节(末尾自动补'\0')
//上面等价的写法(平时不会这么定义 麻烦)
/*
char s1[] = {'h','e','l','l','o',' ','w','o','r','l','d','\0'};
记住如果是这种写法后面一定要加'\0结束符
字符串在内存是以'\0'为结束符
"hello world"这个字符串在内存中存储的方式如下
‘h’‘e’‘l’‘l’‘o ’‘ ’‘w’‘o’‘r’‘l’‘d’‘\0’
*/
//采用优先级2
char s2[20] = "hello world";
//采用优先级3
char *s3 = "hello world";
char *s4 = "hello world123456789"; //sizeof(s4) = 8(64位机)
/*
7-str字符串的长度和大小.c:7:19: warning: initializer-string for array of chars is too long
char s2[10] = "hello world";
注意:固定大小的时候,赋值不能超过它的范围
*/
//char s2[10] = "hello world";
/*
计算字符串(有效字节)长度的函数:strlen
*/
printf("strlen(s1) = %ld\n",strlen(s1)); //11
/*
计算字符串所占空间的大小的运算符"函数":sizeof
*/
printf("sizeof(s1) = %ld\n",sizeof(s1) ); //12
/*
计算s2[20]的大小
*/
printf("strlen(s2) = %ld\n",strlen(s2)); //11
printf("sizeof(s2) = %ld\n",sizeof(s2)); //20
/*
计算指针s3
*/
printf("strlen(s3) = %ld\n",strlen(s3)); //11
//此处sizeof(s3)计算的是指针的大小不是字符串的大小(指针的大小在64位机里面是8个字节)
printf("sizeof(s3) = %ld\n",sizeof(s3)); //8
return 0;
}
五、常量和变量
1.常量
int a = 10; //10是一个常量,它的类型是int 整形常量
char b = 'k'; //10是一个常量,它的类型是char 字符常量
float c = 3.14;//3.14是一个常量,它的类型是float
char s[] = "hello world"; // "hello world"是一个常量(字符串常量是存在于数据段中rodata)
2.变量
1)字母、数字、下划线组成
2)不能以数字开头
3)不能用关键字定义变量
4)定义变量的时候要望文生义
举例说明:
int a1 //对
int 2b //错
char c_3 //对
short rgb2yuv //对 rgb_to_yuv(将rgb格式转换为yuv格式)
int _d //对
int goto //错 关键字
六、以内存的思想来理解变量
int a = 250;
1.常规理解:
定义了一个变量a,它的类型是int类型;然后给它赋值为250;250是整形常量,a是整形变量
2.从内存的角度理解:
在内存中申请了4个字节的连续的存储空间,然后用变量名a来访问这段连续的存储空间
long b = 110;(64位机器)
在内存中申请了8个字节的连续的存储空间,然后用变量名b来访问这段连续的存储空间
说明:
这段连续的存储空间是由系统自动分配
验证是否连续需要用到指针(暂时先放一放,等到后面指针的时候再研究)
七、类型转换
- 概念:不一致但相互兼容的数据类型,在同一表达式中将会发生类型转换。
- 转换模式:
- 隐式转换:系统按照隐式规则自动进行的转换(不建议)
- 强制转换:用户显式自定义进行的转换(建议)
- 隐式规则:从小类型向大类型转换,目的是保证不丢失表达式中数据的精度
隐式转换示例代码
char a = 'a';
int b = 12;
float c = 3.14;
float x = a + b - c; // 在该表达式中将发生隐式转换,所有操作数被提升为float
建议写法
float x = (float)a+(float)b-(float)c;
说明:
开发者写代码的时候尽量不要用这种隐士转换;很容易让别人产生误解。
- 强制转换(显示转换):用户强行将某类型的数据转换为另一种类型,此过程可能丢失精度
char a = 'a';
int b = 12;
float c = 3.14;
float x = a + b - (int)c; // 在该表达式中a隐式自动转换为int,c被强制转为int
不管是隐式转换,还是强制转换,变换的都是操作数在运算过程中的类型,是临时的,操作数本身的类型不会改变,也无法改变。开发者建议使用强制类型转换(显示转换);