新人开荒篇2:变量(基本类型,声明,定义,赋值)

本文介绍了C语言中的基本数据类型、数据在内存中的存储结构,包括栈区、堆区和静态区,以及const和unsigned等修饰符的作用。重点讲解了左值与右值的概念,强调编程思维的培养。

1.摘要

这是新人的第二篇,讲一些变量类型(一笔带过),认识一下,然后讲一下相关修饰(并未详细展开),左值与右值的概念是为了让新人养成编程的思维。当然笔者也肯定有不足之处,望理解并及时指正。

(注:带*的表示萌新刚入手时不必深如研究,有一些深入认识和经验后再次理解)

2.一些基本数据类型

*2.1数据类型抽象化

我们的现实世界中有整数,小数,分数,有理数,无理数等概念,这些都是人为提出的,计算机并不认识,计算机由电路组成,电路中就可以表示有无电流,这里有电流记为“1”,无电流记为“0”(后来人们又有“高电平”“低电平的说法”)(想详细了解可以可以看书《数字逻辑设计》),这使得由0,1组成的二进制天然的成为计算机自己的语言,因此,一切语句最终都要转化为01串(可以回忆一下计算机导论的内容),但这里我们并不详细展开,只在意它的根本,都是0和1,计算机有自己的进制,或者说存储的基本单位——

最小单位bit(比特)  (每位存储0或1)

大一点点的单位Byte(字节) (1Byte=8bit)

再往下就较为熟悉KB   (1KB=1024Byte)

MB,GB,TB同样(此处为接下来做铺垫)

*2.2数据在内存中存储的初步认识和举例

每种编程语言都已经预先给出了一些常见的数据类型(整数int,小数float),而这些类型就必须得有统一的大小规定,C语言中  int 型数据都占 4个字节 ,不足的在前面补0(暂且不严谨的说)

比如 int a=6; 我们知道a的二进制是“110”

那么在内存中存储就是  “00000000  00000000  00000000  00000110”

(这里先不讲原反补的概念,而且导论讲过)

2.3常见数据类型

随便写个代码看看各种数据占的字节数(使用sizeof);

(注:sizeof是运算符,用来求其占据字节数,不是函数)

#include<stdio.h>

int main()
{
	int a1;//常用  “整数 ” 在其他方你可能看到它被define成 i32
	short int a2;
	long int a3;
	long long int a4;//常用 顾名思义 “很长的整数”  其他方你可能看到它被define成 i64
	unsigned int a11;
	char ch1;//常用  “字符型 ”
	unsigned char ch2;
	float f1;//常用   “小数型 ” “浮点型”
	double f2;//常用  “很长的小数 ”约等于更精确的小数  “双精度 ”
	bool b1;//不常用  但会经常接触
		
	printf("int a1字节数:%u\n",sizeof(a1));	
	printf("short int a2字节数:%u\n",sizeof(a2));
	printf("long int a3字节数:%u\n",sizeof(a3));
	printf("long long int a4字节数:%u\n",sizeof(a4));
	printf("unsigned int a11字节数:%u\n",sizeof(a11));
	printf("char ch1字节数:%u\n",sizeof(ch1));
	printf("unsigned char ch2字节数:%u\n",sizeof(ch2));
	printf("float f1字节数:%u\n",sizeof(f1));
	printf("double f2字节数:%u\n",sizeof(f2));
	printf("bool b1字节数:%u\n",sizeof(b1));
	
	return 0;
}

下面是运行结果:

至于每种类型可以表达的数据范围,则可以根据二进制推算,

比如 int 是四个字节,  4x8个bit位,根据乘法原理,可以表示2的(4*8)次幂种,可是实际中由于它也能表示负数,安排第一位为符号位,即第一位为0,表示非负数,第一位为1,表示正数,那么实际表示的非负数就是(0到2的(4*8-1)次幂-1),负数范围为(-  2的(4*8-1)次幂 到 - 1);依照这个规则,我们也可以求出其他数据类型的范围。(具体数值可以查询资料)

