C语言编程总结.pdf
《C语言编程总结.pdf》由会员分享,可在线阅读,更多相关《C语言编程总结.pdf(92页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、 C C 和指针和指针 C C 专家编程专家编程 C C 陷阱与缺陷陷阱与缺陷 C C 语言编程要点语言编程要点 编程精粹编程精粹-MicrosoftMicrosoft 编写优质无错编写优质无错 C C 程序秘诀程序秘诀 总 结 说明:总结的知识点主要源于上面的 4 本书,编程精粹-Microsoft 编写优质无错C 程序秘诀这本书未做总结,该书有清晰版的 pdf 格式的电子版。-wuliming -2007-04-25 wuliming_ 指针和数组相关概念*字符与字符串的区别 指针与数组 1 指针与数组 2 指针和数组的相同与不同 用 malloc 为字符串分配存储空间时的注意事项 作为常
2、数的数组声明(c 缺陷与陷阱 3.3 节.在其它部分有包含该节的知识点,了解 or 略过)字符串常量 用字符串常量初始化指针和数组 二维数组下标操作的相关概念 指向一维、二维数组的指针 array_name 和&array_name 的异同 数组作为函数的参数时,不能通过 sizeof 运算符得到该数组的大小 用 strlen()求字符串的长度 char*和 const char*的兼容性问题 空指针相关的问题 NULL 和 NUL 的区别 未初始化的指针和 NULL 指针的区别 理解函数的声明 函数参数的传值调用 函数指针 作为函数参数的多维数组 强制类型转换相关概念 可变参数相关问题 ma
3、lloc()、calloc()、realloc()在程序退出 main()函数之后,还有可能执行一部分代码吗?总线错误和段错误相关概念 数字和字符串之间转换相关的函数*怎样判断一个字符是数字、字母或其它类别的符号?怎样将数字转换为字符串?怎样将字符串转换为数字?字符串以及内存操作相关函数*字符串拷贝和内存拷贝函数:strcpy strncpy memcpy memmove memccpy bcopy 字符串和内存数据比较函数:strcmp strcasecmp strncasecmp memcmp strcoll bcmp 连接字符串的函数:strcat strncat 查找字符/字符串的函数
4、:strstr strchr strrchr memchr 其它相关的函数:index rindex strlen strdup memset bzero strspn strcspn strpbrk strtok 数据结构及算法相关函数 qsort()bsearch()lsearch(线性搜索)lfind(线性搜索)srand(设置随机数种子)rand(产生随机数)OTHER*什么是标准预定义宏?断言 assert(表达式)相关概念 连接运算符“#”和字符串化运算符#有什么作用?注释掉一段代码的方法注释掉一段代码的方法 Typedef 相关概念=不同于=词法分析中的“贪心法”运算符的优先级问
5、题 变量的存储类型及初始化相关概念 左值和右值相关的概念 变量的值和类型相关的概念 怎样删去字符串尾部的空格?怎样删去字符串头部的空格?怎样打印字符串的一部分?结构的自引用 结构的存储分配 边界计算与不对称边界 整数溢出 返回整数的 getchar 函数 更新顺序文件 随机数的相关概念 用递归和迭代两种办法解 fibonacci 字符与字符串的区别字符与字符串的区别(c 缺陷与陷阱缺陷与陷阱 1.5 节节)#include int main()char ch=abcdefghijklmnopqrstuvwxyz;char str=abcdefghijklmnopqrstuvwxyz;print
6、f(-%c-n%sn,ch,str);return 0;编译该程序可以通过,但是会产生警告;输出结过为:编译该程序可以通过,但是会产生警告;输出结过为:-z-Abcdefghijklmnopqrstuvwxyz /在在 Dev-C+4.9.9.2 编译环境中可以通过,但是在编译环境中可以通过,但是在 VC.0 中通不过中通不过 指针与数组指针与数组 1(c 缺陷与陷阱缺陷与陷阱 3.1 节节)c 语言中的数组值得注意的地方有以下两点:语言中的数组值得注意的地方有以下两点:1、c语言中只有一维数组语言中只有一维数组,而且数组的大小必须在编译期间就作为一个常数确定下来而且数组的大小必须在编译期间就
7、作为一个常数确定下来(C99 标准允许变长数组,标准允许变长数组,GCC 编译器中实现了变长数组编译器中实现了变长数组)。然而,。然而,c 语言中数组的元语言中数组的元素可以是任何类型的对象,当然也可以是另外一个数素可以是任何类型的对象,当然也可以是另外一个数组。这样,要仿真出一个多维数组就不是一件难事。组。这样,要仿真出一个多维数组就不是一件难事。2、对于一个数组,我们只能够做两件事:确定该数组的大小,以及获得指向该数组下标为、对于一个数组,我们只能够做两件事:确定该数组的大小,以及获得指向该数组下标为 0 的元素的指针。的元素的指针。其他有关数组的操作,哪怕它们乍看上去是以数组下标进行运算
8、的,实际上都是通过指针进行的。换句话说,其他有关数组的操作,哪怕它们乍看上去是以数组下标进行运算的,实际上都是通过指针进行的。换句话说,任何一个数组下标运算都等同于一个对应的指针运算,因此我们完全可以依据指针行为定义数组下标的行为。任何一个数组下标运算都等同于一个对应的指针运算,因此我们完全可以依据指针行为定义数组下标的行为。现在考虑下面的例子:int i;int*p;int calendar1231;上面声明的 calendar 是一个数组,该数组拥有该数组拥有 12 个数组类型的元素,其中的每个元素都是一个拥有个数组类型的元素,其中的每个元素都是一个拥有 31 个整个整型元素的数组。型元素
9、的数组。因此,sizeof(calendar)的值是:3112sizeof(int)。考虑一下,calendar4的含义是什么?因为 calender 是一个有着 12 个数组类型元素的数组,它的每个数组类型元素又是一个有着 31 个整型元素的数组,所以 calendar4是 calendar 数组的第 5 个元素,是 calendar数组中 12 个有着 31 个整型元素的数组之一。因此,calendar4的行为也表现为一个有着 31 个整型元素的数组的行为。例如,sizeof(calendar4)的结果是:31sizeof(int)。又如,p=calendar4;这个语句使指针 p 指向了
10、数组 calendar4中下标为 0 的元素。因为 calendar4是一个数组,我们可以通过下标的形式来指定这个数组中的元素:i=calendar47,这个语句也可以写成下面这样而表达的意思保持不变:i=*(calendar4+7),还可以进一步写成:,还可以进一步写成:i=*(*(calendar+4)+7)。下面我们再看:p=calendar;这个语句是非法的,因为 calendar 是一个二维数组,即“数组的数组”,在此处的上下文中使用 calendar 名称会将其转换为一个指向数组的指针。而 p 是一个指向整型变量的指针,两个指针的类型不一样,所以是非法的。显然,我们需要一种声明指向
11、数组的指针的方法。int calendar1231;int(*monthp)31;monthp=calendar;int(*monthp)31 语句声明的*monthp 是一个拥有 31 个整型元素的数组,因此,monthp 就是一个指向这样的数组的指针。monthp 指向数组 calendar 的第一个元素。HERE 指针与数组指针与数组 2(c 和指针和指针.P141.)1、数组的名的值是一个指针常量,不能试图将一个地址赋值给数组名;2、当数组名作为 sizeof 操作符的操作数时,sizeof(arrayname)返回的是整个数组的长度,而不是指向数组的指针的长度;3、当数组名作为单目操
12、作符&的操作数,取一个数组名的地址所产生的是一个指向数组的指针,而不是一个指向某个指针常量值的指针。4、指针和数组并不总是相等的。为了说明这个概念,请考虑下面这两个声明:int a5;int*b;a 和 b 能够互换吗?它们都具有指针值,它们都可以进行间接访问和下标操作。但是,它们还是有很大的区别的:声明一个数组时,编译器将根据声明所指定的元素数量为数组保留内存空间,然后再创建数组名,它的值是一个常量,指向这段空间的起始位置。声明一个指针变量时,编译器只为指针本身保留内存空间,它并不为任何整型值分配内存空间。而且,指针变量并未被初始化为指向任何现有的内存空间,如果它是一个自动变量,它甚至根本不
13、会被初始化。把这两个声明用图的方法表示,可以发现它们之间存在显著的不同:a b?因此,上述声明后,表达式*a 是完全合法的,但表达式*b 却是非法的。*b 将访问内存中某个不确定的位置,或者导致程序终止。另一方面,表达式 b+可以通过编译,但是 a+却不能,因为 a 的值是一个常量。#include int main()/注意注意 sizeof(num)的长度应该为的长度应该为 10*4=40 int num=0,1,2,3,4,5,6,7,8,9;printf(sizeof(num)=%dn,sizeof(num);/注意注意 sizeof(str)的长度应该为的长度应该为 11,包括字符串
14、后面的包括字符串后面的0 char str=0123456789;printf(sizeof(str)=%dn,sizeof(str);/注意注意 sizeof(str1)的长度应该为的长度应该为 10,不包括字符串后面的不包括字符串后面的0,但是,最好将字符串的最后一个字符设定为空,但是,最好将字符串的最后一个字符设定为空 char str1=0,1,2,3,4,5,6,7,8,9;printf(sizeof(str1)=%dn,sizeof(str1);/&num 的类型为的类型为int(*)10,表示的是一个指向长度为,表示的是一个指向长度为 10 的整形数组的指针的整形数组的指针 in
15、t(*ptoint)10=#printf(sizeof(ptoint)=%d,(*ptoint)9=%dn,sizeof(ptoint),(*ptoint)9);/&str 的类型为的类型为char(*)11,表示的是一个指向长度为,表示的是一个指向长度为 11 的字符数组的指针,注意的字符数组的指针,注意 str 数组的长度是数组的长度是 11,而不是,而不是 10 char(*ptostr)11=&str;printf(sizeof(ptostr)=%d,(*ptostr)9=%cn,sizeof(ptostr),(*ptostr)9);/由于由于 p 指向的是数组指向的是数组 n
16、um5,所以对下标取负值后,不会超出数组的正常取值范围,所以对下标取负值后,不会超出数组的正常取值范围 /该例子也说明了为什么下标检查在该例子也说明了为什么下标检查在 c 语言中是一项困难的任务:下标引用可以作用于任意的指针,而不仅仅是数语言中是一项困难的任务:下标引用可以作用于任意的指针,而不仅仅是数组名组名 /作用于指针的下标引用的有效性即依赖于该指针当时恰好指向什么内容,也依赖于下标的值作用于指针的下标引用的有效性即依赖于该指针当时恰好指向什么内容,也依赖于下标的值 int*p=num+5;printf(p-1=%d,p0=%d,p1=%d n,p-1,p0,p1);/下面的表达式中,下
17、面的表达式中,num5和和 5num的值是一样的,把它们转换成对等的间接访问表达式,它们都等同于的值是一样的,把它们转换成对等的间接访问表达式,它们都等同于*(num+2)/5num这个古怪的表达式之所以可行,缘于这个古怪的表达式之所以可行,缘于 C 实现下标的方法。对编译器来说,这两种形式并无差别实现下标的方法。对编译器来说,这两种形式并无差别 /但是,决不应该编写形如但是,决不应该编写形如5num的表达式,因为它会大大的影响程序的可读性的表达式,因为它会大大的影响程序的可读性 printf(num5=%d,5num=%d n,num5,5num);getchar();return 0;输出
18、结果为:指针和数组的相同与不同指针和数组的相同与不同(c 专家编程专家编程.P199.)在实际应用中,数组和指针可以互换的情形要比两者不可互换的情形更为常见。让我们分别考虑“声明”和“使用”这两种情况。声明本身还可以进一步分为 3 种情况:外部数组的声明;数组的定义(定义是声明的一种特殊情况,它分配内存空间,并可能提供一个初始值);函数参数的声明;extern,如 extern char a;不能改写为指针的形式 声明 定义,如 char a10;不能改写为指针的形式 数组 函数的参数,可以随意选择数组 在表达式中使用 的形式或者指针的形式 如 c=ai,可以随意选择数组 形式或者是指针形式
19、也既是:作为函数参数时、在语句或表达式中使用数组时,我们可以采用数组或者指针的任何一种形式,除此作为函数参数时、在语句或表达式中使用数组时,我们可以采用数组或者指针的任何一种形式,除此之外的其他情况下,指针和数组不要互换之外的其他情况下,指针和数组不要互换。下面就数组和指针相同的情况做详细的说明:规则规则 1、表达式中的数组名被编译器当作一个指向该数组第一个元素的指针。、表达式中的数组名被编译器当作一个指向该数组第一个元素的指针。假如我们声明:int a10;int*p=a;就可以通过一下任何一种方式来访问 ai:pi *(p+i)*(a+i)事实上,可以采用的方法很多。对数组的引用如 ai
20、在编译时总是被编译器改写成*(a+i)的形式,C 语言标准要求编译器必须具备这个概念性的行为。编译器自动把下标值的步长调整到数组元素的大小。如果整型数的长度是 4 个字节,那么 ai+1和 ai在内存中的距离就是 4。对起始地址执行加法操作之前,编译器会负责计算每次增加的步长。这就是为什么指针总是有类型限制,每个指针只能指向一种类型的原因所在,因为编译器需要知道对指针进行解除引用操作时应该取几个字节,以及每个下标的步长应取几个字节。规则规则 2、下标总是和指针的偏移量相同。、下标总是和指针的偏移量相同。把数组下标作为指针加偏移量是 c 语言从 BCPL(C 语言的祖先)继承过来的技巧。在人们的
21、常规思维中,在运行时增加对 c 语言下标的范围检查是不切实际的。因为取下标操作只是表示将要访问该数组,但并不保证一定要访问。而且程序员完全可以使用指针来访问数组,从而绕过下标操作符。在这种情况下,数组下标范围检测并不能检测所有对数组的访问的情况。事实上,下标范围检测被认为不值得加入到 c 语言当中。还有一个说法是,在编写数组算法时,使用指针比使用数组更有效率。这个颇为人们所接收的说法在通常情况下是错误的。使用现代的产品质量优化的编译器,一维数组和指针引用所产生的代码并不具有显著的差别。不管怎样,数组下标是定义在指针的基础上,所以优化器常常可以把它转化为更有效率的指针表达式,并生成相同的机器指令
22、。规则规则 3、在函数参数的声明中,数组名被编译器当作指向该数组第一个元素的指针。、在函数参数的声明中,数组名被编译器当作指向该数组第一个元素的指针。在函数形参定义这个特殊情况下,编译器必须把数组形式改写成指向数组第一个元素的指针形式。编译器只向函数传递数组的地址,而不是整个数组的拷贝。这种转换意味着在声明函数的时候,以下三种形式都是合法的(同时无论实参是数组还是真的指针也都是合法的):my_function(int*turnip)my_function(int turnip)my_function(int turnip200)用用 malloc 为字符串分配存储空间时的注意事项为字符串分配存
23、储空间时的注意事项(c 缺陷与陷阱缺陷与陷阱 3.2 节节)作为常数的数组声明作为常数的数组声明(c 缺陷与陷阱缺陷与陷阱 3.3 节节.在其它部分有包含该节的知识点,了解在其它部分有包含该节的知识点,了解 or 略过略过)字符串常量字符串常量(c 和指针和指针.P269.)当一个字符串常量出现在表达式中时,它的值是指针常量。编译器把该字符串的一份拷贝存储在内存的某个位当一个字符串常量出现在表达式中时,它的值是指针常量。编译器把该字符串的一份拷贝存储在内存的某个位置,并存储一个指向第一个字符的指针。我们可以对字符串常量进行下标引用、间接访问以及指针运算。置,并存储一个指向第一个字符的指针。我们
24、可以对字符串常量进行下标引用、间接访问以及指针运算。“xyz”+1“xyz”+1 字符串常量实际上是个指针,这个表达式计算“指针值加上 1”的值。它的结果也是个指针,指向字符串中的第二个字符 y*“xyz”“xyz”对一个指针执行间接访问操作时,其结果就是指针所指向的内容。字符串常量的类型是“指向字符的指针”,所以这个间接访问的结果就是它所指向的字符:x。注意表达式的结果并不是整个字符串,而只是它的第一个字符。“xyzxyz”22 同样可以推断出上面这个表达式的值就是字符 z。#include /接受一个无符号整型值,把它转换成字符,并打印出来接受一个无符号整型值,把它转换成字符,并打印出来/
25、如果是打印如果是打印 16 进值的数,可以用这种方法:进值的数,可以用这种方法:putchar(0123456789ABCDEF value%16 )void binary_to_ascii(unsigned long value)unsigned long quotient;quotient=value/10;if(quotient!=0)binary_to_ascii(quotient);putchar(0123456789 value%10 );int main()/字符串常量实际上是个指针,这个表达式计算字符串常量实际上是个指针,这个表达式计算指针值加上指针值加上 1的值。它的结果也是
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 语言 编程 总结
限制150内