c语言关键字理解和再认识(上)(2)

简介: 前言首先我们来了解一下,代码和计算机的关系。我们在编译器vs2022中写的代码叫做文本代码,因为它是放在一个文件夹中的,是一个文件,而这个文本代码具体和我们的计算机是怎么联系起来的。下面我们就好好探究一下这个关系。首先介绍了文本代码是什么的问题,那么文本代码对应的是什么?其实它就是一个可执行程序(可执行程序就是二进制程序,可执行程序本质上也是个文件)。如图:

大小端

什么是大小端?

表示数据在存储器中的存放顺序。

大端:按照字节为单位,低权值位数据存储在高地址处,就叫做大端。

小端:按照字节为单位,低权值位数据存储在低地址处,就叫做小端。

0e0efd14b6e0da6233a26ba9b34d100b.png那么再来看看大小端概念



0628b76d119de191974e64cde4f10db1.png

口诀

以小端为例

小端低地,否则为大端。

大小端如何影响数据存储的

本质是数据和内存空间的一种映射关系。


例子:

unsigned int a = -10;

这个a变量如何存呢?

-10=1111 1111 1111 1111 1111 1111 1111 0110(补码)=0xFFFFFFF6(十六进制补码)

如何取呢?


先看大小端

1111 1111 1111 1111 1111 1111 1111

再看自身类型

自身类型是unsigned, 不用看符号位,直接变十进制。

为什么有大小端?

  1. 数据按字节为单位的情况下,有高权值和低权值之分。
  2. 内存的地址有高地址和低地址之别的。

整型取值范围

以signed char为例:

char类型是一个字节的大小空间。

那么它的取值范围不就是:11111111 ~ 01111111(-127~127)吗?

这是有问题的。

什么是数据取值范围?

假如两个比特位:

00 ~ 11 这中间还有有01 10

假如是三个比特位:

000 ~ 111 这中间还有001 010 011 100 101 110

那么数据类型能表示多少个数据,是取决于比特位排列组合的个数。

两个比特位就是2^2 个,三个比特位就有2^3 个,三十二个比特位就有2^32个。

回到上面的问题,其实char类型的范围是-128 ~127。那么这个-128怎么来的呢?


首先char类型是占一个字节的内存大小空间,也就是八个比特位,那么八个比特位就有2 ^ 8的排列组合,它的取值范围也就是1111 1111 ~ 0111 1111(-127 ~127),在这个取值范围内,有一个二进制是1000 0000,它的十进制不在-127 ~ 127之间,那么我们规定它是-128或者128,这里符号位是1,那么只能取-128,这就是-128的由来,那么我们怎么去理解-128呢?举一个例子。

char c = -128;
printf("%d\n",c);

打印出的结果是-128,可以打印出正确结果。我们来分析分析,存的过程是先开辟内存空间,然后把-128转化为补码:1 1000 0000存进去;然后取的时候,char类型只能取八个比特位,这时就发生了截断,取的不是1 1000 0000,而是1000 0000,然后先看最高符号位,最高符号位是1,确认是补码,然后转换成原码:0000 0000这时我们发现,它取的时候不是-128而是0;那么就规定1000 0000是-128,直接用。所以char类型的取值范围就是-128 ~ 127。short的取值范围就是-2^15 ~ 2^15 -1。int的取值范围就是:-2^31 ~ 2^31 -1。

#include <stdio.h>
#include <string.h>
int main()
{
    char a[1000];
    for(int i=0;i<1000;i++)
    {
        a[i] = -1-i;
    }
    printf("%d\n",strlen(a));
    return 0;
}

最终输出结果是255。

我们来分析一下,首先strlen函数计算的是这个数组在’\0’结束标志之前的元素个数。那么也就是计算这个数组元素为零之前的元素个数。

a[0] = -1 = 1000 0001(原码) = 1111 1111(补码)
a[i] = -1 + (-1) = 1 1111 1110 (补码) = 1111 1110(截断后取的补码) = 1111 1101(反码) = 1000 0010(原码) = -2 

那么i=0,a[0] = -1;i=1,a[1] = -2;i=2,a[2] = -3;…那么i=127,a[127] = -128;

那么-1 + (-127) = ?

a[127] = -1(1111 1111)(补码) + -127(1000 0001)(补码) = 1 1000 0000 = 1000 0000(截断后取的补码) = -128

那么a[128] = ?

a[128] = -1+(-128) = 1111 1111(-1的补码) + 1000 0000(-128的补码) = 1 0111 1111 = 0111 1111(截断后取的补码) = 127
//-1+(-128)本身越界了,超过了范围,出现错误得正数.

那么a[129] = 126,a[130] = 125,依次递减,那么我们的a[255] = 0.0~255有256个元素(包含\0),所有只有255个元素,打印出255.

例题