2.4数据的其他修饰

*2.4.1电脑中内存的分布

一般分为,栈区,堆区,还有静态区,负责参与运算的迭代器也有一部分内存。其中,栈区存储局部变量(啥叫局部变量,就比如主函数里写的 int a=0;)和函数参数;堆区主要涉及内存操纵的相关函数申请去存储的,可以灵活的运用空间,(比如 malloc,calloc,realloc,free);静态区存储全局变量(啥叫全局变量,int main(){}之前写的int a=0;就是)和静态变量(之后项目举例时再具体介绍)。

2.4.2 const 修饰 与 unsigned修饰

unsigned 修饰有时是为了扩大数据存储的范围,比如一些题目要求数据范围比较大,恰好没有负数,这时就可以使用,此外,对 char 类型,本来应该有256(2的8次幂)种可以表达,实际ASCII码却只有128,其中就保留了符号位,ISO C++标准并没有规定它在默认不加修饰的情况是有无符号,因编译环境而异。

const 修饰,表示这是一个说一不二的常量,或者更通俗讲,常数,比如 2是一个常数,比如π,我们也许不知道他是多少,但它不变,不同使用场景要求精度不同。我们可以像下面这样定义一个π,在这句之后,程序遇到的PI 都回当3.14 进行处理,当工程要求的精度更高时,我们不可以像这样修改,否则就不叫“常量”,但我们可以直接在const这处语句 改为3.1415 。如果不定义这样的常量,代码中莫名出现的数字很可能不知道含义,而且一旦修改,还要连续修改好几处使用了这个量的值,这就是const给我们提供的方便之处。

(另:常量只能用常量去定义)

const float PI = 3.14;  //定义PI表示的值就是3.14(const 和 float 位置可以对调)
//但当然,常量定以后就不能修改,如:
PI=3.1415;   //err

*2.4.3 其他修饰

auto,static,extern,register

函数中的局部变量默认为auto型,也可以显式的进行声明;

extern,“外部声明变量”,可以扩展变量的使用范围,这在多文件的项目中时会用到;

static静态区,与动态的存储相对应,这部分的内容不被释放,赋值等操作后仍然静止在那里,等待你下一步对他操作;

register,"寄存器变量",声明后使其在寄存器中占一席之地,避免了计算结果过多的传递,提高效率,实际上编辑器会帮你做出优化,使用并不多;

还有比如上述关键字有时可以多个一起使用,比如 static extern int num;

3  C语言“ = ”的理解

3.1声明与定义

变量在使用前都要首先声明其类型和名称,我们不妨用汉语表述,如:

“小花是一头牛,张三是一个村民”

这就可以说是一个声明,声明了两个对象“小花”,“张三”,但他们的类型不同,前面是一种动物,后面是一种人,不同对象有不同的处理方式,能够进行的操作也不相同。

比如再接一句

“张三带着小花到河边吃草”,

如果没有前面那句声明,理解起来会多么可怕呀(在日常生活中,往往遍布着的是“名词”,出现名字一般判定为人名,在编程语言中,我们要扭转这种思维,区别两个对象的不同之处,并在声明上进行区分)

(声明要指定对象类型 以及 名称)

下面是一些声明的举例:

char ch;
int num,total;
float v,r,m;

但值得注意的是,声明了,并不意味着引起内存的分配,就比如我声明了张三是个村民,但他长啥样?有多少钱?干啥的?一概不知,张三并不在我心里,这种未知情况下驱使它有时是危险的,我让张三去放牛,我哪知道他是个瘸子啊。

数据的使用也是一样,为了程序尽可能安全且符合预期,我们必须保证所有变量在使用前都经过定义,或者被输入一个合法的值。

上述代码中直接给他们一个值就是定义:

(注:声明时有初始值就叫定义,定义和赋值不一样)

char ch  = 'a';  //字符类型要加单引号
int num = 1, total = 2;
float v = 0,r = 3.2, m = 6.88;

3.2 赋值的初步理解

