《自学C语言》 听课笔记

0

一些常见的需要算法知识的C语言程序:

  • 判断一个数字是否是素数
  • 判断一个数字是否是回文数
  • 编程实现求一个十进制数字的二进制形式
  • 求一个数字的每位是奇数的数字取出来组合形成的新数字 求一个数字倒过来的数字

如何看懂一个程序,分三步:

  1. 流程
  2. 每个语句的功能
  3. 试数

如何学习一些需要算法的程序【如何掌握一个程序】

  1. 尝试自己去编程解决它。但要意识到大部分人都是自己无法解决的,这时不要气馁, 也不要自卑, 如果十五分钟还想不出来, 此时我建议您就可以看答案了。

  2. 如果解决不了, 就看答案。 关键是把答案看懂, 这个要花很大的精力,也是我们学习的重点,看懂一个程序要分三步: 流程、每个语句的功能、 试数。

  3. 看懂之后尝试自己去修改程序,并且知道修改之后程序的输出结果的含义,不建议看懂程序之后就立即自己敲程序 。

  4. 照着答案去敲。
  5. 调试错误
  6. 不看答案,自己独立把答案敲出来
  7. 如果程序实在无法彻底理解, 就把它背会,不过无法彻底理解的程序非常少, 我自己几乎没有碰到过

指针

指针就是地址,地址就是指针 地址就是内存单元的编号 指针变量是存放地址的变量 指针和指针变量是两个不同的概念 但是要注意: 通常我们叙述时会把指针变量简称为指针,实际它们含义并不一样 动态内存和静态内存的比较 静态内存是由系统自动分配,由系统自动释放 静态内存是在栈分配的 动态内存是由程序员手动分配,手动释放 动态内存是在堆分配的

程序的规范化

在C语言中我们不遵守编译器就会报错的规定叫规则;约定成俗但不遵守也不会出错的规定叫规范。 比如变量必须先定义后使用,如果我们不定义就直接使用就会出错。 int a =4; a+=5; 这是正确的。 a+=5; 这是错误的。因为没有事先定义变量a。这叫规则,必须得遵守。

再比如左右花括号{}必须按列对齐写,这是规范。你不对齐写,程序会显得很乱,但编译器仍可以照常运行它。 不幸的是,我初学C的一年中所写的程序全是不规范的。当我后来回头再看当年写的程序时,由于它太乱,也没有任何注释,这一年所敲的程序几乎没有任何可读性(程序员把它称之为垃圾程序)。

如果说,当初因为没有人告诉我程序要写规范,为此而付出惨重代价是可以原谅的话,那您看到我的教训仍然坚持创造出这世界上恐怕除了你没人能看懂的“风格”, 对此我只能表示遗憾。

确切的说程序的规范化有两大好处,第一可读性高,无论自己还是他人都可以很容易读懂, 第二可以减少很多不必要的 错误。比如漏掉括号),加错花括号}等错误都可以避免。

程序的规范化有两大原则: 第一是成对书写,如敲完( 马上敲)然后再在(与)的中间敲语句。 这样永远不会漏掉右边 的)。 第二使用缩进。

程序的风格,即程序的规范化涉及到的细节很多,下面我仅以贝尔实验室的风格为例,简明说几点: 1、成对的{}必须按列对齐写,即先写{,再在下几行按列对齐写},最后返回去在{与}的中间写语句。左右括号类似。 2、声明与语句要分开写。 {与}要独占一行 函数与函数之间要空一行分开