#include <stdio.h>
int main()
{
  int i = -20;
  //-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(补码)
  unsigned int j = 10;
  //0000 0000 0000 0000 0000 0000 0000 1010(原码=补码)
  //i+j
  //1111 1111 1111 1111 1111 1111 1110 1100(-20)
  //0000 0000 0000 0000 0000 0000 0000 1010(10)
  //1111 1111 1111 1111 1111 1111 1111 0110(-20 + 10)
  //%d格式打印是unsigned int
  //看最高符号位
  //1111 1111 1111 1111 1111 1111 1111 0110(-20 + 10)(补码)
  //1000 0000 0000 0000 0000 0000 0000 1010(原码)
  //1000 0000 0000 0000 0000 0000 0000 1010 = -10
  printf("%d\n", i + j);
  return 0;
}
//如果被%u格式解释
//则输入结果是4294967286
//这个值对应的二进制序列仍然是1000 0000 0000 0000 0000 0000 0000 1010
//这就是类型的价值,类型决定我们如何解释内存当中二进制的含义。
#include <stdio.h>
int main()
{
  //死循环
  unsigned int i;
  //unsigned int i ——> 是无符号整数,读取出来的时候就会直接读取,不会转化再转为原码
  for(i = 0; i >= 0; i--)
  {
    printf("%u\n", i);
    //Sleep(1000);//休眠一秒以便观察
  }
  return 0;
}
//死循环从4294967295到0再到4294967295到0一直循环下去(-1对应的%u格式打印出的结果是4294967295)

规则

无符号型常量都应该带有字母U后缀

int a = 10;
unsigned int b = 10u;

调试起来看一下。

b2df818e8099c0bb1fef9fd352bc9d60.png

if-else语句

什么是语句?

c语言中由一个分号;隔开的就是一条语句。

表达式是什么?

c语言中,用各种操作符把变量连起来,形成有意义的式子,就是一个表达式。


if-else基本语法

#include <stdio.h>
int main()
{
    int flag = 1;
    if(1 == flag)
    {
        printf("hello girl\n");
    }
    else
    {
        printf("hello boy\n");
    }
    return 0;
}

结论:

  1. 注释:ctrl+k+c(注释),ctrl+k+u(取消注释)。
    还有一种方法(不推荐)
#include <stdio.h>
int main()
{
    if(0)
    {
    int i = 1;
    if(1 == i)
    {
        printf("hello\n");
    }
    else
    {
        printf("*****\n");
    }
    }
    return 0;
}注释可以用if(0)来注释,不推荐,但是要能看懂就行。
  1. c语言当中0为假,非0为真。
  2. if-else语句怎么执行的?
    先执行()中的表达式或者函数,得到真假结果——>条件判定——>进行分支功能。

    说了怎么执行,那么我们再来看怎么用,深入理解一下。
int fun()
{
    printf("如果没有数据\n");
    return 1;
}
#include <stdio.h>
int main()
{
    if(fun()) //先执行()中的函数再条件判断是非零即为真,则进入分支。
    {
        printf("Yes\n");
    }
    return 0;
}

前面用if-else语句做了铺垫,然后我们一起来看bool类型。

bool类型

c99引入了bool类型。但是c语言大部分都是以c90为标准。所以只需要知道就可以,另外还有微软的BOOL类型,它的空间大小是4个字节,但是不推荐使用,可移植性差(只能适用于微软的编译器)。

#include <stdio.h>
#include <stdbool.h>
int main()
{
    bool x = true;
    if(x)
    {
        printf("hello world!");
    }
    return 0;
}
#include <stdio.h>
#inlcude <stdbool.h>
int main()
{
  bool x = true;
  //#define bool _BOOL(转到定义)
  printf("%d\n", sizeof(x));//输出结果是1,只占一个字节的空间大小。
  return 0;
}

bool类型占一个字节的空间


c语言中怎么进行的bool和0比较呢?

#include <stdio.h>
#include <stdbool.h>
int main()
{
  int flag = 0;
  if (flag == 0) //不推荐
  {
    printf("1\n");
  }
  if (flag == false) //不推荐
  {
    printf("2\n");
  }
  if (flag) //推荐,flag就相当于bool
  {
    printf("3\n");
  }
  return 0;
}
//注意false要引入头文件#include <stdbool.h>

float变量和“零值”进行比较

前言

浮点数在内存中存储,并不是完整存储的,在十进制转化为二进制,有可能有精度损失(数值可能变大,也有可能变小)。

#include <stdio.h>
int main()
{
    double x = 3.4;
    printf("%.50lf\n", x);
    return 0;
}

产生了精度的损失。看图


a0b426df6a10602a14982f1bd95c757f.png

正言

