C语言经典书籍总结合辑.docx

上传人:b****1 文档编号:2395289 上传时间:2023-05-03 格式:DOCX 页数:115 大小:2.72MB
下载 相关 举报
C语言经典书籍总结合辑.docx_第1页
第1页 / 共115页
C语言经典书籍总结合辑.docx_第2页
第2页 / 共115页
C语言经典书籍总结合辑.docx_第3页
第3页 / 共115页
C语言经典书籍总结合辑.docx_第4页
第4页 / 共115页
C语言经典书籍总结合辑.docx_第5页
第5页 / 共115页
C语言经典书籍总结合辑.docx_第6页
第6页 / 共115页
C语言经典书籍总结合辑.docx_第7页
第7页 / 共115页
C语言经典书籍总结合辑.docx_第8页
第8页 / 共115页
C语言经典书籍总结合辑.docx_第9页
第9页 / 共115页
C语言经典书籍总结合辑.docx_第10页
第10页 / 共115页
C语言经典书籍总结合辑.docx_第11页
第11页 / 共115页
C语言经典书籍总结合辑.docx_第12页
第12页 / 共115页
C语言经典书籍总结合辑.docx_第13页
第13页 / 共115页
C语言经典书籍总结合辑.docx_第14页
第14页 / 共115页
C语言经典书籍总结合辑.docx_第15页
第15页 / 共115页
C语言经典书籍总结合辑.docx_第16页
第16页 / 共115页
C语言经典书籍总结合辑.docx_第17页
第17页 / 共115页
C语言经典书籍总结合辑.docx_第18页
第18页 / 共115页
C语言经典书籍总结合辑.docx_第19页
第19页 / 共115页
C语言经典书籍总结合辑.docx_第20页
第20页 / 共115页
亲,该文档总共115页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

C语言经典书籍总结合辑.docx

《C语言经典书籍总结合辑.docx》由会员分享,可在线阅读,更多相关《C语言经典书籍总结合辑.docx(115页珍藏版)》请在冰点文库上搜索。

C语言经典书籍总结合辑.docx

C语言经典书籍总结合辑

《C和指针》

《C专家编程》

《C陷阱与缺陷》

《C语言编程要点》

《编程精粹--Microsoft编写优质无错C程序秘诀》

 

总结

 

指针和数组相关概念

*************************************************

字符与字符串的区别

指针与数组1

指针与数组2

指针和数组的相同与不同

用malloc为字符串分配存储空间时的注意事项

作为常数的数组声明(c缺陷与陷阱3.3节.在其它部分有包含该节的知识点,了解or略过)

字符串常量

用字符串常量初始化指针和数组

二维数组下标操作的相关概念

指向一维、二维数组的指针

array_name和&array_name的异同

数组作为函数的参数时,不能通过sizeof运算符得到该数组的大小

用strlen()求字符串的长度

‘char**’和‘constchar**’的兼容性问题

空指针相关的问题

NULL和NUL的区别

未初始化的指针和NULL指针的区别

理解函数的声明

函数参数的传值调用

函数指针

作为函数参数的多维数组

强制类型转换相关概念

可变参数相关问题

malloc()、calloc()、realloc()

在程序退出main()函数之后,还有可能执行一部分代码吗?

总线错误和段错误相关概念

 

数字和字符串之间转换相关的函数

*************************************************

怎样判断一个字符是数字、字母或其它类别的符号?

怎样将数字转换为字符串?

怎样将字符串转换为数字?

 

字符串以及内存操作相关函数

*************************************************

字符串拷贝和内存拷贝函数:

strcpy

strncpy

memcpy

memmove

memccpy

bcopy

字符串和内存数据比较函数:

strcmp

strcasecmp

strncasecmp

memcmp

strcoll

bcmp

连接字符串的函数:

strcat

strncat

查找字符/字符串的函数:

strstr

strchr

strrchr

memchr

其它相关的函数:

index

rindex

strlen

strdup

memset

bzero

strspn

strcspn

strpbrk

strtok

数据结构及算法相关函数

qsort()

bsearch()

lsearch(线性搜索)

lfind(线性搜索)

srand(设置随机数种子)

rand(产生随机数)

OTHER

*************************************************

什么是标准预定义宏?

断言assert(表达式)相关概念

连接运算符“##”和字符串化运算符"#"有什么作用?

注释掉一段代码的方法

Typedef相关概念

=不同于==

词法分析中的“贪心法”

运算符的优先级问题

变量的存储类型及初始化相关概念

左值和右值相关的概念

变量的值和类型相关的概念

怎样删去字符串尾部的空格?

怎样删去字符串头部的空格?

怎样打印字符串的一部分?

结构的自引用

结构的存储分配

边界计算与不对称边界

整数溢出

返回整数的getchar函数

更新顺序文件

随机数的相关概念

用递归和迭代两种办法解fibonacci

字符与字符串的区别(c缺陷与陷阱1.5节)

#include

intmain()

