您好,欢迎访问三七文档
《C和指针》《C专家编程》《C陷阱与缺陷》《C语言编程要点》《编程精粹--Microsoft编写优质无错C程序秘诀》总结说明:总结的知识点主要源于上面的4本书,《编程精粹--Microsoft编写优质无错C程序秘诀》这本书未做总结,该书有清晰版的pdf格式的电子版。--wuliming--2007-04-25wuliming_sc@163.com指针和数组相关概念*************************************************字符与字符串的区别指针与数组1指针与数组2指针和数组的相同与不同用malloc为字符串分配存储空间时的注意事项作为常数的数组声明(c缺陷与陷阱3.3节.在其它部分有包含该节的知识点,了解or略过)字符串常量用字符串常量初始化指针和数组二维数组下标操作的相关概念指向一维、二维数组的指针array_name和&array_name的异同数组作为函数的参数时,不能通过sizeof运算符得到该数组的大小用strlen()求字符串的长度‘char**’和‘constchar**’的兼容性问题空指针相关的问题NULL和NUL的区别未初始化的指针和NULL指针的区别理解函数的声明函数参数的传值调用函数指针作为函数参数的多维数组强制类型转换相关概念可变参数相关问题malloc()、calloc()、realloc()在程序退出main()函数之后,还有可能执行一部分代码吗?总线错误和段错误相关概念数字和字符串之间转换相关的函数*************************************************怎样判断一个字符是数字、字母或其它类别的符号?怎样将数字转换为字符串?怎样将字符串转换为数字?字符串以及内存操作相关函数*************************************************字符串拷贝和内存拷贝函数:strcpystrncpymemcpymemmovememccpybcopy字符串和内存数据比较函数:strcmpstrcasecmpstrncasecmpmemcmpstrcollbcmp连接字符串的函数:strcatstrncat查找字符/字符串的函数:strstrstrchrstrrchrmemchr其它相关的函数:indexrindexstrlenstrdupmemsetbzerostrspnstrcspnstrpbrkstrtok数据结构及算法相关函数qsort()bsearch()lsearch(线性搜索)lfind(线性搜索)srand(设置随机数种子)rand(产生随机数)OTHER*************************************************什么是标准预定义宏?断言assert(表达式)相关概念连接运算符“##”和字符串化运算符#有什么作用?注释掉一段代码的方法Typedef相关概念=不同于==词法分析中的“贪心法”运算符的优先级问题变量的存储类型及初始化相关概念左值和右值相关的概念变量的值和类型相关的概念怎样删去字符串尾部的空格?怎样删去字符串头部的空格?怎样打印字符串的一部分?结构的自引用结构的存储分配边界计算与不对称边界整数溢出返回整数的getchar函数更新顺序文件随机数的相关概念用递归和迭代两种办法解fibonacci字符与字符串的区别(c缺陷与陷阱1.5节)#includestdio.hintmain(){charch='abcdefghijklmnopqrstuvwxyz';charstr[]=abcdefghijklmnopqrstuvwxyz;printf(-----%c-----\n%s\n,ch,str);return0;}编译该程序可以通过,但是会产生警告;输出结过为:-----z-----Abcdefghijklmnopqrstuvwxyz//在Dev-C++4.9.9.2编译环境中可以通过,但是在VC.0中通不过指针与数组1(c缺陷与陷阱3.1节)c语言中的数组值得注意的地方有以下两点:1、c语言中只有一维数组,而且数组的大小必须在编译期间就作为一个常数确定下来(C99标准允许变长数组,GCC编译器中实现了变长数组)。然而,c语言中数组的元素可以是任何类型的对象,当然也可以是另外一个数组。这样,要仿真出一个多维数组就不是一件难事。2、对于一个数组,我们只能够做两件事:确定该数组的大小,以及获得指向该数组下标为0的元素的指针。其他有关数组的操作,哪怕它们乍看上去是以数组下标进行运算的,实际上都是通过指针进行的。换句话说,任何一个数组下标运算都等同于一个对应的指针运算,因此我们完全可以依据指针行为定义数组下标的行为。现在考虑下面的例子:inti;int*p;intcalendar[12][31];上面声明的calendar是一个数组,该数组拥有12个数组类型的元素,其中的每个元素都是一个拥有31个整型元素的数组。因此,sizeof(calendar)的值是:31×12×sizeof(int)。考虑一下,calendar[4]的含义是什么?因为calender是一个有着12个数组类型元素的数组,它的每个数组类型元素又是一个有着31个整型元素的数组,所以calendar[4]是calendar数组的第5个元素,是calendar数组中12个有着31个整型元素的数组之一。因此,calendar[4]的行为也表现为一个有着31个整型元素的数组的行为。例如,sizeof(calendar[4])的结果是:31×sizeof(int)。又如,p=calendar[4];这个语句使指针p指向了数组calendar[4]中下标为0的元素。因为calendar[4]是一个数组,我们可以通过下标的形式来指定这个数组中的元素:i=calendar[4][7],这个语句也可以写成下面这样而表达的意思保持不变:i=*(calendar[4]+7),还可以进一步写成:i=*(*(calendar+4)+7)。下面我们再看:p=calendar;这个语句是非法的,因为calendar是一个二维数组,即“数组的数组”,在此处的上下文中使用calendar名称会将其转换为一个指向数组的指针。而p是一个指向整型变量的指针,两个指针的类型不一样,所以是非法的。显然,我们需要一种声明指向数组的指针的方法。intcalendar[12][31];int(*monthp)[31];monthp=calendar;int(*monthp)[31]语句声明的*monthp是一个拥有31个整型元素的数组,因此,monthp就是一个指向这样的数组的指针。monthp指向数组calendar的第一个元素。HERE指针与数组2(c和指针.P141.)·1、数组的名的值是一个指针常量,不能试图将一个地址赋值给数组名;·2、当数组名作为sizeof操作符的操作数时,sizeof(arrayname)返回的是整个数组的长度,而不是指向数组的指针的长度;·3、当数组名作为单目操作符&的操作数,取一个数组名的地址所产生的是一个指向数组的指针,而不是一个指向某个指针常量值的指针。·4、指针和数组并不总是相等的。为了说明这个概念,请考虑下面这两个声明:inta[5];int*b;a和b能够互换吗?它们都具有指针值,它们都可以进行间接访问和下标操作。但是,它们还是有很大的区别的:声明一个数组时,编译器将根据声明所指定的元素数量为数组保留内存空间,然后再创建数组名,它的值是一个常量,指向这段空间的起始位置。声明一个指针变量时,编译器只为指针本身保留内存空间,它并不为任何整型值分配内存空间。而且,指针变量并未被初始化为指向任何现有的内存空间,如果它是一个自动变量,它甚至根本不会被初始化。把这两个声明用图的方法表示,可以发现它们之间存在显著的不同:ab?因此,上述声明后,表达式*a是完全合法的,但表达式*b却是非法的。*b将访问内存中某个不确定的位置,或者导致程序终止。另一方面,表达式b++可以通过编译,但是a++却不能,因为a的值是一个常量。#includestdio.hintmain(){//注意sizeof(num)的长度应该为10*4=40intnum[]={0,1,2,3,4,5,6,7,8,9};printf(sizeof(num)=%d\n,sizeof(num));//注意sizeof(str)的长度应该为11,包括字符串后面的'\0'charstr[]=0123456789;printf(sizeof(str)=%d\n,sizeof(str));//注意sizeof(str1)的长度应该为10,不包括字符串后面的'\0',但是,最好将字符串的最后一个字符设定为空charstr1[]={'0','1','2','3','4','5','6','7','8','9'};printf(sizeof(str1)=%d\n,sizeof(str1));//&num的类型为'int(*)[10]',表示的是一个指向长度为10的整形数组的指针int(*ptoint)[10]=#printf(sizeof(ptoint)=%d,(*ptoint)[9]=%d\n,sizeof(ptoint),(*ptoint)[9]);//&str的类型为'char(*)[11]',表示的是一个指向长度为11的字符数组的指针,注意str数组的长度是11,而不是10char(*ptostr)[11]=&str;printf(sizeof(ptostr)=%d,(*ptostr)[9]=%c\n,sizeof(ptostr),(*ptostr)[9]);//由于p指向的是数组num[5],所以对下标取负值后,不会超出数组的正常取值范围//该例子也说明了为什么下标检查在c语言中是一项困难的任务:下标引用可以作用于任意的指针,而不仅仅是数组名//作用于指针的下标引用的有效性即依赖于该指针当时恰好指向什么内容,也依赖于下标的值int*p=num+5;printf(p[-1]=%d,p[0]=%d,p[1]=%d\n,p[-1],p[0],p[1]);//下面的表达式中,'num[5]和5[num]'的值是一样的,把它们转换成对等的间接访问表达式,它们都等同于*(num+2)//'5[num]'这个古怪的表达式之所以可行,缘于C实现下标的方法。对编译器来说,这两种形式并无差别//但是,决不应该编写形如'5[num]'的表达式,因为它会大大的影响程序的可读性printf(num[5]=%d,5[num]=%d\n,num[5],5[num]);getchar();return0;}输出结果为:指针和数组的相同与不同(c专家编程.P199.)在实际应用中,数组和指针可以互换的情形要比两者不可互换的情形更为常见。让我们分别考虑“声明”和“使用”这两种情况。声明本身还可以进一步分为3种情况:·外部数组的声明;·数组的定义(定义是声明的一种特殊情况,它分配内存空间,并可能提供一个初始值);·函数参数的声明;extern,如externchara[];不能改写为指针的形式声明定义,如chara[10];不能改写为指针的形式数组函数的参数,可以随意选择数组在表达式中使用的形式或者指针的形式如c=a[i],可以随意选择数组形式或者是指针形式也既是:作为函数参数时、在语句或表达式中使用数组时,我们可以采用数组或者指针的任何一种形式,除此之外的其他情况下,指针和数组不要互换。下面就数组和指针相同的情况做详细的说明:·规则1、表达式中的数组名被编译器当作一个指向该数组第一个元素的指针。假如我们声明:inta[10];int*p=a;就可以通过一下任何一种方式来访问a[i]:p[i]*(p+i)*(a+i)·····事实上,可以采用的方法很多。对数组的引用如a[i]在编译时总是被编译器改写成*(a+i)的形式,C语言标准要求编译器必须具备这个概
本文标题:C语言编程精髓总结
链接地址:https://www.777doc.com/doc-4941430 .html