#include <stdio.h>
int main()
{
    double x = 1.0;
    double y = 0.1;
    printf("%.50lf\n", (x - 0.9));
    printf("%.50lf\n", y);
    if((x-0.9) == 0.1)
    {
        printf("yes\n");
    }
    else
    {
        printf("no\n");
    }
    return 0;
}

运行后结果:

7aa7acf1f294b571bb7785627b032162.png

浮点数和"零值"进行比较

精度损失导致,那么显然 == 判断操作符绝对不能进行浮点数之间的比较。那么我们规定进行比较是对数和EPSILON进行比较,也就是数是否在一个合法的精度范围内。

#inlcude <float.h>
int main()
{
  //使用DBL_EPSILON要引入头文件#include <float.h>
  DBL_EPSILON; //double最小精度
  //#define DBL_EPSILON      2.2204460492503131e-016 // smallest such that 1.0+DBL_EPSILON != 1.0
  FLT_EPSILON; //float最小精度
  //#define FLT_EPSILON      1.192092896e-07F        // smallest such that 1.0+FLT_EPSILON != 1.0
  return 0;
}

再来看怎么比较

  1. 第一种比较(自定义比较)
#include <stdio.h>
#include <math.h>
#define EPS 0.000000000000000000001
int main()
{
    double x = 1.0;
    double y = 0.1;
    printf("%.50lf\n", (x - 0.9));
    printf("%.50lf\n", y);
    if (fabs((x - 0.9))-y < EPS)
    {
        printf("yes\n");
    }
    else
    {
        printf("no\n");
    }
    return 0;
}//输出结果是yes
  1. 第二种方法(DBL_EPSILON比较——c语言中表示最小的精度值)
#include <stdio.h>
#include <math.h>
#include <float.h>
int main()
{
    double x = 1.0;
    double y = 0.1;
    printf("%.50lf\n", (x - 0.9));
    printf("%.50lf\n", y);
    if (fabs(x - 0.9) - y < DBL_EPSILON)
    {
        printf("yes\n");
    }
    else
    {
        printf("no\n");
    }
    return 0;
}//输出结果是yes

最后我们来看浮点数和零值比较

#include <stdio.h>
#include <math.h>
#include <float.h>
int main()
{
  double x = 0.0;
  if (fabs(x)< DBL_EPSILON)
  {
    printf("yes\n");
  }
  else
    printf("no\n");
  return 0;
}

写法上不建议比较的时候带上等号。

总结:

  1. 浮点数存储的时候是有精度损失的。
  2. 浮点数是不能进行==比较的。
  3. double用DBL_EPSILON比较,float用FLT_EPSILON比较

指针和“零值”比较

#include <stdio.h>
int main()
{
  printf("%d\n", 0);
  printf("%d\n", NULL);
  printf("%d\n", '\0');
  return 0;
}
//输出结果都是0
//数值没变,变的是类型。(强制类型转化)
//强制类型转化:不改变内存中的数据,只改变对应的类型。

看看if语句中括号中的写法注意事项。

#include <stdio.h>
int main()
{
    int *p = NULL;
    //三种写法
    //if(p == 0)  if(p != 0)
    //if(p == NULL)  if(p != NULL)
    //if(p)    if(!p)
    //这些写法不建议,我建议这样写
    //if(NULL == P)  if(NULL != P)
    return 0;
}

else到底和哪个if匹配呢?

int main()
{
  int x = 0;
  int y = 1;
  if (10 == x)
    if (11 == y)
      printf("hello girl!\n");
  else
    printf("hello boy!\n");
  return 0;
}

这样的排版结果是什么都没打印,为什么呢?

原因就是,else匹配if是就近原则。建议循环和if分支语句都带上花括号。

















相关文章
|
6月前
|
存储 程序员 C语言
C语言关键字是什么?什么是关键字?什么是字符和ascll码值
C语言关键字是什么?什么是关键字?什么是字符和ascll码值
|
4月前
|
C语言
|
4月前
|
存储 C语言
C语言中static关键字的作用与用法解析
C语言中static关键字的作用与用法解析
|
5月前
|
存储 C语言
C语言中的typedef关键字:为类型定义新名称
C语言中的typedef关键字:为类型定义新名称
|
5月前
|
C语言
深入探索C语言中的sizeof关键字
深入探索C语言中的sizeof关键字
|
5月前
|
存储 编译器 C语言
C语言中的关键字与标识符详解
C语言中的关键字与标识符详解
|
6月前
|
算法 编译器 API
C语言易混淆、简单算法、结构体题目练习、常见关键字总结-1
C语言易混淆、简单算法、结构体题目练习、常见关键字总结
|
6月前
|
安全 编译器 C语言
C语言中的const关键字
C语言中的const关键字
50 2
|
6月前
|
存储 C语言
【C语言】数据:数据类型关键字
【C语言】数据:数据类型关键字
|
6月前
|
存储 编译器 C语言
c语言中static关键字的作用
c语言中static关键字的作用