CSDN话题挑战赛第2期
参赛话题:学习笔记
本人0基础开始学编程,我能学会的,你也一定可以,学会多少写多少。
下载安装请从官网入手,社区版本即可,这里主要使用的软件是VS2019,图标如下。
上一篇:从0开始学c语言-23-如何写出好(易于调试)的代码、模拟实现库函数:strcpy、strlen 、编程常见错误_阿秋的阿秋不是阿秋的博客-CSDN博客
目录
1. 数据类型详细介绍
数据类型
从0开始学c语言-02-关于数据类型_阿秋的阿秋不是阿秋的博客-CSDN博客
详细的在上面那个文章里
char 1
//
字符数据类型
short 2
//
短整型
int 4
//
整形
long 4
//
长整型
long long 8
//
更长的整形
float 4
//
单精度浮点数
double 8
//
双精度浮点数
|
类型的意义:
1. 使用这个类型开辟内存空间的大小(大小决定了使用范围)。2. 如何看待内存空间的视角——整型和浮点面对同一个二进制序列能够读取出不同的数字。
类型的基本归类
整形家族:
整数:有符号的,无符号的
char |
unsigned char
signed char
|
short |
unsigned short [int]
signed short [int]
|
int |
unsigned int
signed int
|
long |
unsigned long [int]
signed long [int]
|
浮点数家族:
小数:单精度、双精度
float
double
|
构造类型:
特点:自由度大、自定义灵活
> 数组类型
> 结构体类型 struct
> 枚举类型 enum
> 联合类型 union
|
指针类型
int * p;
char * p;
float* p ;
void* p ;
|
空类型:void 表示空类型(无类型)通常应用于函数的返回类型、函数的参数、指针类型空指针不能被解引用,也不能使用 -> 操作符访问结构体成员。
2. 整形在内存中的存储
一个变量的创建是要在内存中开辟空间的。空间的大小是根据不同的类型而决定的。
数据在所开辟内存中到底是如何存储的?
那就需要讲讲各种码了。
.1 原码、反码、补码
原码直接将二进制按照正负数的形式翻译成二进制就可以。反码将原码的符号位不变,其他位依次按位取反就可以得到了。补码反码+1就得到补码。
内存中存放什么码?
对于整形来说:数据存放内存中其实存放的是 补码。
原码 ——> 反码 :符号位不变,其他位按位取反反码 ——> 补码 :+1
补码 ——> 反码 :符号位不变,其他位按位取反反码 ——> 原码 :+1
-1的补码
11111111 11111111 11111111 11111111
-1的反码(符号位不变,其他位按位取反)
10000000 00000000 00000000 00000000
-1的原码(+1)
10000000 00000000 00000000 00000001
可以看到确实是一样的。
我们来看看内存中储存的样子
int a = 1;
//1的补码
00000000 00000000 00000000 00000001
int b = -10;
//-10的原码
10000000 00000000 00000000 00001010
//反码
11111111 11111111 11111111 11110101
//补码
11111111 11111111 11111111 11110110
这是二进制序列,内存中会转化成16进制进行储存
int a = 1;
//16进制
00000001
int b = -10;
//-10的补码
11111111 11111111 11111111 11110110
//16进制
fffffff6
我们调出内存窗口,
然后看一下a和b的储存样子
再看看我们刚刚写的16进制
int a = 1;
//16进制
00000001
int b = -10;
//16进制
fffffff6
.2 大小端
大端小端是啥
大端存储模式,是指数据的低位保存在内存的高地址中,数据的高位保存在内存的低地址小端存储模式,是指数据的低位保存在内存的低地址中,数据的高位 保存在内存的高地址
为什么有大端和小端:
为什么会有大小端模式之分呢?这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8bit。但是在C语言中除了8 bit的char之外,还有16 bit的short型,32 bit的long型(要看具体的编译器),另外,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。
例如:一个 16bit 的 short 型 x ,在内存中的地址为 0x0010 , x 的值为 0x1122 ,那么 0x11 为高字节, 0x22 为低字节。对于大端模式,就将 0x11 放在低地址中,即 0x0010 中, 0x22 放在高地址中,即 0x0011 中。小端模式, 刚好相反。我们常用的 X86 结构是 小端模式,而 KEIL C51 则为大端模式。很多的 ARM , DSP 都为小端模式。有些 ARM 处理器还可以由硬件来选择是大端模式还是小端模式。
判断大小端
我们知道的是,在取地址的时候,取的都是低地址的第一个字节。
然后根据上面a=1的大小端内储存的样子,就可以写出来这段代码啦!
int main()
{
int a = 1;
char* p = (char*)&a;
if (*p == 1)
printf("little");
else
printf("big");
return 0;
}
优化一下,写成函数
int check_sys()
{
union
{
int i;
char c;
}un;
un.i = 1;
return un.c;
}
3·练习出真知
练习1
int main()
{
char a= -1;
signed char b=-1;
unsigned char c=-1;
printf("a=%d,b=%d,c=%d",a,b,c);
return 0; }
先看前三个语句
char a= -1;
signed char b=-1;
unsigned char c=-1;
//-1 的补码
11111111 11111111 11111111 11111111
//截断储存
11111111
printf("a=%d,b=%d,c=%d",a,b,c);
//%d按照有符号整型打印
char a=-1;
11111111 - a
//因为char类型不够int类型的大小,所以要进行整型提升
//整形提升是按照变量的数据类型的符号位来提升的 ,无符号则高位补0
//char是有符号位的
11111111 11111111 11111111 11111111 -a整型提升
因为b也是有符号的char,所以和a是一样的。不再分析。
printf("a=%d,b=%d,c=%d",a,b,c);
//%d按照有符号整型打印
unsigned char c=-1;
11111111 - c
//因为unsigned char类型不够int类型的大小,所以要进行整型提升
//整形提升是按照变量的数据类型的符号位来提升的 ,无符号则高位补0
//unsigned char是无符号位的
00000000 00000000 00000000 11111111 -c整型提升
现在a和b、c的补码都知道了,然后按照%d的形式打印出去。
11111111 11111111 11111111 11111111 -a、b的补码
00000000 00000000 00000000 11111111 -c的补码
c按照%d的方式读取是正数,a和b是负数,所以需要进行原码转换。
11111111 11111111 11111111 11111111 -a、b的补码
10000000 00000000 00000000 00000000 -反码
10000000 00000000 00000000 00000001 -原码
现在看看我们的计算过程是否正确。
1·写出每个变量内存中的补码 - 该截断的就截断。2·如果打印形式(%d、%u)的长度比变量类型(char、short)长,就需要整型提升。3·整型提升看变量类型有无符号位选择按照变量的数据类型的符号位来提升还是无符号补0。4·输出的时候按照输出形式来读取二进制序列。(无符号输出不需要进行正负数的区分)
练习2
int main()
{
char a = -128;
printf("%u\n",a);
return 0; }
按照我们上面的思路来做一下。
1·写出每个变量内存中的补码 - 该截断的就截断。
char a = -128;
10000000 00000000 00000000 10000000 -原码
11111111 11111111 11111111 01111111 -反码
11111111 11111111 11111111 10000000 -补码
10000000 -截断 -a
2·如果打印形式(%d、%u)的长度比变量类型(char、short)长,就需要整型提升。3·整型提升看变量类型有无符号位选择按照变量的数据类型的符号位来提升还是无符号补0。
printf("%u\n",a);
char a = -128;
10000000 -截断 -a
%u比char大,进行整型提升
变量a是char类型,有符号,符号位是1补1
11111111 11111111 11111111 10000000 -a整型提升
4·输出的时候按照输出形式来读取二进制序列。(无符号输出不需要进行正负数的区分)
printf("%u\n",a);
char a = -128;
11111111 11111111 11111111 10000000 -a整型提升
因为%u是无符号整型打印,所以直接输出即可
我用计算机算a的二进制序列= 4294967168
这里不放图了,编译器的结果也是如此,那便证明这样的思路没问题。
练习3
int main()
{
char a = 128;
printf("%u\n",a);
return 0; }
1·写出每个变量内存中的补码 - 该截断的就截断。
char a = 128;
00000000 00000000 00000000 10000000 -原码
01111111 11111111 11111111 01111111 -反码
01111111 11111111 11111111 10000000 -补码
10000000 -截断 -a
2·如果打印形式(%d、%u)的长度比变量类型(char、short)长,就需要整型提升。3·整型提升看变量类型有无符号位选择按照变量的数据类型的符号位来提升还是无符号补0。
printf("%u\n",a);
char a = 128;
10000000 -截断 -a
%u比char大,进行整型提升
变量a是char类型,有符号,符号位是1补1
11111111 11111111 11111111 10000000 -a整型提升
4·输出的时候按照输出形式来读取二进制序列。(无符号输出不需要进行正负数的区分)
printf("%u\n",a);
char a = 128;
11111111 11111111 11111111 10000000 -a整型提升
因为%u是无符号整型打印,所以直接输出即可
你会惊奇的发现,这输出结果和上面的结果一样!
再补充个知识点
有符号char的取值范围
也就是说, 有符号的char输出范围是-128~127
不理解怎么输出的话,我示范一个
无符号char的输出范围是多少呢?
也就是说,有符号的char输出范围是0~255
练习4
int main()
{
int i = -20;
unsigned int j = 10;
printf("%d\n", i + j);
return 0; }
1·写出每个变量内存中的补码 - 该截断的就截断。
int i = -20;
10000000 00000000 00000000 00010100 - i的原码
11111111 11111111 11111111 11101011 - 反码
11111111 11111111 11111111 11101100 - 补码
不需要截断
unsigned int j = 10;
00000000 00000000 00000000 00001010 - j的原码、反码、补码
不需要截断也不需要补码转换,因为无符号整型
2·如果打印形式(%d、%u)的长度比变量类型(char、short)长,就需要整型提升。3·整型提升看变量类型有无符号位选择按照变量的数据类型的符号位来提升还是无符号补0。
printf("%d\n", i + j);
int i = -20;
unsigned int j = 10;
都是int,不需要整型提升
11111111 11111111 11111111 11101100 - i
00000000 00000000 00000000 00001010 - j
11111111 11111111 11111111 11110110 - i+j
4·输出的时候按照输出形式来读取二进制序列。(无符号输出不需要进行正负数的区分)
11111111 11111111 11111111 11110110 - i+j的补码
%d输出,有符号整型,需要进行原码转换运算
10000000 00000000 00000000 00001001 - 反码
10000000 00000000 00000000 00001010 - 原码 等于-10
练习5
int main()
{
unsigned int i;
for (i = 9; i >= 0; i--) {
printf("%u\n", i);
}
return 0;
}
这题不带你做了,你自己试试?
我要开始讲解了哦,你一定要自己思考
首先我们看到了 i 是一个无符号整型变量,在for循环中赋值为9,循环条件是i >=0,满足条件便减一。那么我们就要思考的是,什么时候跳出循环再确定要输出什么。
i 是一个无符号整型变量,循环条件是 i >=0,那就是说如果i想跳出循环,i 跳出循环的时候应该是小于0的,也就是说i 跳出循环的时候应该是负数,但是i 是一个无符号整型变量,是一个没有负数的变量,那就没办法跳出循环了。
截图出来了一些,实际上运行的时候你都看不清是什么数,一直死循环。
包括你也看到了for有个波浪号,我们看看警告,
练习6
int main()
{
char a[1000];
int i;
for(i=0; i<1000; i++)
{
a[i] = -1-i;
}
printf("%d",strlen(a));
return 0; }
先自己思考,我要开始讲了
首先我们看到char a[1000]是一个存放char类型的数组,在for循环中,一直在数组中存放数据,存放好后,strlen函数来求char a[1000]数组的长度,那便是找\0了,而 \0的ASCII码是0,所以遇到0就不再计算长度了。
当 i =0 ,a[0]= -1i =1 , a[1]= -2一直到a[某个数]= -128然后再a[某个数]= 127再到a[某个数]=0再继续循环储存,直到循环结束
-1 -2 -3……-128 127 126 ……3 2 1 0 -1 -2……128 127 126……
最后储存的数据就像上面这样
练习7
unsigned char i = 0;
int main()
{
for(i = 0;i<=255;i++)
{
printf("hello world\n");
}
return 0; }
应该会了吧?已经做过很多同类型的了。
首先unsigned char i的取值范围是0-255,而跳出循环的条件是i>255,是死循环哦~