同鞋们新年快乐。在这辞旧迎新之际,嘿嘿,我来写点东西给你们。上个学期我们学了 C 语言,我也有幸拜读了各位的一些代码,发现了一些问题,零零散散的,可能说得也不能让大家瞬间理解一些东西。不过不要紧,语言要积累嘛,一点一点说哈,有问题就提在评论里哈。如果有高手路过,还请绕道哈。


首先,大家注意下面这行代码:

1
char *str = "Hello";

我看到一些同学写代码的时候写成这样,不知道是不是谭浩强提倡的啊……不过希望大家不要写出这样的代码,因为那个字面常量 “Hello” 在内存中的位置没法预料,而你的指针又经常传递到这传递到那的,指不定会发生什么意外。况且你让一个指针指向一个常量,你也必须使用 const char * 才对啊。然而你应该这样写:

1
const char str[] = "Hello";

这样是多么和谐啊。


然后,我还见过一些同学写出了这样的代码:

1
2
3
4
char *func() {
    char buffer[] = "Hello";
    return buffer;
}

有同学就理直气壮地说,返回值就是字符指针,字符串也是字符指针,那这样有错吗?其实这样的代码语法上应该是没错的,但是,buffer 所指向的缓冲区是在栈空间里的,当这个函数返回之后,这个缓冲区就会被编译器认为是被释放了,这时就有各种奇怪的东西进入到缓冲区里。虽然返回的地址的确是这个缓冲区的首地址,但是缓冲区的内容已经不可预料了,而且对该位置的内存操作是否有权限还不知道。大家在用一些软件的时候经常会遇到这样的对话框:

该内存不能为“read”

该内存不能为“read”

其实出现这样的情况,很有可能是程序员编程的时候指针没有用好。为了你将来的用户不 curse 你,就不要像那样写啦。


对于上面那种情况,可能有同学就想问:“如果我需要返回一个缓冲区,怎么办?”下面这些代码,能够正确处理这个问题:

1
2
3
4
5
char *func() {
    char *buffer = (char *)malloc(1024);
    strcpy(buffer, "Hello");
    return buffer;
}

此时函数产生的返回值,能够指向一个正确的缓冲区,并且缓冲区内保证是 “Hello”, 没有谁会去释放这个缓冲区。但是注意啦,也许不让编译器释放缓冲区是你的初衷,但是,如果你不释放它,谁来释放它呢?没有人释放它,就会产生很多的内存垃圾,然而 C 和 C++ 目前都是没有垃圾收集器的。所以平时你如果发现一些程序运行时间越长,占用内存越来越大,那么做这些程序的程序员可能就是没有清理这样的垃圾。

所以正确的方法是在使用 func() 之后,一定要注意将其返回的内容给释放了:

1
2
3
4
5
6
7
int main() {
    char *p = func();
    /* Some operations to p. */
    printf("%s\n", p);
    free(p); // This step is so necessary to be taken.
    return 0;
}

其实虽然说这样做是正确的,但是并不是“有效率的”。在 C 或 C++ 中有一个约定,就是“谁创建的缓冲区就由谁释放”。这个“谁”当然不是指“你”了,而是比较抽象的东西,比如一个函数,或者一个模块,或者一个功能块等。上面的例子中 func() 与 main() 并不是一个“整体”,那么由 func() 来开辟缓冲区,又由 main() 来释放,是比较不合理的。

不过说这些似乎有点为时过早了哈,大家有点印象就好,能做对就值得鼓励。


最后说一点东西,呃,大家可以认为我是吹毛求疵。很多同学把 main 函数声明为 void 的返回值,其实这是不对的。还有李宏宇老师在第一节课上课的时候说“谭浩强的书都是按照 ANSI C 标准写的”也是不对的。为什么这两件事都是不对的呢?因为呀,谭浩强的书上的所有 main 都是 void 返回值的。其实这也不是谭浩强的错,他写书的时候,这个 C 语言标准比较混乱,16 位机器还横行于世(从他说 int 是 2 字节就可以看出来了)。而且他现在老了,不想修订书了,也不用计较了哈。人家高德纳不也把 1 字节说成 6 位嘛。

扯远了,大家可能会问 main 函数应该怎样声明,以下两种就是 main 函数的 ANSI C 标准形式:

1
2
int main();
int main(int argc, char *argv[]);

其中第二种形式,可以获取程序执行的参数,具体就不多说啦。那么现在有两个问题,第一个是返回值有什么用,第二个是返回值要返回多少。其实这返回值通常是在调试的时候使用的,用户并不能看到一个程序的返回值,而你在调试的时候,当程序返回后,在 IDE 的输出窗口往往会显示程序以什么返回值结束。这个返回值就是 main 函数的返回值。通常我们如果不是很计较的话,返回 0 就可以了。在 stdlib.h 里有一个常量叫 EXIT_SUCCESS, 它的值就是 0. 我比较喜欢返回这个常量。

其实什么 ANSI C 标准也是浮云一朵,ANSI 似乎是美国的某个机构,所以它对中文似乎都不是很友好。到后期大家会学到 wchar_t 这样的数据类型,这些是为世界上很多非英语语言设计的数据类型,如果要写 UNICODE 程序,那么就必须使用这种数据类型。(其实 char 也是可以处理中文的,但是有一定局限性,这似乎叫 MBSC.)然而它又是与 char 相对的,所以写出 UNICODE 程序就必须使用类似于 int main(int argc, wchar_t *argv[]); 这样的入口函数。当然现在似乎没有这样的入口函数。在这个方面还是微软给力一点,Windows 应用程序的入口函数是 WinMain. 其实还有一个 UNICODE 的入口函数似乎叫 wWinMain.

OK 了,如果我有哪里说不清楚,或者说错了,欢迎无情地指正。谢谢大家。

原创文章,转载请注明来源:http://euyuil.com/2040/notes-for-c-language-beginners/