读书笔记 C和指针

一.熟悉C语言

1.空白和注释

   (1)程序的空白就是指用空行将程序的不同部分分隔开,Tab用于缩进语句,如果我们想要写出漂亮的代码,就需要在编写程序的时候注意使得代码容易阅读,要有一个良好的编程风格。就良好 编程风格来说,大家可以看看《高质量的C C++编程》

   (2)注释:注释以符号/*开始,以符号*/结束。注释不能嵌套,就是说在出现第一个/*和第一个*/之间都被看作是注释,无论中间出现多少个/*

  当然,如果想要注释掉整段代码的话,就需要#if、#end if了,它们之间的程序可以有效的被注释掉,即使代码中间原先存在注释也没关系了


2.#include预处理指令可以使一个函数库头文件的内容由编译器进行处理,#define指令允许我们自己给字面值常量取符号名


3.main函数是程序执行的起点。函数的标量参数通过传值方式进行传递,而数组名参数则具有传址调用的语义


4.一个必须注意的问题很多初学者很容易不小心把测试相等性的符号==写成赋值符号=,这就造成程序的错误,我在初次写代码时就容易犯这个错误,希望大家不要注意



二.基本概念

1.翻译环境、执行环境

翻译过程:组成一个程序的每个源文件通过编译过程分别转换为目标代码(object code),然后,各个目标文件由链接器(Linker)捆绑在一起,形成一个单一而完整的可执行程序。如下图:

wKiom1diMQmjhmM8AABDmk5QwAc412.png-wh_50

执行:程序载入到内存中,程序执行开始,调用main函数,执行程序代码,程序终止。

程序执行代码过程中,大多是机器里,①程序将使用一个运行时堆栈(stack),它用于存储函数的局部变量和返回地址。②使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程中将一直保留它们的值。


2.字符:

⑴转义序列由一个反斜杠 \ 加上一个或多个其他字符组成。每个转义序列代表反斜杠后面的那个字符,但并未给这个字符增加特别的意义。


记住:\ddd表示1~3个八进制数字

          \xddd表示十六进制数字


3.标识符:变量、函数、类型等的名字。由大小写字母、数字和下划线组成,但是不能以数字开头。

下列关键字不能作为标识符使用:

auto            do            goto         signed  unsigned   break  double  

 if                sizeof       void          case      wlse            int       volatile

 static

 enum      long        struct     while   const        extern 

register      switch       continue  float     return          char


下面通过写一个小程序来练习一下C程序:

编写一个程序,它从标准输入读取C源代码,并验证所有的花括号都正确地成对出现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<stdio.h>
#include<stdlib.h>
int main()
{
int ch;
int count=0;
while((ch=getchar())!=EOF)
{
if(ch =='{')
count+=1;
if(ch =='}')
{
if(count == 0)
printf("匹配失败!\n");
else
count-=1;
}
    }
if(count>0)
printf("匹配成功!\n");
system("pause");
return 0;
}


三.数据

1.基本数据类型

⑴整型

整型值大小规定:长整型至少应该和整型一样长,而整型至少应该和短整型一样长

                                     变量的最小范围


      类型       最小范围
char 0~127
signed char -127~127
unsigned char 0~255
short int  -32767~32767
unsigned short int 0~65535
int -32767~32767
unsigned int

0~65535

long int -2147483647~2147483647
unsigned long int 0~4294967295

 整型字面值:指定了自身的值,并且不允许发生改变

与普通变量的区别:被初始化以后,其值不能被改变

 书写:

十进制整型值:  123   65535      (缺省情况下是最短类型)

八进制或十六进制 : 0173  017777  0x7b


枚举类型:值为符号常量而不是字面值的类型

  这种类型的变量实际上以整型的方式存储,符号名的实际值都是整型值


①适当时候为符号名指定特定的整型值:

enum Jar_Type{CUP = 8, PINT = 16};

②如果某个符号名未显示指定一个值,那么它的值就比前面一个符号名的值大1


⑵浮点类型:


ANSI标准规定long double 至少和double一样长,而double至少和float一样长。同时规定了一个最小范围:所有浮

点类型至少能够容纳从10^-37到10^37之间的任何值

 浮点数的形式存储: 

   对于浮点类型的数据采用单精度类型(float)和双精度类型(double)来存储,float数据占用32bit,double数据占用64bit,我们在声明一个变量float f= 2.25f的时候,是如何分配内存的呢?如果胡乱分配,那世界岂不是乱套了么,其实不论是float还是double在存储方式上都是遵从IEEE的规范的,float遵从的是IEEE R32.24 ,而double 遵从的是R64.53。

    无论是单精度还是双精度在存储中都分为三个部分:

  1. 符号位(Sign) : 0代表正,1代表为负

  2. 指数位(Exponent):用于存储科学计数法中的指数数据,并且采用移位存储

  3. 尾数部分(Mantissa):尾数部分

 其中float的存储方式如下图所示:

wKioL1diYGOSlJD6AAANdcKkEws520.gif-wh_50

而双精度的存储方式为:

 

wKioL1diYHLj-xG2AAAOwyM_xPY119.gif-wh_50

    R32.24和R64.53的存储方式都是用科学计数法来存储数据的,比如8.25用十进制的科学计数法表示就为:8.25*clip_image0021,而120.5可以表示为:1.205*clip_image0022,这些小学的知识就不用多说了吧。而我们傻蛋计算机根本不认识十进制的数据,他只认识0,1,所以在计算机存储中,首先要将上面的数更改为二进制的科学计数法表示,8.25用二进制表示可表示为1000.01,我靠,不会连这都不会转换吧?那我估计要没辙了。120.5用二进制表示为:1110110.1用二进制的科学计数法表示1000.01可以表示为1.0001*clip_image002[2],1110110.1可以表示为1.1101101*clip_image002[3],任何一个数都的科学计数法表示都为1.xxx*clip_image002[1],尾数部分就可以表示为xxxx,第一位都是1嘛,干嘛还要表示呀?可以将小数点前面的1省略,所以23bit的尾数部分,可以表示的精度却变成了24bit,道理就是在这里,那24bit能精确到小数点后几位呢,我们知道9的二进制表示为1001,所以4bit能精确十进制中的1位小数点,24bit就能使float能精确到小数点后6位,而对于指数部分,因为指数可正可负,8位的指数位能表示的指数范围就应该为:-127-128了,所以指数部分的存储采用移位存储,存储的数据为元数据+127,下面就看看8.25和120.5在内存中真正的存储方式。

     首先看下8.25,用二进制的科学计数法表示为:1.0001*clip_image002[2]

按照上面的存储方式,符号位为:0,表示为正,指数位为:3+127=130 ,位数部分为,故8.25的存储方式如下图所示:

wKioL1diYISh-XjcAAAWt0tHCKc121.gif-wh_50

而单精度浮点数120.5的存储方式如下图所示:

wKioL1diYJeTF-qkAAAY5iQ9n2s607.gif-wh_50

那么如果给出内存中一段数据,并且告诉你是单精度存储的话,你如何知道该数据的十进制数值呢?其实就是对上面的反推过程,比如给出如下内存数据:0100001011101101000000000000,首先我们现将该数据分段,0 10000 0101 110 1101 0000 0000 0000 0000,在内存中的存储就为下图所示:

wKiom1diX9rSYkFqAAAIvLYD9Vo080.gif-wh_50

根据我们的计算方式,可以计算出,这样一组数据表示为:1.1101101*clip_image002[3]=120.5

而双精度浮点数的存储和单精度的存储大同小异,不同的是指数部分和尾数部分的位数。所以这里不再详细的介绍双精度的存储方式了,只将120.5的最后存储方式图给出,大家可以仔细想想为何是这样子的

wKioL1diYQazJ8nJAAAKZR-XJWk686.gif-wh_50

下面我就这个基础知识点来解决一个我们的一个疑惑,请看下面一段程序,注意观察输出结果

            float f = 2.2f;
            double d = (double)f;
            Console.WriteLine(d.ToString("0.0000000000000"));
            f = 2.25f;
            d = (double)f;
            Console.WriteLine(d.ToString("0.0000000000000"));

可能输出的结果让大家疑惑不解,单精度的2.2转换为双精度后,精确到小数点后13位后变为了2.2000000476837,而单精度的2.25转换为双精度后,变为了2.2500000000000,为何2.2在转换后的数值更改了而2.25却没有更改呢?很奇怪吧?其实通过上面关于两种存储结果的介绍,我们已经大概能找到答案。首先我们看看2.25的单精度存储方式,很简单 0 1000 0001 001 0000 0000 0000 0000 0000,而2.25的双精度表示为:0 100 0000 0001 0010 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000,这样2.25在进行强制转换的时候,数值是不会变的,而我们再看看2.2呢,2.2用科学计数法表示应该为:将十进制的小数转换为二进制的小数的方法为将小数*2,取整数部分,所以0.282=0.4,所以二进制小数第一位为0.4的整数部分0,0.4×2=0.8,第二位为0,0.8*2=1.6,第三位为1,0.6×2 = 1.2,第四位为1,0.2*2=0.4,第五位为0,这样永远也不可能乘到=1.0,得到的二进制是一个无限循环的排列 00110011001100110011... ,对于单精度数据来说,尾数只能表示24bit的精度,所以2.2的float存储为:

wKiom1diYAGz36CxAAAIsQi-nzI661.gif-wh_50

但是这样存储方式,换算成十进制的值,却不会是2.2的,应为十进制在转换为二进制的时候可能会不准确,如2.2,而double类型的数据也存在同样的问题,所以在浮点数表示中会产生些许的误差,在单精度转换为双精度的时候,也会存在误差的问题,对于能够用二进制表示的十进制数据,如2.25,这个误差就会不存在,所以会出现上面比较奇怪的输出结果。

指针

字符串常量

字符串常量可以为空,但即使是空字符串,依然存在作为终止符的NILL字节。

  在程序中使用字符串常量会生成一个“指向字符的常量指针”。当一个字符串常量出现于一个表达式中时,表达式所使用的值就是这些字符所存储的地址,而不是这些字符本身。因此,我们可以把字符串常量赋值给一个“指向字符的指针”,后者指向这些字符所存储的地址。但是,不能把字符串常量赋值给一个字符数组,因为字符串常量的直接值是一个指针,而不是这些字符本身。

2.基本声明

例:int i;        

       char j,k,l;

初始化: int j = 24;

声明数组: int value[20];

声明指针:int  *a;这句表示表达式*a产生的结果类型是int

注意:如果上例写成int*   a ,a被声明为int*的指针。然而这在一些声明中容易造成错误。比如

int*   b,  c,  d;

人们会自然的认为这是把三个变量声明为指向整型的指针,事实并非如此。*实际上只是*b的一部分,b是一个指针,c,d只是普通的整型。正确的应该是

int   *b, *c, *d;


3.常量

使用关键字const来声明常量

1
2
3
4
int *pi;  //普通的指向整型的指针
int const *pi;   //指向整型常量的指针
int const cpi;   //指向整型的常量指针
int const const cpci;  //无论指针本身还是所指向的值都是常量

const变量只能用于允许使用变量的地方

4.作用域

代码块作用域(代码块开始处;嵌套时不要有同样的变量名)

文件作用域

原型作用域

函数作用域(函数中的所有语句标签必须唯一)

基于Spring Boot搭建的一个多功能在线学习系统的实现细节。系统分为管理员用户两个主要模块。管理员负责视频、文件文章资料的管理以及系统运营维护;用户则可以进行视频播放、资料下载、参与学习论坛并享受个性化学习服务。文中重点探讨了文件下载的安全性性能优化(如使用Resource对象避免内存溢出),积分排行榜的高效实现(采用Redis Sorted Set结构),敏感词过滤机制(利用DFA算法构建内存过滤树)以及视频播放的浏览器兼容性解决方案(通过FFmpeg调整MOOV原子位置)。此外,还提到了权限管理方面自定义动态加载器的应用,提高了系统的灵活性易用性。 适合人群:对Spring Boot有一定了解,希望深入理解其实际应用的技术人员,尤其是从事在线教育平台开发的相关从业者。 使用场景及目标:适用于需要快速搭建稳定高效的在线学习平台的企业或团队。目标在于提供一套完整的解决方案,涵盖从资源管理到用户体验优化等多个方面,帮助开发者更好地理解掌握Spring Boot框架的实际运用技巧。 其他说明:文中不仅提供了具体的代码示例技术思路,还分享了许多实践经验教训,对于提高项目质量有着重要的指导意义。同时强调了安全性、性能优化等方面的重要性,确保系统能够应对大规模用户的并发访问需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值