高质量C语言编程
1.%d 十进制整数;%o 八进制数;%x 十六进制数;%u 无符号整型;%s字符串
%l(d…) long数值;%h(d…) short数值;%c 一个字符;%f 浮点数 %p 指针地址。
2.包含头文件用<>和“”的区别:搜索路径不同;“”从当前目录开始搜索,<>从存放标准库的目录开始。
3.return;?
4.sizeof和strlen区别?
sizeof()是关键字,strlen()是库函数。
sizeof()以字节为单位给出数据的大小,strlen()函数以字符为单位给出字符串的长度。
strlen()不计算字符串末尾的’\0’;sizeof()得出的结果中包含结尾的’\0’.
sizeof()参数可以是变量或者数据类型,strlen()参数只能是字符串。
5.main不是关键字,在底层代码中定义,可以被修改。
6.常量一般用大写字母表示。
7. 常量的类型:1.字面常量 1,2,3…;a,b,c…
2.const常量 const int price=30;
3.宏常量 #define PRICE 30
4.枚举
不建议在程序中直接使用字面常量,使用宏定义常量程序易维护。
指向const对象的指针不能赋给指向非const对象的指针,但反过来可以。
const int *p;int const *p;int * const p;区别???
8. typedef 和define区别: eg: typedef float FP32和 #define FF FP32?
(类型重定义)define在预编译时处理,简单字符置换;
typedef编译时处理,为已有类型命名。
9.字符串最后以’\0’结尾。
10.#include包含的头文件全部复制到程序还是只复制程序中用到的函数原型?
11.scanf()读取字符串遇到空格、制表符、换行符停止;一般输入带空格的字符串用gets()。
12.全局变量和静态变量系统自动赋初值0;局部变量不被赋初值。
13.定义和声明的区别?
定义分配内存空间和地址,声明不分配。
定义只能一次,声明可多次。
带extern关键字的一定是声明。
14.%要求两侧均为整型。
15.不同编译器,表达式的运算顺序可能不同,或从左向右,或从右向左。
16.C语句以分号作为分隔符,编译后产生机器指令,完成操作任务。
17.结构体类型和结构体变量的区别:类型不分配内存,不可参与运算;结构体变量分配内存,可以参与运算。
18.结构体变量可以在定义时整体初始化,但定义后不能用’=’整体赋值(不可以再用{}依次为每个成员赋值;可以b=a赋值,但结构体类型中含有指针时很危险)。要用
结构体变量.成员名
指向结构体变量的指针->成员名
(*指向结构体变量的指针).成员名
为成员赋值。
不能用==判断两个结构体变量是否相等。
19. C语言不能在for循环中定义循环变量i.(变量的定义要在所有的语句之前)。
20.结构体数据对齐?可伸缩型数组成员?
21.结构体中的字符串指针成员怎么赋值?不可以用字符串常量赋值》
22.volatile关键字:不被编译器优化。(应用程序开发很少用到,驱动程序开发常用)
23. static修饰全局变量不能被其他文件使用。static修饰局部变量 ? static变量分配在静态充储区,在程序运行期间不释放。但static修饰的局部变量作用域仅仅在函数或块的内部才可见。static修饰的局部变量在编译时进行初始化,且只有一次,如果不赋初值,编译器会自动赋初值0。
24.内存划分:数据区(栈区stack;静态区 static;堆区heap);代码区code。
栈区:auto存放局部数据。由系统按栈原则管理?,编译时已经规划好了,运行时才使用。函数执行完即释放,用户无法干预变量的诞生和消亡。
静态区:存放全局数据和静态数据。编译时已确定并初始化了,受作用域限制。
堆区:存放动态申请的数据,无名,用户可干预,程序运行时才动态分配,申请可能失败。
代码区:存放程序执行代码,每段代码都有名称—函数名。用户无法干预,只能通过函数名调用。
25.char *pa; scanf(“%s”pa);错误, 原因?
26.ctype.h 字符判断函数(例如是不是小写字母)
27. void* malloc(size_t size); 从堆中分配连续的大小为size字节的块。(PS:void* 任意类型的指针)
void* calloc(size_t num,size_t size); 分配内存并全部清零。分配的大小为num*size.
void *realloc(void *mem_address, unsigned int newsize); 扩大已分配的内存块大小
(先判断当前的指针是否有足够的连续空间,若有,扩大mem_address指向的地址,并且将mem_address返回,如果空间不够,先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域,返回新分配的内存区域的首地址。即重新分配存储器块的地址。(这种情况需特别注意,如果还有别的指针也指向原先mem_address指向的地址,新分配后,那些指针变为野指针。使用该函数可能需要malloc.h头文件)很危险,使用需谨慎
void free(void* memblock); 释放动态分配的空间。释放的是整个块,而不是指向该块的指针类型所指的大小。(PS:动态非配内存,一般编译器会在分配的内存块的首地址-1中存储分配的内存的大小,以便free完全释放。)
在stdlib.h中定义。
28.动态分配后要用if(NULL==p) 判断是否分配成功。
内存使用的过程中要注意“越界”问题。
用free释放空间后,要将指向那块空间的指针指向NULL,防止“野指针”的产生。
29.char *p=”neusoft” p指向的字符串是常量,在静态区,只能读取,不能通过指针修改。
30.二维数组做函数参数?(二维数组不能做函数的返回值,只能返回二维数组的首地址。)
(1)可以用二维数组名作为实参或者形参,在被调用函数中对形参数组定义时可以指定所有维数的大小,也可以省略第一维的大小说明,如:
void Func(int array[3][10]);
void Func(int array[][10]);
二者都是合法而且等价,但是不能把第二维或者更高维的大小省略,如下面的定义是不合法的:
void Func(int array[][]);
(2)如果找一个通用方程只能用: void f(int *p, int row, int col ) //给出数组的行和列,对堆上的数组不合适 { for (int i = 0; i < row; i++ ) { for ( int j = 0; j < col; j++ ) { cout < < p[i * row + j] < < " "; } cout < < endl; } }
31.switch-case语句 标签必须都是整型值(包括char),而且case标签必须是常量或完全由常量组成的表达式。
32.goto语句可以在出现故障时从一组嵌套的循环中跳出。(单挑break语句只能跳出最内层的循环)
33.内存相关库函数:(1)void *memcpy(void *destin,void *source,unsigned int n);
从源source中拷贝N个字节到目标destin中。
(2)void *memmove(void *destin,void *source,unsigned int n);
移动一块字节。(返回指向destin)
memcpy()和memmove()区别:当源内存和目标内存存在重叠时,memcpy会出现错误,而memmove能正确地实施拷贝,但也增加了一点开销。(当源内存的首地址大于目标内存的首地址时,实行正向拷贝;当源内存的首地址小于目标内存的首地址时,实行反向拷贝)
(3)void *memchr(void *s,char ch,unsigned int n);
在数组的前N个字节中搜索字符。(返回指向第一个出现的位置)
定义在<string.h>头文件。
34.文件的输入输出:(1) FILE *fopen(const char* filename, const char *mode);
打开用filename指定的文件,并使其与一个流相关联(r,w,w+,t,b)。
fopen创建结构体变量,并指向该变量,结构体成员char *_base指向缓冲区,使缓冲区内存与磁盘相关联,文件读写位置指示器指向磁盘中打开的文件。
(2)int fclose(FILE *stream); 关闭一个流。
(3)void rewind(FILE *stream); 指向文件开头
(4)long ftell(FILE *stream); 返回文件位置指示器的当前值。
(5)int fseek(FILE *stream,long offset,int origin); 把读写的位置指示器移到指定的位置(SEEK_SET 文件开始,SEEK_CUR 文件当前位置,SEEK_END 文件末尾)
(6)int fprintf(FILE *stream,const char *format |,argument |…,地址); 格式化输出到一个流中。(用法类似于printf())。
(7)int fscanf(FILE *stream,const char *format |,argument |…,地址); 从一个流中执行格式化输入。
(8)size_t fread(void *buffer,size_t size,size_t count,FILE *stream); 从一个流中读取数据块。(数据块大小*数据块数)
(9) size_t fwrite (const void *buffer,size_t size,size_t count,FILE *stream); 写数据块内容到流中。(写入结构体类型的数据十分方便。可以把一个结构体数据一次写入)
(10)char ch=fgetc(fp); 读取一个字节。
(11)getc(fp);putc(fp);fgets(buf,max,fp);fputs(buf,fp);
35.存放文件信息的结构体:
struct _iobuf
{
char *_ptr;
int _cnt;
char *_base;
int _flag;
int _file;
int _charbuf;
int _bufsiz;
char *_tmpfname;
};
typedef struct _iobuf FILE;
36.char c[10]=”aaaaa”;
char *p=c;
printf(“%c”,*c++); //错误 数组名不能做自增运算,但是printf(“%c”,*(c+1));
printf(“%c”,*p++);//正确
数组名代表数组内存的首地址,是可修改的常地址。(数组名:地址常量)。
37.char c[3]=”aaaaa”; 如果赋值的字符串超过数组长度,会将此字符串截断,只将数组长度的字节赋给数组。
38.当数组作为函数的参数进行传递时,该数组自动蜕化为指针。改变形参数组中的值,实参数组也同时改变。
39.C语言编译器不检查数组是否越界。(靠程序员自己控制)
int a[3]={1,2,3,4};
printf(“%d”,a[3]); //输出的并不是4,而是原来那块内存中存放的数据。赋值时只将前3个赋给数组,第四个被抛弃。
a[3]=4;
printf(“%d”,a[3]); //输出的是4,数组越界访问,修改了那块内存。
40.二维数组int a[5][7],a和a[0](即*a),指向的地址相同,但指向的数据类型不同,a指向的数据类型为int[7],a[0]指向的是int.所以a+1和a[0]+1(即*a+1)移动的位置不同。**a表示首地址中数据的值。
41.效率问题:1.减少内存分配次数,一次能够申请的内存,不要多次申请。
2.嵌套循环中,循环次数多的放内层可提高运行速度。(但可能浪费一点空间。)
3.将二维数组看成一维数组计算,不用嵌套循环,提高效率。
4.被多次访问的函数中存储“不变量”的变量应该定义为static.
eg: static char a[]=”abc”;
42.int a[]={1,2,3,4,5,6 } sizeof(a)/sizeof(a[0]) 算出数组长度,在for循环中常用。
43.使用i++和++i要注意系统对表达式或参数的处理顺序是从右到左还是从左到右eg:fun(i,i++); 若从左到右 fun(0,0); 若从右到左 fun(1,0);
44.*(int *)(内存地址)=9;//指针直接操作内存。
45.使用指针变量之前一定要确保该指针变量指向一个合法可用的内存单元。定义指针时先将其指向NULL是很好的习惯。
46.#define NULL (void *)0
47.函数无法返回整个数组,只能返回数组的地址。
48.指针相加减????
49.字符串函数:size_t strlen(const char *s);//字符串长度。
char* strcat(char *s1,const char *s2);//若string1不是足够大,会溢出
char* strncat(char *s1,const char *s2,size_t n);//最多允许添加的字符数目。
int strcmp(constr char *s1,const char *s2) ;//若第一个字符串小于第二个,返
回负值;等于返回0;大于返回正值。
int strncmp(constr char *s1,const char *s2,size_t n) ;//比较到不同处或比较完指定的字符数
char* strcpy(char *s1,const char *s2);//将第二个字符串中的内容拷贝到第
一个字符串中。不检查目标字符串是否能容纳源字符串。可能溢出,不安全。
char* strncpy(char *s1,const char *s2,size_t n);//最多拷贝n个。
sprint(target,”%s,%d?%s”,string1,int2,string3);//把几个元素合成一个字符串放在target里.(元素可以是字符串,也可以是其他的如浮点型,函数做格式转换)不输出。
char *strchr(const char *s,char c);//返回s中存放字符c的第一个位置的指针。
char *strrchr(const char *s,char c);//返回s中字符c的最后出现位置的指针。
char *strpbrk(constr char *s1,const char *s2);返回s1中存放s2中任何字符的第一个位置。
char *strstr(constr char *s1,const char *s2);//返回s1中第一次出现s2字符串的地方。
50.字符串转化为数字的函数:atoi();atol();atof();strtol();strtod(类型int,long,double;long;double)
51.在一个函数中为另一个函数中的指针分配地址,要用二级指针。
void fun(char **p)
{
*p=(char *)malloc(sizeof(int)*10);
}
int main()
{
int i;
char *q;
fun(&q);
for(i=0;i<10;i++)
{
q[i]=i;
printf("%d",q[i]);
}
return 0;
}
52.用一级指针做参数,在另一函数中修改形参指针指向的空间内的值,实参指针指向的空间内的值改变,但是如果是修改形参指针指向的空间,实参指针指向的空间不变。
53.函数指针最常见的应用:(1)回调函数(2)结构体中封装函数实现面向对象效果。
54.野指针(未指向合法内存的指针)产生的原因
指针变量没有初始化就赋值;
没有检测malloc是否成功就使用指针。不成功指向NULL;
free之后没有置空,让人误以为它仍是合法指针;
指针操作超过变量的作用范围如数组下标越界;
55.静态代码分析工具:检查潜在错误!
56.不要返回局部变量的地址(指向堆空间的指针或存放在静态空间的变量可以),栈空间分配的地址,在函数结束之后就被系统收回,指向该地址的指针变为野指针。
57.基本位操作符
& 位与;| 位或;~ 位反;^ 位异或;<< 左移;>> 右移
&作用:1.清零 2.取一个数中某些指定位(位掩码:与所操作的值相同长度)
|作用:设置变量的某些位为1;
^位异或:若两个相应的位相同则为0;否则为1.可以使变量某些特定的位反转。(与0相^保留原值,与1相^变反,与自身^结果为0)
eg:交换a,b的值:a=a^b;b=a^b;a=a^b;
<< 左移:左移一位相当于乘以2,低位补0,高位移出。
>> 右移:移出的位数全部丢失,对无符号数,高位补0,右移一位相当于除以2;对有符号数,符号位补0还是补1取决于计算机系统。
58位字段
struct bitexample{
unsigned int a:1;
unsigned int b:3
unsigned int c:2;
}; a占1位,只能赋值0,1;b占3位;c占2位
59.带参数的宏,参数也最好用()括起来 eg: #define AVG(x,y) (((x)+(y))/2)
避免在宏的参数中使用++ 和——运算符
#运算符(把语言符号(宏参数)转化为字符串);##运算符(进行语言符号的粘合操作)
宏(空间换时间,同一段代码出现多次)使用带参数的宏函数时经常产生奇怪的现象
在使用宏时需要注意的几点:
(1)宏的名字中不能有空格(在替代字符串中可以使用空格)
(2)用()括住每个参数,并括住宏的整体定义
(3)用大写字母表示宏常量和宏函数名
(4)宏替代函数可以加快程序的运行速度,尤其在嵌套循环中更有帮助(但只运行一次或极少的宏对程序运行速度不会有明显改善)。
60.头文件中最常见的内容:明显常量,宏函数,函数声明,结构模板定义,类型定义等
61.预处理指令:#include;#define;#undef(取消前面#define定义); #if; #ifdef; #ifndef; #else; #elif; #endif;(用于选择什么情况下编译哪些代码);#line(重置行和文件信息);#error(给出错误消息)
#pragma(用于向编译器发出指示)
62.宏定义的作用:可读性,减少了书写错误,可维护性,提高运行速度(时间花在预编译)
63.条件编译是重点!作用:调试代码;只使有效代码参加编译;防止头文件被重复编译;
eg: #ifndef 01_H #define 01_H …… #endif
64.库函数:qsort()快排 atoi()等 ateit() ;exit() srand();rand() ctype.h判断输入字符函数 fabs()求浮点数的绝对值 abs()求整数的绝对值 sqrt() ceil()求大于此浮点数的最小整数
floor() 求小于此浮点数的最大整数exp()以自然对数e为底指数函数 pow()计算x的y次幂
time.h:C/C++中的日期和时间头文件;可以获取当前时间等有关时间的信息。
|