以下程序段在VC6.0环境中运行的输出结果是 struct studentinfo{ char name[10]; int SNO; char sex;求求

★在1973年,美国贝尔实验室的D.M.Ritchie(丹尼斯·里奇)在B语言的基础上最终设计出了一种新的语言,他取了BCPL的第二个字母作为这种语言的名字,这就是C语言,并首次用C重新实现了UNIX操作系统。
★1978年Brian W. Kernighan和Dennis M. Ritchie出版的名著《C程序设计语言(The C ProgrammingLanguage)》,常被人们称为K&R版C教材或“白皮书”,为C语言的推广普及立下汗马功劳。在书籍方面我还要极力推荐的一本精典著作就是《C和指针》,写的特别深入、详细。
1、C语言是一门兼具高级语言功能和低级语言大部分功能的程序开发语言,所以既能开发应用软件也能开发系统软件。
2、C程序是由函数构成的,一个C源程序至少且仅包含一个main函数。一个C程序总是从main函数开始执行的,而不论main函数在整个程序的位置如何。main(int argc,char *c[])主函数也可以带参数,此时在命令行执行test.exe ma wen 3、C语言是严格区分大、小写的,比如定义变量时:int a和int A是两个完全不同的变量。
4、C语言中整常数可以有3种表示形式:
(2)以0开头为八进制,如:0123表示八进制123,十进制为83。
(3)以0x开头为十六进制(没有直接表示二进制格式,所以可以用十六进制表示),如:0x123。
5、数据类型的长度由编译器决定,比如Turbo C给int型变量分配2个字节的存储单元,而Visual C++6.0则分配4字节。
★这里简单说下C语言变量在内存中的存储位置:
(1)全局变量和静态变量都在静态内存区,说白了也就是普通内存区即进程所分配的地址空间,当程序退出自动释放。
(2)函数的参数和局部变量都在堆栈中,函数退出后这些变量都会自动出栈,即释放内存。
(3)用malloc、calloc、realloc分配的内存属于堆区,必须手动调用free函数释放,否则会造成内存泄露。
(4)寄存器变量,直接存储在寄存器中,存取值速度快,只有局部自动(非静态)变量和形式参数可以这样定义。如:register ...
6、整数默认就是有符号数,浮点型常量默认为双精度,有些编译器比如Turbo C把字符型变量默认定义为signed char型,即有符号字符,这样它的范围就是-128到127之间了,但有些编译器默认字符是无符号(unsigned char)的,范围在0到255之间。
7、%模运算符(求余数)两侧均应为整型数据。
8、变量使用前必须先声明,而且声明要在代码之前(即变量声明紧随代码块左大括号)。
C语言的逻辑运算符(&&,||,!)与、或、非的判断结果是假则值为0,结果为真则值为非0(包括任何非0数)。
10、逻辑表达式在求解中,并不是所有的逻辑运算符都被执行,只是在必须执行下一个逻辑运算符后才能确定表达式结果时才执行后面运算,比如:a&&b&&c如果a为真(非0)才去计算b,如果a为假(0)则b和c都不会去执行,最终结果就是假。
11、if语言的判断条件是一个关系表达式或逻辑表达式,由于逻辑表达式的真用非0代表,所以if语句的判断条件可以是0和任意非0数值,包括(整数、实型、字符型、指针型),如:if(3) printf("0k");或者if('a')printf("no");都是合法的。切记即使在条件判断和循环判断中传的是负数也会为真,即 if(-2)...while(-1)等等这样的表达式都会为真,只要不是0就为真。
12、条件表达式即三目运算符x?a:b中a和b的结果类型不同,则三目运算符的最终结果会取类型较高的,比如x>y?1:1.5,如果x>y为真则最终结果为1.0。
13、for循环中for(表达式1;表达式2;表达式3),都可以省略这样就成了死循环,但分号不能去掉。而且表达式1和表达式3中都可以包括多个表达式用逗号分隔如,比如:
14、while循环、do...while循环和for循环可以用break语句跳出循环,用continue语句结束本次循环,但对于goto语句和if语句构成的循环不能用break和continue进行控制。切记:
break的作用只有两个:
(1)跳出最近的那层循环。
(2)跳出最近的整个switch语句。
15、提到字符串就必须说说字符串常量,比如:
char c = "asdf";这样用指针定义的字符串常量不能改变字符串的任何字符,因为字符串常量存储在静态内存区的只读区域,所以如果你试图用c++ = 'm'等语句想改变字符串的内容,系统编译不会发现问题,但运行时则会报错,这个问题前期一般很难发现,所以必须谨慎,字符数组可以修改如:char a[]="sdf",(奇怪的是好多C程序书籍居然没有提到这一点,真不明白现在的作者都是以何目的而写作的)。还有一点就是字符串连接:"ma" "wentao"就等价于"mawentao"
16、关于数组要说的首先是数组名,数组名在作为&和sizeof的操作数时,不会当做指针用,此时就代表整个数组,即char a[] = "bao"; &a就是数组的起始地址,即&a[0].
其次,函数不能返回数组,只能返回数组的首地址(指针),关于数组的初始化必须按顺序来。
最后,数组中的元素长度都是相同的,即都是相同的类型,所以数组元素不可能是像函数等等这些没固定长度的元素,而且定义数组时下标必须是个常量即数组大小在编译时就要确定。
(ANSI标准规定数组下标访问不能超过数组左边界,但VC++6.0没这限制,不论数组左边界还是右边界都可以随便超出访问,不过没实际意义,所以程序员自己要注意)
17、关于指针,好多作者都会给读者一个下马威,说是指针特别复杂,其实真没有什么,指针就是地址。一般指针变量自身都占4个字节,它所指向的类型除了基本类型外比较复杂的有结构体 、数组、函数。数组名作为参数传递时就是首地址的拷贝,而结构仍是结构本身的拷贝。
array[5]等价于5[array],因为内部都是指针加下标偏移量实现的。
(复杂声明的理解技巧其实很简单,就是每次都从变量名开始根据它前后的符号确定其根本)
18、extern声明外部变量,可以扩展外部变量的作用域。如:extern a,b;...int a=10,b=11;虽然a和b在extern语句之后定义但在extern后面且在a和b定义之前的语句都可以用a和b。 extern用于函数定义表示全局可见(不加也可以),用于变量说明它在其他地方定义。
19、在编译时,对类型是不分配空间的,只对变量分配空间。比如:建立结构体类型struct student{char name[],int age};此时不会分配内存,只有定义了变量在系统编译时才分配空间。比如:struct student student1;
20、sizeof操作符可以获得一个类型所占的字节数,sizeof不是个函数,如果后面的操作数不是个类型名就不用加括号。如:int i; sizeof i;当sizeof操作结构时,好多时候都大于结构中所有成员的长度和,这是由于字节对齐所造成的。操作数组时会返回整个数组的长度。
(1)可以用其他类型说明符对宏类型名进行扩展,但对typedef所定义的类型名却不能这么做。
(2)在连续的几个变量声明中,用typedef定义的类型能够保证声明中的所有变量均为同一种类型,而用define定义的类型则无法保证。比如连续定义指针类型时,define就会出问题。
22、>>右移操作符:Turbo C和其他一些编译器采用的是算数右移,即对有符号数右移时,如果原来符号位原来为1,左面移入高位的是1。如果某个编译器对于右移采用逻辑右移则永远在高位补0,所以右移负数时存在不可移植性问题。
23、位段:C语言中允许在一个结构体中以位为单位制定其成员所占内存长度,这种以位为单位的成员称为“位段”或称“位域”
利用位段能够用较少的位数存储数据。(前提也是在结构体内,并且类型只能是int、unsigned int、singned int)
}da;这样定义的结构体a、b、c分别占8位、2位、6位。
24、所有非整形函数一定要在调用之前声明。(没有被声明就调用的函数被默认为返回整型)
(1)函数前面不写返回值默认就返回整型,但函数体此时可以不返回任何东西,也可以返回整数。
(2)函数不返回任何东西,可以显示的用void声明,比如这样定义主函数void main(){}。
26、关键字const并不能把变量变成常量,在一个符号前加上const限定符只是表示这个符号不能被赋值,可以初始化一次。
注意:const int * p;和int const * p;都叫常量指针(这种读法很简单,因为先是const再是*),都是指针所指的对象不能修改。
int * const p;这和字符串常量、数组名(当做指针时)一样都叫指针常量,表示指针本身是不可变的。
27、void *v类型参数是一个通用的指针类型,即这种类型的指针可以转换成任意具体类型的指针,但它不可以移动,比如:v++,v-1等等,这是因为此指针所指向的对象大小无法确定,那么它自然就不知道+1是移动多少了。(这个解释实在太经典了,嘿嘿)
28、C语言在定义数组时,需要指定数组中元素的个数,方括号中的表达式用来表示元素的个数,即数组长度。下标只能是表达式常量和符号常量,不能包含变量,也就是说C语言不允许对数组大小作动态定义,即数组的大小不依赖于程序运行过程中的变量值。
●C语言在编译时就要确定任何变量的存储空间大小,包括数组。(可以想想汇编数据段中的内存分配)