/*自编的strcmy()函数*/
# include <stdio.h>
int main(void)
{ /* 无论是 { 还是 }, 我们都要独占一行 */
int mystrcmy(char *, char *);
char str1[20],;
char str2[20]; /* 上面是声明, 与下面的语句要空一行,分开 */
clrscr();
gets(str1);
gets(str2); printf("%d\n",mystrcmp(str1, str2));
return 0; }
/* 主函数与下面的mystrcmp()函数要空行分开 */ int mystrcmp(char *p, char *q)
{
while (*p==*q && *p!='\0')
{ p++; q++; }
return (*p - *q); }
3、地位相等的语句按列对齐写,功能嵌套的按3个空格缩进。
Status OrderInsertMerge(LinkList *L,ElemType e,int(* compare)(term,term)) { /* 按有序判定函数compare()的约定,将值为e的结点插入或合并到升序链表L的
适当位置 */
Position q,s;
if(LocateElemP(*L,e,&q,compare)) /* L中存在该指数项 */ {
q->datacoef+=ecoef; /* 改变当前结点系数的值 */ if(!q->datacoef) /* 系数为0 */
{ s=PriorPos(*L,q); /* s为当前结点的前驱 */
if(!s) /* q无前驱 */
s=(*L)head; DelFirst(L,s,&q); FreeNode(&q);
}
return OK; }
else if(MakeNode(&s,e)) /* 生成结点成功 */
{ InsFirst(L,q,s); return OK;
}
else /* 生成结点失败 */ {
  
 return ERROR; }
}

如果不按规范写那就成了:

int fun(LinkList *L,ElemType e,int(* compare)(term,term))
{ Position q,s; if(LocateElemP(*L,e,&q,compare))
{ q->datacoef+=ecoef; if(!q->datacoef){
s=PriorPos(*L,q); if(!s) s=(*L)head;
DelFirst(L,s,&q); FreeNode(&q); } return OK;
}else /* 第7行 */ if(MakeNode(&s,e))
{ InsFirst(L,q,s);
return OK;
}else return ERROR;
/* 第3行*/
}

当你看到这个函数时,能一眼看出它的功能么? 你能看出第3行的if的范围是么,答案是不可能的。 另外比如定义的特殊用途的变量要在定义时加注释,函数的前面要加功能的注释,在整个程序的前面要写上日期,最重要 的是写上编本程序的目的等等,类似的就不在此赘述了 郝斌 2007-4-6

附录:跨函数使用内存的问题试题

8、下程序中,能够通过调用函数fun,使main函数中的指针变量p指向一个合法的整型单元的是

A) main()
{ int *p; fun(p); ...
}
int fun(int *p) { int s;
p=&s; }
B) main() { int *p;
fun(&p);
... }
int fun(int **q) { ints;
*q=&s; }
C) #include <stdlib.h> main()
{ int *p; fun(&p); ...
}
int fun(int **q)
{ *q=(int *)malloc(4); }
D)
#include <stdlib.h> main()
{ int *p; fun(p); ...
}
int fun(int *p)
{ p=(int *)malloc(sizeof(int)); }

注释: 这个题很有意思。考查了: 1) 指针的指针的使用 2) 动态内存分配与自动变量的内存分配。

2003年4月45题 动态分配的内存必须调用free()函数才能释放,而自动变量一旦跳出它的代码作用范围,就会由编译器自动释放掉。 让我们先看:

A) 选项无论fun()中p的值如何变化,都不会影响到主函数中p的值,因为它是值传递 B) 选项倒是把p的地址&p传递给了fun()函数,但遗憾的是,由于s是个自动变量,当推出fun()函数后,s变量所占内存单元会被会 被释放掉,此时主函数中的p还是没法指向一个合法的int型单元 C) 选项fun()的形参 int **p; 表明p是个指向指针变量的指针变量,即是个指针的指针。 而主函数中的 int *p; 表明p只是个指针变 量,但&p则指向了p,&p也是个指向指针变量p的指针变量,实参和形参类型一致。 fun()的功能是使实参p指向了一个int型变 量, 又由于该int型变量是由malloc()动态分配的,所以推出fun()函数并不会影响实参p的指向, 故C是对 D) 选项犯了和A同样的错误。 真想不到二C还会考到这个知识,哈哈!

本文最后修改时间: 2015-06-28 17:02:44 +0000 (完) CC BY-NC-ND 3.0

若您发现文章中的错误,并愿告知于我,或想与我交流,我的联系方式在: Contacts


上一篇 英语语法个人总结

All The Best

下一篇 Git/GitHub 个人经验