数据在内存中的存储
一、基本的内置类型:
char - 字符数据类型
short - 短整型
int - 整形
long - 长整型
long long - 更长的整形
float - 单精度浮点数
double - 双精度浮点数
整型:
1.char
unsigned char
signed char
2.short
unsigned short [int]
signed short [int]
3.int
unsigned int
signed int
4.long
unsigned long [int]
signed long [int]
浮点数:
1.float
2.double
构造类型:
1.数组类型
2.结构体类型 struct
3.枚举类型 enum
4.联合类型 union
指针类型:
1.int pi;
2.char pc;
3.float pf;
4.void pv;
空类型:
1.void 表示空类型(无类型)
2.通常应用于函数的返回类型void test()、函数的参数void test(void)、指针类型void* p
二、整形在内存中的存储
数据在内存中,以二进制的形式存储,对于整数来说,其二进制分3种表示形式,即原码、反码和补码。三种表示方法均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”.
正整数:原、反、补码都相同。
负整数:三种表示方法各不相同。
原码:直接将二进制按照正负数的形式翻译成二进制就可以。
反码:将原码的符号位不变,其他位依次按位取反就可以得到了。
补码:反码+1就得到补码。
int main()
{
int a = -10;
//原码:10000000000000000000000000001010
//反码:11111111111111111111111111110101
//补码:11111111111111111111111111110110
return 0;
}
说明:整数在内存中,存的是补码。
在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。
大端(存储)模式:是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中。
小端(存储)模式:是指数据的低位保存在内存的低地址中,而数据的高位,,保存在内存的高地址中。
练习:设计一个小程序来判断当前机器的字节序
int check_sys()
{
int a = 1;
char* p = (char*)&a;
return *p;
}
int main()
{
int ret = check_sys();
if (ret == 1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}
练习:输出什么?
int main()
{
char a = -1;
//10000000000000000000000000000001
//11111111111111111111111111111110
//11111111111111111111111111111111
//11111111(存储)
//11111111111111111111111111111111(整型提升)
signed char b = -1;
//11111111
//11111111111111111111111111111111(整型提升)
unsigned char c = -1;
//11111111
//00000000000000000000000011111111(整型提升)
printf("a=%d,b=%d,c=%d", a, b, c);
return 0;
}
补充:
1.char是signed char还是unsigned char C语言标准没有规定,取决于编译器
2.int是signed int;short也是signed short。
int main()
{
char a = -128;
//10000000000000000000000010000000
//11111111111111111111111101111111
//11111111111111111111111110000000
//10000000
printf("%u\n", a);
//11111111111111111111111110000000
return 0;
}
int main()
{
char a = 128;
//0000 0000 0000 0000 0000 0000 1000 0000
//1000 0000
//1111 1111 1111 1111 1111 1111 1000 0000
printf("%u\n", a);
return 0;
}
补充:signed char 00000000~11111111 -128~127
10000000- -128
unsigned char 00000000~11111111 0~255
int main()
{
int i = -20;
//1000 0000 0000 0000 0000 0000 0001 0100
//1111 1111 1111 1111 1111 1111 1110 1011
//1111 1111 1111 1111 1111 1111 1110 1100
//1111 1111 1111 1111 1111 1111 1110 1100
unsigned int j = 10;
//0000 0000 0000 0000 0000 0000 0000 1010
printf("%d\n", i + j);
//按照补码的形式进行运算,最后格式化成为有符号整数
//1111 1111 1111 1111 1111 1111 1111 0110
return 0;
}
int main()
{
unsigned int i;
for (i = 9; i >= 0; i--)
{
printf("%u\n", i);
}
return 0;
}
结果:死循环
int main()
{
char a[1000];
int i;
for (i = 0; i < 1000; i++)
{
a[i] = -1 - i;
}
printf("%d", strlen(a));
return 0;
}
unsigned char i = 0;
int main()
{
for(i = 0;i<=255;i++)
{
printf("hello world\n");
}
return 0; }
结果:死循环
三、浮点型在内存中的存储
常见的浮点数:
3.14159
1E10
浮点数家族包括: float、double、long double 类型。
浮点数表示的范围:float.h中定义
int main()
{
int n = 9;
float* pFloat = (float*)&n;
printf("n的值为:%d\n", n);
printf("*pFloat的值为:%f\n", *pFloat);
*pFloat = 9.0;
printf("num的值为:%d\n", n);
printf("*pFloat的值为:%f\n", *pFloat);
return 0;
}
说明:浮点数和整数在内存中的存储存在区别
浮点数存储规则:
根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数V可以表示成下面的形式:
-
-(-1)^S * M * 2^E
-
(-1)^s表示符号位,当s=0,V为正数;当s=1,V为负数。
-
M表示有效数字,大于等于1,小于2。
-
2^E表示指数位。
对于32位的浮点数,最高的1位是符号位s,接着的8位是指数E,剩下的23位为有效数字M。 对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M。
IEEE 754对有效数字M和指数E,还有一些特别规定。
由于 1≤M<2 ,也就是说,M可以写成 1.xxxxxx 的形式,其中xxxxxx表示小数部分。
IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。比如保存1.01的时候,只保存01,等到读取的时候,再把第一位的1加上去。这样做的目的,是节省1位有效数字。以32位浮点数为例,留给M只有23位,将第一位的1舍去以后,等于可以保存24位有效数字。
首先,E为一个无符号整数(unsigned int)
这意味着,如果E为8位,它的取值范围为0~255;如果E为11位,它的取值范围为0-2047。但是,我们知道,科学计数法中的E是可以出现负数的,所以IEEE 754规定,存入内存时E的真实值必须再加上一个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。比如,2^10的E是10,所以保存成32位浮点数时,必须保存成10+127=137,即10001001。
1.E不全为0或不全为1
这时,浮点数就采用下面的规则表示,即指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1。
2.E全为0
这时,浮点数的指数E等于(1-127)(或者(1-1023))即为真实值,有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于0的很小的数字。
3.E全为1
这时,如果有效数字M全为0,表示±无穷大(正负取决于符号位s)。
解析:
int main()
{
int n = 9;
//0000 0000 0000 0000 0000 1001
float* pFloat = (float*)&n;
printf("n的值为:%d\n", n);//9
printf("*pFloat的值为:%f\n", *pFloat);
//0 00000000 000000000001001
//s E M
//0.000000000001001*2^(-126)
//打印小数点后6位0.000000
*pFloat = 9.0;//1001.0 1.001*2^3
//0 10000010 001000000000000000000000
printf("num的值为:%d\n", n);
printf("*pFloat的值为:%f\n", *pFloat);
return 0;
}
指针
- 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
- 指针的大小是固定的4/8个字节(32位平台/64位平台)。
- 指针是有类型,指针的类型决定了指针的±整数的步长,指针解引用操作的时候的权限。
- 指针的运算。
一、字符指针
int main()
{
char str1[] = "hello";
char str2[] = "hello";
const char* str3 = "hello";
const char* str4 = "hello";
if (str1 == str2)
printf("str1 and str2 are same\n");
else
printf("str1 and str2 are not same\n");
if (str3 == str4)
printf("str3 and str4 are same\n");
else
printf("str3 and str4 are not same\n");
return 0;
}
结果:
说明:这里str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4不同。
二、指针数组
是一个存放指针的数组。
int* arr1[10]; //整型指针的数组
char *arr2[4]; //一级字符指针的数组
char **arr3[5];//二级字符指针的数组
三、数组指针
整型指针: int * pint; 能够指向整形数据的指针。
浮点型指针: float * pf; 能够指向浮点型数据的指针。
数组指针:int (*p)[]; 能够指向数组的指针。
int main()
{
int arr[10] = { 0 };
printf("arr = %p\n", arr);
printf("&arr= %p\n", &arr);
printf("arr+1 = %p\n", arr + 1);
printf("&arr+1= %p\n", &arr + 1);
return 0;
}
结果:
&arr和arr,虽然值是一样的,意义不一样。
数组名是数组首元素的地址。
有2个例外:
1.sizeof(数组名)- 数组名表示整个数组,计算的是整个数组的大小,单位是字节。
2.&数组名 - 数组名表示整个数组,取出的是整个数组的地址。
二维数组的数组名表示首元素的地址,二维数组的首元素是第一行。
1.int arr[5]; //整型数组
2.int *parr1[10]; //整型指针的数组
3.int (*parr2)[10]; //数组指针,该指针能够指向一个数组,数组的10个元素,每个元素是int类型
4.int (*parr3[10])[5]; //parr3是一个储存数组指针的数组,该数组能够存放10个数组指针。每个数组指针能够指向一个数组,数组5个元素,每个元素是int类型
练习
1.求结果
int i;//i是全局变量,不初始化,默认是0
int main()
{
i--;//-1
//sizeof这个操作符,算出的结果是unsigned int
if (i > sizeof(i))
{
printf(">");
}
else
{
printf("<");
}
return
结果:>
2.求Sn=a+aa+aaa+aaaa+aaaaa+…,其中,a为0~9
int main()
{
//由a组成的前n项之和,不考虑溢出
int a = 0;
int n = 0;
scanf("%d %d", &a, &n);
int i = 0;
int sum = 0;
int ret = 0;//一项
for (i = 0; i < n; i++)
{
//算出一项
ret = ret * 10 + a;
sum = sum + ret;
}
printf("sum=%d\n", sum);
return 0;
}
3.写一个函数使用指针打印整型一维数组arr的内容
int main()
{
int arr[] = { 1,2,1,21,2,1,2,4,2,4,2,4 };
int* p = arr;
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", *(p + i));
}
return 0;
}
4.打印1~100000之间所有的自幂数
int main()
{
int i = 0;
for (i = 0; i <= 100000; i++)
{
//判断i是否为自幂数
//1.计算i的位数n
int n = 1;
int tmp = i;
while (tmp / 10)
{
n++;
tmp = tmp / 10;
}
//2.计算i的每一位的n次方之和
tmp = i;
int sum = 0;
while (tmp)
{
//pow:用来求次方数
sum+=pow(tmp % 10, n);
tmp = tmp / 10;
}
//3.判断
if (sum == i)
{
printf("%d ", i);
}
}
}
5.练习:打印菱形图案
int main()
{
int line = 0;
scanf("%d", &line);
//上
int i = 0;
for (i = 0; i < line; i++)
{
//打印一行
//空格
int j = 0;
for (j = 0; j < line - 1-i; j++)
{
printf(" ");
}
//*
for (j = 0; j < 2*i+1; j++)
{
printf("*");
}
printf("\n");
}
//下
for (i = 0; i < line - 1; i++)
{
//打印一行
//空格
int j = 0;
for (j = 0; j <=i; j++)
{
printf(" ");
}
//*
for (j = 0; j < 2 *( line-1-i) - 1; j++)
{
printf("*");
}
printf("\n");
}
return 0;
}
6.练习:共20元,一瓶汽水1元,2个空瓶换1瓶汽水,可以喝多少汽水
int main()
{
int money = 0;
scanf("%d", &money);
int total = money;
int empty = money;
//开始置换
while (empty >= 2)
{
total+=empty / 2;
empty = empty / 2 + empty % 2;
}
printf("%d\n", total);
return 0;
}
7.练习:调整数组,使奇数全部位于偶数前面
void print(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
void move(int arr[], int sz)
{
int left = 0;
int right = sz - 1;
while (left < right)
{
//从前往后找偶数
while((left < right)&&(arr[left] % 2 == 1))
{
left++;
}
//从后往前找奇数
while ((left < right)&&(arr[right] % 2 == 0))
{
right--;
}
//
if (left < right)
{
int tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
}
}
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7,3,5,76,434,343 };
int sz = sizeof(arr) / sizeof(arr[0]);
move(arr,sz);
print(arr, sz);
return 0;
}
8.练习:打印杨辉三角
int main()
{
int arr[10][10] = { 0 };
int i = 0;
int j = 0;
for (i = 0; i < 10; i++)
{
for (j = 0; j <= i; j++)
{
if (j == 0)
{
arr[i][j] = 1;
}
if (i == j)
{
arr[i][j] = 1;
}
if (i >= 2 && j >= 1)
{
arr[i][j] = arr[i - 1][j - 1] + arr[i - 1][j];
}
}
}
for (i = 0; i < 10; i++)
{
for (j = 0; j <= i; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
(欢迎大家批评指正,侵权即删)