在上述代码中,我们让ch,num等变量有了初始值,但这还不足以让我们与数学进行区分。

数学里,代表值相等,是形容两数或两式关系的。

而计算机语言,是形容将这个 量 放进相应名字的 地址 上,存储相应的信息。

早期学习者可以先这样理解,把不同的液体,倒入相应的杯子里。

比如写让两个数交换的程序:

#include<stdio.h>

int main(){

int a = 13;  //a瓶子里装着酱油
int b = 29;  //b瓶子里装着醋
printf("a = %d  b = %d\n",a,b); // 打印一下看看

int c = a;   //找来c瓶子 ,把a瓶装的酱油倒进去
a = b;       //把 b瓶子中的醋 倒入a瓶
b = c;       //再把 c瓶装着的酱油 倒入 b瓶

printf("a = %d  b = %d\n",a,b);  //打印验证交换结果

return 0;
}

这样理解也未尝不可,但随着日益深入,我们要知道它相当于是把瓶子倒掉再灌入,或者说“覆盖”

3.3 左值与右值

事实上,计算机的计算使用寄存器,而且寄存器件可以通过内部电路互相读写,这就需要汇编语言才能更好理解,这里目的是介绍左值右值的思想,不详细展开。

以代码举例:

int a, b, c, d;
int main(){
    a = 4;
    b = 2;
    c = 3;
    d = (a + b) * c;
    retrun 0;
}

前三个完成了定义的变量实际操作是:

(其实是汇编语言描述了一下,新人建议先不要急着进行)

“把对应格式的值写入对应的地址”

后面却不然,概括讲是:

“分别将 a , b取出来存到两个寄存器上”

“对二者执行相加,并把加法结果存在其中一个寄存器上”

“把c 取出来存到寄存器上”

“对这两个寄存器做乘法,并将结果存储到相应寄存器”

“将寄存器中结果写入相应的d的地址”

这步中,d位于等式左端,a,b,c组成的算式放在右端。

一般意义上我们对 ‘ = ’ 左端叫左值,右端叫右值,

事实上,左端的变量才会长期出现在内存中,而右端是短期的迭代器中的运算,并没有实际的改变右端的'a'或其他变量的值。了解了这个特点,我们才能理所当然的把我们要修改的值放左边,抽象的计算过程放右边。

从这个角度看,我们不妨对上面交换两数换种写法,试试不创建临时变量,交换两数。

#include<stdio.h>

//这里是为了新人刚入手理解而做出的朴实的解释,大佬们不要浪费时间
int main(){

int a = 13;  //定义 整型 a的值是13
int b = 29;  //定义 整型 b的值是29
printf("a = %d  b = %d\n",a,b); // 打印一下看看

//每次要修改的值如果是直接把另一个赋值,必然覆盖掉原有内容
//所以第一次可以存表达两数关系的值,这里比较安全的是“和”

a = a + b;       //把a与b的和赋值给a

//a=a0+b0    b=b0
//因为刚刚修改过a,下一次赋值语句不可能还是a
//于是根据上面的式子思考对b赋值的表达式,“我希望b赋值为原来的a”

b = a - b;       //把a与b的差赋值给b

//另一个想法是代还   b= (a+b)-b    ==a
//b换好了,接着换a

a = a - b;       //把a与b的差赋值给a

//和上面b的赋值类似

printf("a = %d  b = %d\n",a,b);  //打印验证交换结果

return 0;
}

当然,你如果不够萌新,那么你能够发现问题,这里给新人留下一点疑问:

什么情况下,上述代码会出现问题,为什么?(后续我们还会进一步讨论)

4.总结

这次的内容主要初步了解数据类型,一些类型名的修饰,当然,这些东西在真正进入相应项目前,你可能会觉得,这些类型有什么用?又或者,只有这些就够描述世界了吗?这些都要放到后续去拓展,这里只需要你记住有这些类型即可。内存理解后面也会逐步渗透,这是学C的“内功”,会提高你的上限,讲左值与右值是为了培养新人的思维方式,习惯计算机中的编程。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值