编程语言杂谈
大家好啊,今天来点大家想看的东西啊。
世界上有很多门编程语言,每门语言都有着自己的优缺点,使用场景也各不相同。国内教学一般会选择 C 语言作为入门语言,后面再教 Java、Python 等。自己写了这么久的代码,接触过不少语言,总归是有一些想法的,在此分享一下。注意,本文属于杂谈,行文较为随意。
我是初中开始对编程感兴趣的,于是购买了《C 语言从入门到精通》这本书 —— 然后写完 Hello World 就再也没看过了。只能说还是太小吧,思考能力还是不够,以及教材本身的落后。落后在哪我真的很难说,国内的大多数教材总是严重滞后于生产实际,这大家有目共睹。就开发工具来说,书中推荐的是 Visual Studio 6.0,我嘞个老东西啊。
回到 C 语言本身,现在来谈一些缺点。是的,我其实并不喜欢 C 语言,虽说是高级语言,但它离底层实在是太近了,我觉得有点不够“高级”。
字符串
C 语言中字符串的定义是这样的:
char s[6] = {'H', 'e', 'l', 'l', 'o', '\0'}; // 定义长度为 6 的字符串
char s2[5] = {'H', 'e', 'l', 'l', 'o'}; // 定义长度为 5 的字符串,未以 '\0' 结尾在学 C 的时候,我就觉得字符串的处理实在是太麻烦了。C 语言中没有专门的字符串类型,字符串其实就是字符数组,结尾用一个特殊的空字符 \0 来标识结束。为什么?因为 C 语言是支持越界访问数组的,没有 \0 就不知道字符串在哪结束。
我们来对比一下 Python 中用 list 来模拟字符串的例子:
s = ['H', 'e', 'l', 'l', 'o'] # 定义长度为 5 的字符串如果我们访问第六个元素 s[5],Python 会抛出一个 IndexError 异常,告诉我们索引超出了范围。而在 C 语言中,访问 s[5] 我们能知道字符串该结束了,而访问 s2[5] 直接就是 未定义行为 了,很可能出现安全漏洞和崩溃。
YouTube 视频 The worst programming language of all time 喷了两个小时的 C++,我要笑死了。
其实,正如 Rust 程序设计语言 所说,字符串并不简单。你还需要去处理编码、Unicode 等问题,我想很少有人没有见到一堆乱码。
修改传入的参数
先说结论:我坚决反对修改传进来的值。这种做法既不直观,也不安全,更违背了函数式编程的基本原则。
export const authOptions: NextAuthOptions = {
callbacks: {
jwt({ token, user }) {
// The arguments user, account, profile and isNewUser are only passed the first time this callback is called on a new session,
// after the user signs in. In subsequent calls, only token will be available.
// token : The current JSON Web Token
// user : The user object returned by the callback for the session (only available on first sign in)
if (user) {
token = { ...token, ...user };
}
return token;
},
session({ session, token }) {
if (session.user == undefined) {
session.user = {};
session.user.name = token.name;
} else {
session.user.name = token.name;
}
session.step = token.step;
session.secretCode = token.secretCode;
session.accessToken = token.accessToken;
session.refreshToken = token.refreshToken;
session.mfaSession = token.mfaSession;
return session;
},
},
};在 C 语言中,参数传递有两种方式:按值传递和按指针传递。按值传递还好,至少不会影响到原始数据。但是按指针传递就麻烦了,你永远不知道一个函数会不会偷偷地修改你传进去的参数。
void mysterious_function(int *arr, int size) {
// 这个函数做了什么?
// 它会修改数组内容吗?
// 只有看实现才知道!
}这种设计带来的问题是显而易见的:
- 可读性差:仅从函数调用无法判断参数是否会被修改
- 调试困难:当数据被意外修改时,很难追踪到是哪个函数干的
- 副作用泛滥:函数不再是纯函数,而是充满了副作用的“黑盒”
更要命的是,C 语言没有 const 关键字的严格限制(早期版本),也没有明确的输入输出参数标记。你拿到一个函数,完全不知道它会对你的数据做什么。
undefined 还是 null?
在 JavaScript/TypeScript 中,我们有 undefined 和 null,他们到底有什么区别?我从接触到 JavaScript/TypeScript 开始就一直搞不清楚。
直到我认识到 Map 这个数据结构,才在获取值时发现了两者的区别:
糟糕的 NullPointerException
Java 语言中,NullPointerException(简称 NPE)是最常见的运行时错误之一。NPE 通常发生在你试图访问一个为 null 的对象的成员或方法时。
String str = null;
int length = str.length(); // 这里会抛出 NullPointerExceptionNPE 的问题在于它通常是在运行时才被发现的,这使得调试变得非常困难。更糟糕的是,NPE 可能会在程序的任何地方发生,导致程序崩溃或产生不可预期的行为。