浅谈C、Java与Python之间的小差异
✅作者简介:人工智能专业本科在读,喜欢计算机与编程,写博客记录自己的学习历程。
🍎个人主页:小嗷犬的博客
🍊个人信条:为天地立心,为生民立命,为往圣继绝学,为万世开太平。
🥭本文内容:浅谈C、Java与Python之间的小差异
@TOC
1.变量是什么
1.1 C认为变量应该是容器
传统的观点通常将变量比喻成容器,不同类型的容器储存不同的物质,就如同不同数据类型的变量存储不同类型的值一样。
人们用杯子装水,用篮子装菜,这一切都是这么正常。
C也是这样认为的,它认为变量就和这些容器一样,int类型的杯子就不能装double类型的菜,float类型的篮子就不能装char类型的水。
它也是这样做的,在C程序中,将不匹配的值赋值给变量是不合法的,这些值有的会被隐式转换,有的干脆就办不了,程序直接撂挑子不干了。
所以C中的变量分别鲜明,各尽其责,所有变量的空间在诞生时就被分配好,未来将要存储的数据也被注定。
#include <stdio.h>
int main()
{
int a = 10;
float b = 3.14;
char c = 'm';
printf("a:%d, b:%f, c:%c", a, b, c);
return 0;
}
1.2 Python认为变量应该是标签
Python并不认同以往的观点,它觉得传统的观点太刻板了,一个变量的未来不应该在它诞生之际就被限定死了,如同印度的种姓制度一样。
Python主张各变量之间的平等,变量可以存储任何类型的数据,Python也确实是这么做的。
a = 10
print(a)
a = 3.14
print(a)
a = "Hello World!"
print(a)
但一旦认为是变量存储数据后,存储空间的分配又成了困扰Python的难题,一向平等的它不知道该按照什么标准去分配空间,变量们将要存储的数据类型还未可知,如果现在平均分配空间必然会导致今后的一些问题。
Python冥思苦想,终于有一天它想出了一个神奇的构想,它不必将数据需要的空间分配给变量,它只用给变量留足存储自己的空间,而数据则单独找它认领空间,然后将数据空间的地址交给变量保管。这样一个完全不一样的分配制度和所有制度就出现了。
// C中的空间地址是与变量绑定的
#include <stdio.h>
int main()
{
int a = 10;
int b = 10;
printf("int型变量a的值为%d,地址为%p\n", a, &a);
printf("int型变量b的值为%d,地址为%p\n", b, &b);
a += 10;
b += 10;
printf("变化后变量a的值为%d,地址为%p\n", a, &a);
printf("变化后变量b的值为%d,地址为%p\n", b, &b);
return 0;
}
# Python中的空间地址是与值绑定的
a = 10
b = 10
print(f"变量a的值为{a},地址为{id(a)}")
print(f"变量b的值为{b},地址为{id(b)}")
a += 10
b += 10
print(f"变化后变量a的值为{a},地址为{id(a)}")
print(f"变化后变量b的值为{b},地址为{id(b)}")
1.3 Java认为我们不应该关心这个问题
Java打造了一台功能强大的虚拟机,即所谓的JVM,来帮我们解决包括存储空间分配在内的内存管理问题,涉及到内存的操作都由JVM来完成,程序员不用也不能直接操作内存。
Java担心我们不能合理分配利用空间,所以它用自己的方法解决了这个问题。
2.循环与迭代
2.1 传统的循环
C是三者中最年长的语言,它的循环也很具有代表性,循环结构是用来处理一系列相似的事物的,这种重复劳动是十分无趣的,所以C通常是数着数来统计重复的次数,然后看看什么时候能下班。
它使用一种自增运算来数数,用一个整型变量来存储次数,为了方便逐个的自增,它发明了i++和++i这种结构来简化命令,这样的运算也被后来的许多语言效仿。
#include <stdio.h>
int main()
{
for (int i = 0; i < 10; i++)//经典的i++自增
{
printf("%d\n", i);
}
return 0;
}
2.2 继承与发展
Java也是一位比较老的语言,但它的资历没有C老,自身习惯上也受到C很大的影响,循环就是其中之一,C语言中的循环在它这里也能使用。
但它后来就发现了问题,传统的循环要在知道该重复多少次的时候才能按时结束,对于成堆的任务,Java有时候很难事先知道它们有多少。
Java后来发现,是它考虑的太多了,它根本没必要知道要做多少,甚至没必要统计自己现在做了多少了,对于固定量的任务,它只要让循环把它们都做完就行了,没有做完就继续做,做完了就可以提前下班。
于是Java规定了一个新的循环规则,即迭代循环。
public class Main {
public static void main(String[] args) {
int[] numbers = { 5, 3, 8, 4, 6, 1, 7, 2 };
//迭代循环
for (int item : numbers) {
System.out.println(item);
}
}
}
这样的循环似乎并不比传统循环更难使用,但那时候的大多数语言还是以传统循环为主,包括我们的Java。
2.3 新的问题
在其他语言使用传统循环解决问题时,Python似乎有点愁眉苦脸,它又遇到了新的问题。
在之前对变量处理中,Python选择为值分配内存空间,而不是传统的只为变量分配空间。在进行重复工作时,工作的计数器会不断产生新的值,Python要为这些产生的新值都分配空间,在少量的重复工作时,Python感觉还能吃得消,但量级一大,Python就没有这么多空间可以分配了。
按照这种分配方式,计数器的每一次++自增都会产生一个新值,都需要为它分配内存空间,如果循环重复100次,它就要消耗100个数据单位的空间,如果重复1000次呢?10000次呢?Python不敢往下想了,问题迫在眉睫。
Python翻阅古籍,终于找到了以前Java发明的一种不同的循环方式,这就是它要找的解决问题的答案。它发明了一个迭代器range来取代之前的自增计数器,迭代器里的数只有在取用的时候才占据内存,使用完将会在循环结束后释放,这样一来就能解决内存空间的问题了。
# Python迭代循环打印0到9
for i in range(10):
print(i)
// C语言传统循环打印0到9
#include <stdio.h>
int main()
{
for (int i = 0; i < 10; i++)
{
printf("%d\n", i);
}
return 0;
}
实验结果正如同Python想的那样,内存空间的无谓损失被降低了。
为了推广这个新的循环方式,它禁用了传统循环,并废除了i++或是++i自增的用法,鼓励大家使用迭代器range搭配新的循环。
3.尾声
3.1 万物皆对象
三者中最先提到类与对象这个概念的是我们的Java,它认为万物皆是类,包括它自己,从此面向对象编程横空出世,但它还要兼容传统的思想,对此它的JVM对传统的基本数据类型和新的类对象做了不同处理。
对于传统的基本数据类型,JVM将其存在栈中,而将实例化的对象存在堆中。在函数调用时,对于简单的基本数据类型,JVM进行值传递,而对于复杂的对象则采用引用传递。
相应的变量在相互赋值的时候,有的对象会不幸失去引用,如果没有变量引用它,它将变得无法操作,它的存在将没有意义。Java很贴心地安排了JVM去回收它们,这样的回收系统同样也可以用来回收那些不会再被用到的数据。
虽然看起来Java对它们有些不尽人情,用完不需要别人了就回收,但这一切都是为了计算机更好的执行任务。
3.2 C的自增
为了更好的适应新的任务,C也学着Java加入了类与对象的功能,C实现了自身的进化,进化成了C++,它在利用类与对象的便利的同时,并不承认万物皆对象的观点,这也让它保存了C那时面向过程编程的风格。
C++封装了许多常用类来方便我们工作,同时允许我们直接操作内存,没有像JVM虚拟机那样的阻隔,保持着C语言原有的高速,同时更加接近底层。
3.3 Python的好名声
Python是三者中诞生最晚的,它最开始是没有什么知名度的,在C与Java面前只能老老实实当小弟。但年轻人还是不一样,它敢想敢做,首先是实现了各变量之间的平等,让它们能够存储任意数据,然后解决了内存空间的相关问题。
除此之外,它开源自己,广泛吸取大家的意见,Python逐渐变得知名了。
在这些过程中,Python不断改善自身,变得十分人性化,简单的语法、便捷的方法,都吸引着其他程序员去了解,程序员们也能很容易的入门。
现在,Python除了自带的标准库外,支持导入很多的外部库,大大地拓展了Python的应用范围。这一切都仰仗它一直以来的好名声,使得不断有人前赴后继贡献自己的便捷方法,为其编写外部库。
现在Python虽然没有C/C++那样的速度,但是它的便捷性已经不输其他老一辈的语言了,程序员们在写小程序的时候会更加青睐它。
- 点赞
- 收藏
- 关注作者
评论(0)