浅谈c语言的知识体系
一.选择结构程序设计以及循环结构设计
1.if语句的使用
1).bool变量与“零值”比较
bool i = FALSE;
那么下面初始化什么比较好?
if( i == 0); if(i == 1) // 1 if( i == TURE); if( i == FALSE); //2 if(i); if(!i); //3
注意:大家知道if语句是靠其后面括号里表达式的值来进行分支跳转。表达式为真,则执行if语句后面紧跟的代码;否则不执行。那显然,第三组写法很好。不会引起误会,也不会因为TRUE,FALSE的不同定义值而出错。
当然,还有float变量和“零值”比较。指针变量与“零值”比较。大家可以自己研究。我在这里就不再讲述了。
2.while循环使用
1).讲述一个例子来剖析一下吧
#include int main() { int a = 1; int b = 2; int c = 2; int t; while (a < b < c) { t = a; a = b; b = t; c--; } printf("%d %d", a, c); system("pause"); return 0; }
分析一下结果会是什么?a的值为1,c的值为0;while循环也关注的是括号里面的值是否为真,那么a
3.for循环的使用
同样用一个例子来分析一下
#include int main() { int i = 0; int arr[10] = {0}; for (i = 0; i < 10; i++) { arr[i] = i; } for (i = 0; i < 10; i++) { printf("%d\n", arr[i]); } system("pause"); return 0; }
当然这个程序是非常简单,但足以说明问题。当i = 10的时候会跳出循环。那么就很简单明了了。当然for循环也可以用while循环改写。
二.数组
数组是储存同一数据类型的一组有序的数据的集合。
数组中需要注意的点:
1.数组用循环语句赋值。同样是上面的for循环,可以自己试试将条件 i<10改为 i<11试试。程序会无条件的奔溃。因为你操作越界了,访问了不属于你自己的内存。
2.数组下标的操作。记住一点的就是,数组的下标是从0开始,并不是1!!!
3.数组和逗号表达式一起迷惑你。
#include int main() { int a[3][2] = { (0, 1), (2, 3), (4, 5) }; int *p = a[0]; printf("%d", p[0]); system("pause"); return 0; }
这个时候的p[0]等于1;为什么,不是应该是0?这就考到了逗号表达式,逗号表达式最后一个值才是整个表达式的值,那么上面的代码就变成了 int a[3][2] = {1,3,5};如果这样说,你是不是就会做对呢?
4.二维数组在内存中的存储
假如二维数组
int arr[3][3] = {0};
二维数组在内存中存储是线性的。我们可以用一维数组的方式来理解二维数组,上述代码代表的二维数组有三个元素,arr[0],arr[1],arr[2],同时每一个元素分别有三个元素,arr[0][0],arr[0][1],arr[0][2]...,那么可以这样理解arr[0],arr[1],arr[2],就是一维数组的数组名,只不过这三个一维数组是有联系的并且在内存中是连续开辟的。
同理我们可以推到三维数组直到n维数组。只要你有时间并且有毅力和决心(意义不大)。哈哈!!
5.字符数组
字符数组的赋值方式,为什么要提字符数组的赋值方式,本人认为这和字符串的操作息息相关,看看两种不同的赋值方式。
1).
char arr[]={'i',' ','a','m',' ','h','a','c','k','e','r'};
可以预见的是,没有‘\0’;那么这个字符数组所储存的字符串是不会使用一系列的字符串操作函数。如果对常见的字符串操作函数不太了解,那么访问下面的链接,https://10799170.blog.51cto.com/10789170/1715083 帮助你了解常见的字符串操作函数的实现与注意的事项。言归正传,第二种赋值方式。
2).
char arr[] = "i am hacker";
这种赋值方式,是最常见也是经的起考验,建议大家就这样使用字符数组的赋值。这种赋值方式不会丢掉“\0”。那么,问题来了,可不可以这样赋值?
char arr[] = {"i am hacker"}; char arr[] { = "i am hacker"};
答案是肯定的,有兴趣的读者可以在《c语言深度剖析》第二章第6小节看到。
6.总结一下:
1).自己给数组在内存中赋值的时候,千万注意内存越界的问题。
2).数组的下标操作。
3).字符数组的“\0”。
4).二维数组的赋值方式,理解方式以及使用方式。
三.指针
指针是地址。我在初学指针的时候,总是分不清楚,指针和指针变量和它指向的地址中的内容的区别,所幸,我在钻研了一下午理解了指针这个东西。得益于一本叫做《c和指针》的书,里面讲述的清楚易懂。
1.一级指针
定义一个一级指针
1
int *p = NULL;
首先要说的是,指针就是地址,指针变量就是用来保存指针指向的那块空间的地址,“*”就是打开这块内存的钥匙。其次,指针的定义和赋值是两码事,不可以混为一谈。最后我也曾经写过自己对指针,数组指针的理解。读者有兴趣可以打开下面的链接
1).指针的笑话(在vc6.0中实现)
int *p = (int *)ox12ff7c; //这个地址必须是内存开始给变量分配地址的第一个,要不是 //不会有作用! *p = NULL; p = NULL;
读者可以自己试试结果是什么。
2).谈谈指针的安全
指针是可以指向内存中的任何地址的,但是在一个程序中,总是只有自己的栈帧,或者储存在静态去和堆上的内存。如果一不小心访问到不属于自己的内存,并改掉其中的值。对于一个大型程序来说,调试都是一个非常庞大的工作。所以,在使用指针的时候千万要小心。杜绝野指针!!
3).const和assert修饰指针
不需要修改指针指向的值,请必须加上const修饰
const int *p; int const *p; int * const p; const int * const p;
读者可以尝试着分析上面的语句代表着什么。
assert只在debug版本生效,在release版本没有意义。在debug版本可以帮助我们检验指针的有效性。大公司面试的时候很注重这一定。
自己在学习的时候看的书是《c语言深度剖析》和《c和指针》。读者可以自己进一步了解。
四.内存操作
说到内存操作,不得不提到栈帧。读者可以参考我原来写过的一片博文 附上url
https://10799170.blog.51cto.com/10789170/1715186
那么我们在这里就主要讨论讨论malloc开辟内存时应该注意的问题。
1).动态开辟内存后,需要判断内存是否开辟成功。
如果内存没有开辟成功,我们直接使用会造成访问越界;
2).在使用完成后必须使用free函数释放掉内存,其原理是将指针与内存切断联系。
会造成内存泄漏。很危险!
3).使用完成后指针应赋为NULL;杜绝野指针。
没有及时将指针赋值为NULL,指针会指向“垃圾”内存!
五.函数
函数这一部分,本来是不愿意拿出来说的,毕竟每一个学习c语言的人,几乎都在写函数。那么我们就总结一下函数的好处吧。
1).降低复杂性
2).避免重复代码
3).限制改动带来的影响。
4).隐含顺序
5).改进性能
6).进行集中控制
读者需要了解请参照《c语言深度剖析》。
六.结构体
说说我对结构体的感悟吧,不再谈语法。
1.结构体是一个自定义的类型,可以用这个类型定义结构体变量。
2.结构体不同于数组,它可以将集中不同的数据类型集中在一起。
3.是实现链表的结点。
4.本人用结构体实现过一个简易的电话本,附上url