The C Programming Language
1. 设计一个程序,作用为无限字符输出:
int c; printf(“Please in put the char …\n”); //getchar接受字符输入并存放到缓冲区,逐个返回给c c=getchar(); while(c!=EOF) { putchar(c); c=getchar(); } |
2. 设计一个程序,统计字符数目:
printf(“Please input char …/n”); long nc=0; while(getchar()!=EOF) { ++nc; } printf(“%d\n”,nc); |
3. 设计一个程序,统计单词的个数:
#define IN 1 #define OUT 2 //c表示char,nc表示出现字符的数目,nw表示单词数目,nl表示段落数 int c, nc, nw, nl, state; state = OUT; nl = nw = nc =0;
while(c=getchar()!=EOF) { ++nc; if(c==’\n’) { ++nl; } if(c==’ ’||c==’\n’||c==’\t’) { state=OUT; } else if(state==OUT) { state = IN; ++nw; } printf(“字符数:%d,单词数:%d,段落数:%d”,nc,nw,nl); } |
4. C语言中的数据类型:
|-char(1byte)、int(4byte)、float(4byte)、double(8byte)、bool(1byte)
|-整形两种限定符:
|-short和long,用于限定整形int,int可以不写。short int(2byte)、long int(4byte)。
|-unsigned和signed用于限定是否含有符号,可用于char和int。默认为signed。
5. 判断闰年:
int year=0; scanf(“%d”,&year); if(((year%4==0)&&(year%100)!=0)||year%400==0) { printf(“This year %d is leap year”,year); } else { … } |
6. enum与#define定义常量的对比
|-enum定义枚举,#define定义宏,enum定义常量的值可以自动生成。
|-enum能够用于调试,而define定义的常量编译时不检查,只是对其进行字符的替换。
7. 常用函数自定义实现:
//strlen int strlen(char s[]) { int i=0; while(s[i]!=’\0’) { ++i; } return i; } //或者 int strlen(char *s) { int n; for(n=0;*s!=’\0’;s++) { n++; } return n; } |
//atoi int atoi(char s[]) { int n=0; for(int i=0;s[i]>=’0’&&s[i]<=’9’;++i) { n=(s[i]-‘0’)+10*n; } } |
//lower int lower(int c) { if(c>=’A’&&c<=’Z’) { return c+(‘a’-‘A’) } else return c; } |
//strcat void strcat(char s[], char t[]) { int i=0; int j=0; while(s[i]!='\0') { ++i; } while ((s[i++]=t[j++])!='\0') { } } |
//删除字符 void squeeze(char s[], int c) { int j,j; for(int i=j=0;s[i]!=’\0’;i++) { if(s[i]!=c) { s[j++]=s[i]; } s[j]=’\0’; } } |
//trim,简化版,用于删除字符串尾部空格,理解break的使用 int trim(char s[]) { int n; for(n=strlen(s)-1;n>=0;n--)\ { if(s[n]!=’ ’&&s[n]!=’\t’&&s[n]!=’\n’) { break; } s[n+1]=’\0’; return n; } } |
//strcpy void strcpy(char *s,char *t) { while((*s++==*t++)!=’\0’) {} } |
//strcmp int strcmp() { for(;*s==*t;s++,t++) { if(*s==’\0’) { return 0; } return (*s - *t); } } |
8. 在进行复制和函数参数传递时,低精度的可以向高精度的进行自动转换。例如接受float参数的函数乐意接受doulbe类型的参数。高精度要转换成低精度就要进行强制类型转换。
9. 在c语言中,字符数组和字符串很容易引起混淆。c语言中的字符串被当做成字符数组来处理。字符串的定义有两种:
char * c = “Hello”;或者char s[] =“hello”;字符串在字符数组的结尾加上’\0’,因此对于char s2[] = {‘h’,’e’,’l’,’l’,’o’};其sizeof(s2)=5,而sizeof(s)=6。
10. 算法:
//冒泡 void bubble(int s[],int n) { int tmp=0; for(int i=0;i<n;i++) { for (int j=0;j<n-i;j++) { if (s[j]>s[j+1]) { tmp = s[j]; s[j] = s[j+1]; s[j+1] = tmp; } } } } |
//折半查找,x是要查找的数字,v是已排序的数组,n是数组长度 int binsearch(int x,int v[],int n) { int low,high,mid; low = 0; high = n-1; while (low<=high) { mid = (low + high)/2; if(x<v[mid]) { high = mid-1; } else if(x>v[mid]) { low = mid +1; } else return mid; } } |
10. 对于extern修饰符,extern i;表示变量声明,并未生成对象,用于说明i为全局变量,可在不同的文件中调用。
11. 对于static修饰符,在c语言中,使用static声明变量为文件中访问,文件外不能访问该static变量。
12. 寄存器变量使用register表示,其访问速度快,但寄存器地址不能访问。
13. 预处理器:
|-#include:<>系统默认搜索路径,””源文件路径+默认路径
|-#define:编译时进行字符替换
|-优点:节省调用开销
|-缺点:重复计算产生副作用
|-#ifndef…#define…#endif:条件包含
14. 关于*和++(--)的结合性,先++再*;
|-*p++ ==> p++; *p;
|-(*p)++ ==> p指向的对象值+1;
例如int s[]={2,3};int *p = s;printf(“%d,”*p++);结果输出的是2,而不是3,因为是先引用在++;
15. ++i和i++:在VC编译环境下,如果在一个计算表达式中,首先计算++i,然后看有几个i++,完成该表达式后,有几个i++,i就自增几。例如:int i=3,j; j=(i++) + (i++) + (++i);printf(“i=%d,j=%d”,i,j);编译输出为6和12。
16. 在VC的编译器中,printf函数是自右向左执行的。例如,printf(“%d,%d”,i,i+1);首先计算i+1,然后再输出i。
17. 使用宏编写比较两个数的大小
#define max(A,B) ((A)>(B)?(A) : (B)) |
注意一定要加括号,用于放回宏的副作用。
18. 对于32位的机器,指针永远占据4byte的长度,即sizeof(指针)=4;
19. 数组名就是指向数组指针的首地址,与指向数组的指针相比(例如int a[]={2,3};int *p = a;),a不可以a++,但是p可以p++。在函数定义中,char *s < == > char s[].
20. *p++=value;理解为入栈,value放到p原先的位置,然后p++;
val=*--p;理解为出栈,p地址首先减一,然后取值。
21. 命令行参数
#include <stdio.h> void main(int argc,char* argv[])//或者int argc, char**argv { int i; for(i=1;i<argc;i++) { printf(“%s”,argv[i]); } } |
22. 指向函数的指针
//函数的定义并声明 void printf() { printf(“Hello”); }
void (*f)(); //指向函数的指针声明 f = printf(); //赋值、初始化 (*f)(); //调用 |
int (*fun) (par):指向函数的指针,返回类型为int;
int* fun(par):函数,返回类型为int *;
23. union:单块存储区中管理不同类型的数据,其中所有元素相对于union的基地地址的偏移为0;
24. void* malloc(size_t n):返回n字节大小的未初始化的空间
void* calloc(size_t n,size_t size):返回能够容纳n个size大小的空间
PS:
1.#和##分别用与转换字符串和连接字符串。
#define TOSTRING(n) #n #define CONTACT(n,m)n##m |
2.i++和++i效率问题:
对于原生数据,效率一样;对于自定义类型,i++返回的是临时变量(需要使用自增之后的值),++i返回的是引用(使用自增之后的值),效率高。
3.四字节对齐:
#define ALIGN4(n) (((n)+3)&(~3)) |
4.#pragma
#pragma message("Hello") 编译时输出信息 #pragma once 确保该文件被编译一次 |
5.#undef用于注销之前定义的宏变量
6.长度为0的数组:
也成为柔性数组,多用于占位符。不增加类型的大小(因此使用柔性数组而不用char*(四个字节))。这样一次malloc便能申请带有缓冲区的一块内存,并且是连续的内存。柔性数组可以按照数组的访问方式进行读取。如下:
typedef struct strZeroTest { int num; char position[0]; } ZeroTest;
int main() { ZeroTest *pzt = (ZeroTest*)malloc(sizeof(ZeroTest)+10); for (int i = 0; i < 10; ++i) { pzt->position[i] = i; } return 0; } |