{

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的第一个元素。

 

指针与数组2(c和指针.P141.)

·1、数组的名的值是一个指针常量,不能试图将一个地址赋值给数组名;

·2、当数组名作为sizeof操作符的操作数时,sizeof(arrayname)返回的是整个数组的长度,而不是指向数组的指针的长度;

·3、当数组名作为单目操作符&的操作数,取一个数组名的地址所产生的是一个指向数组的指针,而不是一个指向某个指针常量值的指针。

·4、指针和数组并不总是相等的。

为了说明这个概念,请考虑下面这两个声明:

inta[5];

int*b;

a和b能够互换吗?

它们都具有指针值,它们都可以进行间接访问和下标操作。

但是,它们还是有很大的区别的:

声明一个数组时,编译器将根据声明所指定的元素数量为数组保留内存空间,然后再创建数组名,它的值是一个常量,指向这段空间的起始位置。

声明一个指针变量时,编译器只为指针本身保留内存空间,它并不为任何整型值分配内存空间。

而且,指针变量并未被初始化为指向任何现有的内存空间,如果它是一个自动变量,它甚至根本不会被初始化。

把这两个声明用图的方法表示,可以发现它们之间存在显著的不同:

a

b

因此,上述声明后,表达式*a是完全合法的,但表达式*b却是非法的。

*b将访问内存中某个不确定的位置,或者导致程序终止。

另一方面,表达式b++可以通过编译,但是a++却不能,因为a的值是一个常量。

#include

intmain()

{

//注意sizeof(num)的长度应该为10*4=40

intnum[]={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,而不是10

char(*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+5)

//'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语言标准要求编译器必须具备这个概念性的行为。

编译器自动把下标值的步长调整到数组元素的大小。

如果整型数的长度是4个字节,那么a[i+1]和a[i]在内存中的距离就是4。

对起始地址执行加法操作之前,编译器会负责计算每次增加的步长。

这就是为什么指针总是有类型限制,每个指针只能指向一种类型的原因所在,因为编译器需要知道对指针进行解除引用操作时应该取几个字节,以及每个下标的步长应取几个字节。

·规则2、下标总是和指针的偏移量相同。

把数组下标作为指针加偏移量是c语言从BCPL(C语言的祖先)继承过来的技巧。

在人们的常规思维中,在运行时增加对c语言下标的范围检查是不切实际的。

因为取下标操作只是表示将要访问该数组,但并不保证一定要访问。

而且程序员完全可以使用指针来访问数组,从而绕过下标操作符。

在这种情况下,数组下标范围检测并不能检测所有对数组的访问的情况。

事实上,下标范围检测被认为不值得加入到c语言当中。

还有一个说法是,在编写数组算法时,使用指针比使用数组更有效率。

这个颇为人们所接收的说法在通常情况下是错误的。

使用现代的产品质量优化的编译器,一维数组和指针引用所产生的代码并不具有显著的差别。

不管怎样,数组下标是定义在指针的基础上,所以优化器常常可以把它转化为更有效率的指针表达式,并生成相同的机器指令。

 

·规则3、在函数参数的声明中,数组名被编译器当作指向该数组第一个元素的指针。

在函数形参定义这个特殊情况下,编译器必须把数组形式改写成指向数组第一个元素的指针形式。

编译器只向函数传递数组的地址,而不是整个数组的拷贝。

这种转换意味着在声明函数的时候,以下三种形式都是合法的(同时无论实参是数组还是真的指针也都是合法的):

my_function(int*turnip){···}

my_function(intturnip[]){···}

my_function(intturnip[200]){···}

 

用malloc为字符串分配存储空间时的注意事项(c缺陷与陷阱3.2节)

 

作为常数的数组声明(c缺陷与陷阱3.3节.在其它部分有包含该节的知识点,了解or略过)

字符串常量(c和指针.P269.)

当一个字符串常量出现在表达式中时,它的值是指针常量。

编译器把该字符串的一份拷贝存储在内存的某个位置,并存储一个指向第一个字符的指针。

我们可以对字符串常量进行下标引用、间接访问以及指针运算。

“xyz”+1

字符串常量实际上是个指针,这个表达式计算“指针值加上1”的值。

它的结果也是个指针,指向字符串中的第二个字符y

*“xyz”

对一个指针执行间接访问操作时,其结果就是指针所指向的内容。

字符串常量的类型是“指向字符的指针”,所以这个间接访问的结果就是它所指向的字符:

x。

注意表达式的结果并不是整个字符串,而只是它的第一个字符。

“xyz”[2]

同样可以推断出上面这个表达式的值就是字符z。

#include

//接受一个无符号整型值,把它转换成字符,并打印出来

//如果是打印16进值的数,可以用这种方法:

putchar("0123456789ABCDEF"[value%16])

voidbinary_to_ascii(unsignedlongvalue)

{

unsignedlongquotient;

quotient=value/10;

if(quotient!

=0)

binary_to_ascii(quotient);

putchar("0123456789"[value%10]);

}

intmain()

{

//字符串常量实际上是个指针,这个表达式计算"指针值加上1"的值。

它的结果也是个指针,

//指向字符串中的第二个字符:

y

printf("%s\n","xyz"+1);

//对一个指针执行间接访问操作时,其结果就是指针所指向的内容。

//字符串常量的类型是"指向字符的指针",所以这个间接访问的结果就是它所指向的字符:

x

printf("%c\n",*"abcdefg");

//同样可以推断出上面这个表达式的值就是字符z

printf("%c\n","abcdefg"[3]);

binary_to_ascii(1234567);

getchar();

return0;

}

 

用字符串常量初始化指针和数组(c专家编程.P87.)

定义指针时,编译器并不为指针所指的对象分配空间,它只是分配指针本身的空间,除非在定义时同时赋给指针一个字符串常量进行初始化。

例如,下面的定义创建一个字符串常量(为其分配内存):

char*p=“breadfruit”;

注意只有对字符串常量才是如此。

不能指望为浮点数之类的变量分配空间,如:

float*pip=3.14;/*错误,无法通过编译*/

在ANSIC中,初始化指针时所创建的字符串常量被定义为只读。

如果试图通过指针修改这个字符串值,程序会出现未定义的行为。

在有些编译器中,字符串常量被存放在只允许读取的文本段中,以防止它被修改。

数组也可以用字符串常量进行初始化:

chara[]=“gooseberry”;

与指针相反,由字符串常量初始化的数组是可以修改的。

比如下面的语句:

strncpy(a,“black”,5);

将数组的值修改为“blackberry”。

#include

#include

intmain(void)

{

char*p="thisisaexample";

//char*pi=3.14;//这样定义是错误的,无法通过编译

//p[0]='T';//修改该字符串常量时,编译是没问题,但是运行时会出现异常

chara[]="gooseberry";

strncpy(a,"black",5);

printf("%s\n",p);

printf("%s\n",a);

return0;

}

 

二维数组下标操作的相关概念(c和指针.P156.)

指向一维、二维数组的指针(c和指针.P158.)

array_name和&array_name的异同

前者是指向数组中第一个元素的指针,后者是指向整个数组的指针。

chara[MAX];/*arrayofMAXcharacters*/

char*p=a;/*p为指向数组的指针*/

char*pa=&a;/*该语句是不正确的,pa的类型为'char*',而&a的类型为'char(*)[MAX]’*/

char(*pb)[MAX]=&a;/*该语句是正确的,pb的类型为'char(*)[MAX]'*/

#include

voidmain()

{

chara[5]={'a','b','c','d','\0'};

char*p=a;

//运行下面这句后,vc6.0提示的错误为:

cannotconvertfrom'char(*)[5]'to'char*',&a的类型应该是指向一个数组的指针

//char*pa=&a;

//所以,应该定义一个指向相同类型和大小的数组的指针来获得“&a”的值

char(*point_to_str)[5];

point_to_str=&a;

printf("%d\n%d\n",&p,&point_to_str);

printf("%s\n%s\n",p,point_to_str);

}

运行结果为:

1245044

1245040

abcd

abcd

 

数组作为函数的参数时,不能通过sizeof运算符得到该数组的大小

不可以。

当把数组作为函数的参数时,你无法在程序运行时通过数组参数本身告诉函数该数组的大小,因为函数的数组参数相当于指向该数组第一个元素的指针。

这意味着把数组传递给函数的效率非常高,也意味着程序员必须通过某种机制告诉函数数组参数的大小。

为了告诉函数数组参数的大小,人们通常采用以下两种方法:

第一种方法是将数组和表示数组大小的值一起传递给函数,例如memcpy()函数就是这样做的:

   memcpy(dest,source,length);

第二种方法是引入某种规则来结束一个数组,例如在C语言中字符串总是以ASCII字符NUL('\0')结束,而一个指针数组总是以空指针结束。

请看下述函数,它的参数是一个以空指针结束的字符指针数组,这个空指针告诉该函数什么时候停止工作:

   voidprintMany(char*strings[])   

   {

       int i=0;  

       while(strings[i]!

=NULL)

       {

           puts(strings[i++]);

       }

   }

C程序员经常用指针来代替数组下标,因此大多数C程序员通常会将上述函数编写得更隐蔽一些:

   voidprintMany(char*strings[])

   {

       while(*strings)

       {

           puts(*strings++);

       }

   }

尽管你不能改变一个数组名的值,但是strings是一个数组参数,相当于一个指针,因此可以对它进行自增运算,并且可以在调用puts()函数时对strings进行自增运算。

用strlen()求字符串的长度(c和指针.P159.)

库函数strlen的原型为:

size_tstrlen(charconst*string);

strlen返回一个类型为size_t的值。

这个类型是在头文件stddef.h中定义的,它是一个无符号整型类型。

在表达式中使用无符号数可能导致不可预期的结果。

例如,下面两个表达式看起来是相等的:

if(strlen(str1)>=strlen(str2))…

if

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 求职职场 > 简历

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2