29、""[n%10]这个表达式将返回n(整数)的最后一位数值,别忘了字符串常量就是一个指针常量。(为了防止字符表中的数字没有顺序排列)
30、对于参数的计算顺序和整形数右移是否扩展符号位,标准都没有规定,只能依赖编译器了。
32、有好多人经常问标准库函数到底是在哪里实现的?为什么我们很简单的用include包含后,可就是找不到math的实现,感觉很奇怪,之前我也觉得很好奇,其实等你了解了动态链接(.dll)、引用链接库(.lib)一切都会明白了,在编译器的某个文件夹下存在好多.dll和.lib文件,其中dll文件就是动态链接库,内部就是函数的实现,lib库中有那些函数名的声明引用,当系统发现你调用pow()函数时,就会去相应文件夹下lib文件中找到pow在哪个动态库中调用了,然后把动态库中的此函数体自动加载到内存执行,大概过程就是这样。
33、C语言把文件看做一个字符(字节)序列,即C文件是一个字节流或二进制流,根据数据的组织形式不同,可以分为ascii文件和二进制文件ascii 文件也叫文本文件,它的每一个字节存放一个 ascii代码,代表一个字符。二进制文件是把内存中的数据按其在内存中的存储形式原样输出到文件磁盘上存放。 注意文本/二进制文件区别只是发生在文件打开时,一旦文件打开之后,在其上调用什么I/O函数无关紧要。
用fopen打开文件时,路径有两种写法:双反斜杠//、单正斜杠/。
34、有些专家建议在C语言的操作符中牢记两个优先级就够了:乘法和除法优先于加法和减法,在涉及到其他操作符时,一律加上括号。虽然有点夸张了,但我觉得我们真不应该把时间浪费在死记操作符的优先级上,而且为了代码的可读性和维护性就应该多加括号。
35、i=x/y;这里本意是指针x所指向的内容除以指针y所指向的内容然后赋给i,但/*被理解为注释开始了,编译器会报错,所以/和*之间必须加上空格。这也是空格作用体现的一个很好例子,因此我们在写代码时一定要养成一个好的习惯,操作符两边都以空格分隔。
37、函数内部不能返回一个局部数组指针,如int* f(){int a[3]={1,2,4}; return a;},这样返回了一个指针a,但函数结束后整个数组都被释放,a指向的内存内容是个未知的,或许已经被别的内容覆盖,这是很危险的。
38、声明只确定了变量的基本类型以及初始值(如果有的话).
39、如何解析C语言声明?
1、取最左边的标示符(变量名)
2、查看标示符右边的下一个符号,如果是方括号对于每一对方括号表示“...的数组”;如果是一个左括号,到右括号为止的内容表示“返回...的函数”;(如果变量名临近右边无任 何有价值的符号就直接走第3步,比如int (*p)[2])
3、接着查看的左边符号,如果是一个左括号,这个左括号把已经处理的部分声明组合在一起,直到遇见对应的右括号,然后从第2步从新开始;
4、上面三部分析完后,在看已经处理的部分左边的符号如果是下述之一:const、volatile、,则继续向左边读符号,直到所读符号不再是上面那三个之一。如果符号是const,表示“只读”;如果是volatile表示"volatile";如果是,表示“指向...的指针”然后重复第三步;
5、剩下的符号形成声明的基本类型,剩下的所有符号可以一并阅读,如:static unsigned int。

切记:const或volatile关键字后面如果紧跟类型说明符如(int、long),则它作用于类型说明符,其他情况下都作用于左边的临近指针星号。

举例比如:char * const (next)();我们来简单分析下这个声明,就按照上面的5个步骤进行:
1、取最左边的标示符:next,表示变量next是个...;
4、next左边是*,表示“next是指向...的指针”;再重复第三步,匹配到了一对小括号后再从第二步开始,表示“返回...的函数”;接着又到第三步:不匹配,再去第四步:左边遇到一个*即(next)外面紧跟const的那个,表示“指向...的指针”,因为右边我们已经分析完了就不再看前三步了,继续第四步遇到了const,“表示只读”;再又是一个“指向...的指针”,最后就是char类型定义了。
●上面所有合起来就是next是一个指向函数的指针,该函数返回另一个指针,该指针指向一个只读的指向char的指针。
char ( c[10])(int **p)就表示:c是一个指针数组,即里面的元素全是指针,每个指针都指向一个函数,即函数指针,该函数有个指向int型指针的指针的参数,返回值是个char型指针。
●注意:在数组中被函数指针指向的所有函数都把一个指向指针的指针作为它们的唯一参数。(上面就是一个例子)
40、关于补码请记住:对于有符号数,正数和负数都是由补码构成的。(汇编语言)
一个正数的补码取反加1后,为其负数的补码;负数的补码取反加1后,为其绝对值。
用<>括起来的头文件一般先会在标准位置搜索即上面的目录,用“”括起来的头文件首先会在当前目录搜索,然后去标准位置搜索。
43、判断偶数的小技巧就是看二进制最后一位是否为0,如果是0则为偶数,为1就是奇数。
44、可变参数列表的应用主靠头文件,里面有个va_list结构体和三个宏va_start、va_arg、va_end。(具体应用任何C书籍都有提到)
由于字节对齐要求声明上面结构体比较浪费内存(比如在32位机器上,s1会浪费6个字节,因为CPU从内存获取数据时是从地址空间为4的整数倍开始取的,则CPU下个周期会移动4byte取数据)
而且这里结构体变量名s2和数组名不同,s2不会当做一个指向结构体第一个成员的指针,s2就代表整个结构。
47、#define 宏定义中:#argument这种结构,可以把宏参数argument转换成字符串。如果宏内容中出现两个#号,比如:sum ## i; ##会把位于它两本的符号组合成一个标识符。切记:宏语句最好不要以分号结束。(当宏内容太长了,可以换行并在除过最后一行的所有行末尾加上反斜杠)。

49、为了防止#include指令包含多个相同的头文件,可以使用:
50、流:就C而言,所有的I/O操作只是简单的从程序移进或移出字节序列,这些字节序列就是字节流简称为流。我们的程序只关心创建正确的输出字节数据,以及正确的解释从输入读取的字节数据。特定I/O设备的I/O操作细节对程序员来说是隐藏的。
绝大多数流是完全缓冲的,这意味着“读取”和“写入”实际上是从一块被称为缓冲区的内存区域来回复制数据。用于输出流的缓冲只有当它写满时才会被刷新 (flush)到设备或文件中。同样,输入缓冲区当它为空时通过从设备或文件读取下一块较大的输入,重新填充缓冲区。
●流分为:文本流(文本文件、字符流)和二进制流(二进制文件、字节流),这两种流的划分都是由写入和读取时的打开方式决定的。
51、文本文件(文本流):是以特定的编码方式写入I/O设备,并且要以特定的解码方式打开,否则这些信息将会变成乱码而无法解释。
52、二进制文件(二进制流):二进制流中的字节将完全根据程序编写他们的形式写入到文件或设备中,而且完全根据他们从文件或设备读取的形式读入到程序中。
53、FILE是个结构,在中定义,用于访问一个流。如果你同时激活几个流,每个流都有一个相应的FILE与它关联。
54、对于每个ANSI C程序,运行时系统必须至少提供三个流,标准输入(stdin)、标准输出(stdout)、标准错误(stderr),他们都是一个指向FILE结构的指针。操作系统一般都允许程序员修改缺省的输入输出设备。比如修,(键盘、屏幕)。
●I/O函数以三种基本形式处理数据:单个字符(getchar和putchar家族)、文本行(gets和puts家族、scanf和printf家族)和二进制数据(fread和fwrite家族)。对于每种形式,都有一组特定的函数对他们进行处理。
55、打开流的常用模式:
读取 写入 可读可写(用于更新) 可读可写(有就删除) 添加(有不会删除) 更新(可读可写)
●一个简单的规律是文本文件后边加个b就成了二进制文件模式,比如r+到二进制模式的转换就是:rb+或r+b。
★套接字:为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了称为套接字(Socket)的接口。
区分不同应用程序进程间的网络通信和连接,主要有3个参数:通信的目的IP地址、使用的传输层协议(TCP或UDP)和使用的端口号。Socket原意是 “插座”。通过将这3个参数结合起来,
与一个“插座”Socket绑定,应用层就可以和传输层通过套接字接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。
一、套接字就是网络编程接口中基于TCP面向连接的socket编程服务器端流程如下:
(2)将套接字绑定到一个本地地址和端口上(bind)。
(3)将套接字设为监听状态,准备接收客户请求(listen)。
(4)等待客户请求到来,当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept)。
(5)用返回的套接字和客户端进行通信,获得数据用recv()方法,发送数据用send()方法。
(6)返回等待另一客户请求。

二、基于TCP面向连接的socket编程客户端流程如下:
(3)和服务器端进行通信,发送数据用send(),接收数据用recv()。

●在客户端不用调用bind绑定函数,因为服务器需要接收客户端请求,所以必须告诉本地主机它打算在哪个IP地址和哪个端口上等待客户请求。因此服务器端必须调用bind函数实现这一功能。
而对客户端来说,当它发起连接请求,服务器端接收该请求后,在服务器端就保存了客户端的IP地址和端口信息。因此服务器端就可以调用recv/send函数与客户端进行通信。

6.0占4个byte,和int一样),float是单精度型、double是双精度型、longdouble长双精度,系统默认把小数当做双精度对待,后面加个f或F就成单精度了。浮点数按照指数形式存储,大部分C编译器以24位表示小数部分(包括符号),以8位表示指数部分(包括指数符号),小数部分占的位(bit)数愈多,数的有效数字愈多,精度也就愈高。指数部分占的位数愈多,则能表示的数值范围愈大。
(4)enum枚举类型:如果一个变量只有几种可能的值,则可定义为枚举类型。枚举类型就是将变量的值一一列出来,变量的值只限于列举出来的值的范围内。 C编译对枚举元素按常量处理,故称枚举常量。但它们是有值的,即从第一个元素开始由0、1、2等等一直到最后一个元素。当枚举元素在做算术运算时就会用这些值去运算。
构造类型: (1)数组类型:必须存储相同的数据类型。字符串也可以这样定义char c[]="abc";
(2)struct结构体类型:把不同类型的数据组合成一个有机的整体,以便于引用。这时会用到的数据结构就是结构体。结构体变量所占的内存长度是各成员占的内存长度之和,每个成员分别占有其自己的内存单元。结构体的成员也可以是结构体变量,结构体指针变量(*p).age就等于p->age。
(3)union共用体类型:把不同类型的变量存放到同一段内存中,共用体变量所占的内存长度等于最长的成员长度。联合指针不能作为函数参数,函数也不能返回联合的指针。
指针类型: 指针就是内存地址,指针的指针就是指向地址的指针。 指针也可以作为函数的返回值,如:char *new()
^ 按位异或(判断两个相应的位值是否为异,即是否不同,如果不同则返回真(1),如果相同结果为0)

★键盘特殊按键的ASCII十进制数:

■C标准函数库中的常用函数:
c的库函数程序都是c的运行环境提供的,即C编译器所提供。而这些库函数我们是看不到源码,这些库函数的功能是通过编译器提供的静态链接库和动态链接库所实现的,在windows中静态链接库后缀是.lib,动态库是.dll,这些链接库在安装编译器时自动安装到了某个文件下。(动态链接库扩展性好,所占资源空间小)
2、int fclose(FILE *f),当顺利的执行了关闭返回0,否则返回EOF(-1)。关闭一个流可以防止与它相关联的文件被再次访问,并保证任何存储于缓冲区的数据被正确的写到文件中(即对于输出流,会刷新缓冲区),并且释放FILE结构所占用的内存。
●字符输入家族(此家族函数从流中读取下一个字符,并把它作为函数的返回值返回,如果流中不存在更多的字符,函数就返回EOF):
(1)int getchar(void)从标准输入读取字符,返回用户输入的第一个字符的ASCII码,用户输入的所有字符都保存在键盘缓冲区中,直到按回车后,getchar才开始从stdin流中每次读入一个字符,回车字符也会被getchar接收。
●字符输出家族(如果函数写入到一个已经关闭的流,将导致函数失败,则会返回EOF)
◆切记:上面两个家族里,只有fgetc和fputc是真正的函数,其余都是通过#define指令定义的宏,还有就是getchar和putchar只能访问标准IO设备。
字符串,函数返回第一个参数。当读取一个换行符并存储到缓冲区后就不再读取,而且这个结尾换行符会被存储到缓冲区。在任何字符读取前就到达了文件末尾,缓冲区就未进行修改,此时返回一个NULL指针。
(2)char *gets(char *buffer)功能基本同上,区别在于此函数从标准输入接收文本行而且不会在缓冲区存储结尾的换行符。gets函数的结束标记就是键入回车,此时返回 NULL。 书上说此函数为玩具函数,所以尽量少用之,因为它很容易造成溢出。
●格式化的行I/O(这里pintf和scanf可以处理多行文本)
scanf家族:每个原型的省略号表示一个可变长度的指针列表,从输入转换而来的值逐个存储到这些指针参数所指向的内存位置,返回实际转换的数目。
printf家族(返回值为实际存储的字符数):
%d(整形输出,还有%md这样会打印m个宽度,也会让输出结果右对齐。如果%md中m为负数则多余的空格输出在数字的右边,比如%-4d在右边输出4个空格)。
%f(双精度浮点输出,printf("%.2f")打印小数点后两位,.0则不打印小数)。%u(无符号十进制数输出)。
%x(十六进制输出)。
%s(输出一个字符串)。
%e(指数形式输出实数)。
%g(根据数值大小自动选f或者e格式选择宽度较小的,且不输出无意义的0)。
%%表示打印一个%号而不是用\%。
注意:除了X,E,G大小写都可以外,其他格式字符必须用小写,比如%d就不能写成%D。
printf("%f\n",5/3);结果相当另类,3/2是0,5/3也等于0。原因在于两个整型数据在做运算时,结果必然是整型,而5/3或者3 /2的整型结果都等于1,它们

★断言,就是声明某种东西应该为真。
2、NDEBUG 在头文件signal.h被包含之前,定义这个宏可以消除所有断言。

我要回帖

更多关于 vc6.0怎么调试 的文章

 

随机推荐