例一:
int main(){
int *p,*q;
p = malloc(sizeof (int)*20);
q = malloc(sizeof (char )*10);
...
q = p; //内存泄露,q指向的存储单元内容不能找回
...
free(p);
free(q); //重复释放,q/p已经指向同一内存单元
}
修改:将free(q)放在q=p的前面,同时实现两处修改
例二:交换字符串
void fun(char *a, char*b){
char *temp;
*temp =*a; // 内存泄露,指针没有初始化!!!
*a = *b;
*b =*temp;
}
修改:将temp修改为数组,再用字符串复制函数
void fun(char *a, char *b){
char temp[100];
strcpy(temp,a);
strcpy(a,b);
strcpy(b,temp);
//不能使用下面的语句,因为把实参地址作为参数在给形参交换内容
//实际上实参指针并没有改变。
// char *temp =a;
// a = b;
// b = temp;
}
#include
#include
例三:
/*完美乘数:若a*b=c,且a,b,c中每个数字0~9各出现一次*/
int main(){
int f[10],s[3];
int n=0;
for(int a=12;a<999;a++){ //遍历a
int t=0; //当前轮 统计出现1次的数字
for(int x=0;x<10;x++){
f[x]=0;
} //当前轮 初始化10个数字次数为0
for(int b=345;b<9999;b++){ //遍历b
int c; //当前轮 计算c
c= a*b;
s[0] =a;s[1] =b;s[2] =c; //当前轮 a,b,c,因为后面还有输出a,b,c,所以要复制出来
for(int x=0;x<3;x++){
int y =s[x];
while(y!=0){ //数字分离
int t =y%10;
f[t]++; //统计数字0~9
y/=10;
}
}
for(int x=0;x<10;x++){
if(f[x] !=1) t++; //计数,要求每个数字都必须出现一次
}
if(t==0) {
printf("%d*%d =%d", a, b, c);
n++; //输出符合条件的完美乘数
}
}
}
printf("cnt:%d",n);
}
例四:
/*判断出生日期的大小
* res = isbothsame("500236200005194417","500236200104194417");
*/
int isbothsame (char str1[19],char str2[19]){ //可以不指定,二维数组必须指定 列数
int i=6,j=13; // 手写,找出下标
while (str1[i] ==str2[i]) i++; //找到不匹配的字符
return str1[i]-str2[i]; //大:正数 等:0 小:负数
}
例五:
/* 计算:1-x+x^2/2!-x^3/3! +...+x^n/n! */
int main(){
float sum =1.0,temp;
int x,n,i;
printf("输入n,x");
scanf("%d %d",&x,&n);
temp =1;
for (i =1;i<=n;i++){
temp = temp*(-1)*x/i;
sum+=temp;
}
printf("%f",sum);
}
例六:
/*创建链表 进行选择排序*/
typedef struct node{
int data;
struct node *next;
}NODE;
NODE * create(){
NODE * head,*p;
head = (NODE *)(malloc(sizeof (NODE)*8));
head ->data = 100;
head ->next=NULL;
for (int i = 1; i < 8; ++i) {
p =(NODE *) malloc(sizeof (NODE *));
p->data =rand()%10;
p->next =head ->next;
head->next =p;
}
p=head->next;
while(p){
printf("%d",p->data);
p=p->next;
}
printf("\n");
return head;
}
NODE *sort(NODE * head){
NODE *p =(NODE *) malloc(sizeof(NODE)); //仅仅暂时连接头节点
p->next =head;
head =p;
p= head->next; //初始节点的头节点
NODE *k =head,*q =head;
while(p->next){ //选择循环的两层
q = p->next; // 新链表的第一个数已排序节点
k =q; //内存循环的指针
while(q){
if(q->data<k->data) //寻找最小值
k=q;
q =q->next;
}
if(p->next !=k){ //交换data
int temp = p->next->data;
p->next->data =k->data;
k->data =temp;
}
p=p->next; //待排序的第一个数据元素
}
p = head;
head =head->next;
free(p); //释放创建的节点
return head;
}
void print(NODE *head){
NODE *p;
p=head->next;
while(p){
printf("%3d",p->data);
p=p->next;
}
printf("\n");
}
int main(){
NODE * head;
head =create();
sort(head);
print(head);
}
例七:
/* 按快速排序的思想,求数组n个元素中第k大的数 */
void swap(int *a,int *b){
int temp =*a;
*a =*b;
*b =temp;
}
int find_k(int *a,int n,int k){
int i,j,t;
int low =0,high =n-1;
do{ //第一层循环,相当于在二分查找第k大的
i=low;j=high;t=a[low];
do{ //第二层循环,相当于以low为枢轴,划分左、右
while(a[j]>t) j--; //注意:如果后面有相同的数,则要求用前面大的数与其交换
while(a[i]<=t) i++; //注意:前面的数只能是大的数
// [2,4,6,1,3,2,8,10]->[2,2,6,1,3,4,8,10]
// ->[2,2,1(i),6(j),3,4,8,10]->[2,2,1(j),6(i),3,4,8,10]
if(i<j)
swap(&a[i],&a[j]);
else
break;
}while(i<j); // 一趟排序,以a[low]为枢轴,此时i为枢轴下标
if(i==k)
return t;
if(i>k)
high =i-1;
if(i<k)
low =i+1;
}while(low<high);
return t;
}
int main(){
int a[] ={0,1,2,3,4,4,5,6,7,8,9,12};
int n ;
n= find_k(a,12,10);
printf("%d",n);
}
例八:
/* 约瑟夫环问题 */
int lastRemaining(int n, int m){
// 反向计算,正向模拟
int i=0;
int alive= 0; //最后只剩下一个人
for(i=2;i<=n;i++){
alive =(alive+m)%i;
}
printf("%d",alive);
return alive;
}
/* 约瑟夫环问题 */
#define N 10
int main(){
int a[N+1]; // 下标0不使用
int *p=a,i,j,k; // k剩余人数
for(i=0;i<N+1;i++) //初始化下标
*(p+i)=i;
p=a+1; // p模拟移动指针
k=N; // 圈里剩余的人数
for(i=0;k!=1;p++){ //i为间隔计数器
if(p >a+N) //构成环状,循环
p=a+1;
if(*p!=0)
i++;
if(i==3) //离场,人数减一,并将数组元素赋值为0
{
k =k-1;
i=0;
*p =0;
}
}
for(i=0;i<N+1;i++){
if(a[i]!=0)
printf("%d\n",a[i]);
}
}
例九
/* 创建链表,找出最大值并移动到链表尾部 */
typedef struct node{
int data;
struct node *next;
}NODE;
NODE * create(){
NODE * head,*p;
head = (NODE *)(malloc(sizeof (NODE)*8));
head ->data = 100;
head ->next=NULL;
for (int i = 1; i < 8; ++i) {
p =(NODE *) malloc(sizeof (NODE *));
p->data =rand()%10;
p->next =head ->next;
head->next =p;
}
p=head->next;
while(p){
printf("%d",p->data);
p=p->next;
}
printf("\n");
return head;
}
NODE *sort(NODE * head){
NODE *p,*max;
p =head->next;
max =p;
while(p->next){
if (p->data >max->data)
max =p;
p =p->next;
}
int temp =p->data;
p->data =max->data;
max->data=temp;
return head;
}
void print(NODE *head){
NODE *p;
p=head->next;
while(p){
printf("%3d",p->data);
p=p->next;
}
printf("\n");
}
int main(){
NODE * head;
head =create();
print(sort(head));
}
例一:
void main(){
char p[10]="abc";
char q[] ="xyz";
int i=0,j;
while(*(p+i)!='\0') //计算P数组的长度,i=3
i++;
j=0;
i++; //注意,这里又移动了,i=4
while(*(q+j)!='\0'){
*(p+i) =*(p+j);
j++; //注意:这里i没有移动,所以重复修改i=4里面的字符
}
// p= "abc\0z"
printf("%s",p);
}
例二:
/* 质数分解为素数乘积:90 =2*3*3*5
* 任何数都可以分解为素数的乘积 */
void main(){
int n;
printf("input num:");
scanf("%d",&n);
printf("%d=",n);
int i=2;
while(n>1){
if(n%i ==0){
printf("%d",i);
n/=i;
if(n>1) //因为除尽时,最后一定会是1,
printf("*");
}
else //否则,不是素数
i++;
}
}
例三:
/*将给定字符串,如”aaa111bbb222”中的数字全部提取出来,放到给定的数组中,字符串以#结尾 */
void int_find(int *a,char *p){
int j=0;
while(*p !='\0'){
if(*p>='0'&&*p<='9')
a[j++] =*p-'0';
p++;
}
a[j] =-1;
}
void main(){
int num [100],i=0;
char str[] ="aaa111bbb222";
int_find(num,str);
while(num[i]!=-1) {
printf("%d", num[i]);
i++;
}
}
例四:
/* 随机输入最多100个整数和浮点数,将整数按从大到小排列,浮点数按从小到大排列(浮点数排序可省略),并输出
* strstr() 用于判断字符串str2在字符串str1中第一次出现的位置
* atoi() 用于将字符串转化为int型整数 atol() 字符串转化为long
* atof() 用于将字符串转换为double浮点数 abs() 求int的绝对值 fabs()求double的绝对值
*
* */
void sort_i(int a[100] ,int len){
int temp,i,j,max;
for(i=0;i<len-1;i++){
max =i;
for(j=i+1;j<len;j++){
if(a[j]>a[max]){
max=j;
}
}
if(max!=i){
temp =a[i];
a[i] =a[max];
a[max] =temp;
}
}
for(i =0;i<len;i++){
printf("%d",a[i]);
}
}
void sort_f(float f[100],int len){
int temp,i,j,min;
for(i=0;i<len-1;i++){
min =i;
for(j=i+1;j<len;j++){
if(f[j]<f[min]){
min=j;
}
}
if(min!=i){
temp =f[i];
f[i] =f[min];
f[min] =temp;
}
}
for(i =0;i<len;i++){
printf("%d",f[i]);
}
printf("\n");
}
void main(){
float f[100]; //存放浮点数
int i[100]; //存放整数
int m=1,n;
int n1=0,n2=0;
char buff[100]; // 暂存当前输入的数据
printf("input n:");
scanf("%d",&n);
while(m<=n){
scanf("%s",buff);
if(strstr(buff,"."))
f[n1++] =atof(buff);
else
i[n2++] = atoi(buff);
m++;
}
sort_i(i,n1);
printf("\n");
sort_f(f,n2);
}
例五:
/* 创建一个整数集合,实现集合的操作:查找、插入、删除、交集、并集
* */
struct set{
int data;
struct set * next;
};
void add(struct set *a, int d){
struct set *node;
node =(struct set*) malloc(sizeof (struct set));
node->data = d;
node->next =a->next;
a->next =node;
}
int find(struct set *a,int d){
struct set *p =a->next;
while(p){
if(p->data == d)
return 1;
p = p->next;
}
return 0;
}
void delete(struct set *a,int d){
struct set * p=a->next,*pre =a;
while (p) {
if (p->data == d) {
pre->next = p->next;
free(p);
break;
} else {
pre = p;
p = p->next;
}
}
}
void union_set(struct set *la, struct set *lb){
struct set *tempa,*tempb;
tempa =la->next;
while(tempa){
tempb =lb->next;
while(tempb){
if(tempa->data == tempb->data){
printf("%d\t",tempa->data);
break;
}
tempb =tempb->next;
}
tempa =tempa->next;
}
}
void intersection(struct set *la,struct set *lb){
struct set *tempa =la->next,*tempb =lb->next;
int is_find =0;
while(tempa){
printf("%d",tempa->data);
tempa =tempa->next;
}
while(tempb){
if(!(find(tempa,tempb->data))){
printf("%d",tempb->data);
}
tempb =tempb ->next;
}
}
void main(){
struct set *la,*lb;
la = (struct set*)malloc(sizeof (struct set));
lb =(struct set*) malloc(sizeof (struct set));
la ->next =NULL;lb->next =NULL;
int m,n;
printf("分别输入链表la,lb的长度:m,n");
scanf("%d %d",&m,&n);
int i=1,temp;
while(i<=m){
scanf("%d",&temp);
add(la,temp);
i++;
}
i=1;
while (i<=n){
scanf("%d",&temp);
add(lb,temp);
i++;
}
printf("输出集合的并集:");
union_set(la,lb);
printf("输出集合的交集:");
intersection(la,lb);
}
例六:
从提高程序的执行效率的角度,说明C语言采取哪些措施并指出原因:
1.使用指针:对于指针的理解简单点可以认为类似于汇编中的寻址方式,
正是指针的存在使C语言威力无穷。有些程序用其他语言也可以实现,但C能够更有效地实现;
有些程序无法用其它语言实现,如直接访问硬件,但C却可以。
正因为指针可以拥有类似于汇编的寻址方式,所以可以使程序更高效。
2.使用宏函数:函数和宏函数的区别就在于,宏函数占用了大量的空间,而函数占用了时间。
函数调用是要使用系统的栈来保存数据的,如果编译器里有栈检查选项,一般在函数的头会嵌入一些汇编语句对当前栈进行检查;
同时,CPU也要在函数调用时保存和恢复当前的现场,进行压栈和弹栈操作,所以,函数调用需要一些CPU时间。
而宏函数不存在这个问题。宏函数仅仅作为预先写好的代码嵌入到当前程序,不会产生函数调用,所以仅仅是占用了空间,而使程序可以高效运行。
在频繁调用同一个宏函数的时候,该现象尤其突出。
3.使用位操作:位操作可以减少除法和取模的运算。在计算机程序中数据的位是可以操作的最小数据单位,理论上可以用"位运算"来完成所有的运算和操作。
一般的位操作是用来控制硬件的,或者做数据变换使用,但是,灵活的位操作可以有效地提高程序运行的效率。
4.循环嵌套中将较长循环设为内存循环,较短循环设为外置循环,以减少cpu跨切循环层的次数,提高程序的运行效率。
5.上下文相关文法,申明先于使用,使得可以简化处理,错误提示
1:do while:1,-3
2:数组a[],指针p1,p2 :8,8
3:fun§+*p :5
4:switch
5:数组求和:45
6:文件重复打开与写:B
7:fib
8:位运算和左移:12480
1:逆序输出字符串修改:长度、指针初始化、指针元素赋值
1:程序效率:if if if 与if else if else
if if if 必须判断n次,但是if else 可能会提前结束,速度更快
2:宏定义SQUARE(X) (a++)(a++) =56 =30
3:函数调用,循环修改最后一个数组元素
4:截取字符串str后m的字符
5:C语言的隐式转换
6:C语言的
例一:
int func(int *p){ //尽管形参和实参指向同一个存储单元7,但是形参p和实参p是量不同的指针
printf("%p",&p); //输出:000000e7a6fffbf0
return (*p-- =3)-1; //首先将p指向的arr[1]修改为3,计算得出返回值为2;但是形参p--指向arr[0],但是形参p没变
}
void main(){
int arr[] ={10,7,5};
int *p =arr+1; //这里指向7
printf("%p\n",&p); //输出:000000e7a6fffc18
printf("%d",func(p)+ *p); // 2+*p =2+3 =5
}
结果输出:5
例二:
/*将字符串逆序输出 修改程序*/
void main(){
char *str="hello,world";
char *dest,*p,*d;
int len;
len = strlen(str);
dest =(char *)malloc(len); //len+1,为'\0'预留空间
p = str[len]; // &str[len-1]
d =dest;
while(len-- !=0){
d++ =p--; //*d++ =*p--;
}
//增加结束符
printf("%s",dest);
}
例三:
/*建立一个结构体包含学术信息(学号,成绩),使用结构体数组和而结构体指针,
* 输入200个学生信息,按成绩由低到高排序,输出成绩最高的学生的学号和成绩 */
# define N 3
struct stu{
char name[10];
int score;
};
void main(){
struct stu student[N],*p,*q,*min,temp;
int i,j,max=0;
for(i=0;i<N;i++){ //注意:必须指针要移动,要么用p++,要么用数组
scanf("%s",&student[i].name);
scanf("%d",&student[i].score);
}
p= student;
//选择排序
for(i=0;i<N-1;i++,p++){
min =p;
q =p+1;
for(j=i+1;j<N;j++,q++){
if(min->score >q->score){
min =q;
}
}
temp =*p;
*p= *min;
*min =temp;
}
p =student;
for(i=0;i<N;i++){
if(student[i].score>max)
max =student[i].score;
}
for (i = 0; i < N; i++) {
if(student[i].score == max)
printf("score:%d,name:%s",student[i].score,student[i].name);
}
}
例四:
/*逆序构建一个单向链表*/
#define N 3
struct list{
int data;
struct list *next;
};
void main(){
struct list *head,*p;
int n =N;
head =(struct list *) malloc(sizeof (struct list));
head ->next =NULL;
while(n--!=0){
p =(struct list *) malloc(sizeof (struct list));
scanf("%d",&p->data);
p->next =head->next;
head->next=p;
}
p =head->next;
while(p){
printf("%d\t",p->data);
p=p->next;
}
}
1:do while输出1,-3
2:数组a[],指针p1,p2
3:fun§+*p,*p不变实参
4:fib
5:12480数字位运算和左移
6:
7:数组求和45
8:结构体数组引用
1:字符串复制修改:长度,指针初始化,指针复位,结束符
2:文件指针p1,p2修改:内存泄露、重复free
3:交换字符串修改:改用数组、strcpy()
4:字符串复制与指针修改:常量不能改,长度不足
1:素数筛选
2:C语言效率
3:C语言的单词类型(关键字、标识符、运算符、分隔符、常量、注释符…)
4:表格
1:“UESTC\0z”
2:找出1~100内的数和它的平方,同时包含输入整数d
3:截取str中第m个之后的字符
4:C语言的四种隐式类型转化
6:C语言效率(指针、宏定义、位运算、寄存器变量、不对数组做下标检查、无布尔类型、a&&b短路原则、合理malloc/free动态分配存储空间)
7:表格
8:1-x+x^2/2!+
9:完美乘数
1:快排找K小
2:在str1中删除str2中出现的字符
3:链表逆序(头插法,注意节点开始位置)
4:互满数
5:链表L1、L2交集
6:整数降序,浮点数升序
例一:
void main(){
char pa[] ="ABCDE";
char *pb ="efg";
pb[1] ='a'; //字符串常量,不能修改,但是能读取
// printf("%c",pb[1]);
pb= pa;
strcpy(pa,"QBCDEFGH"); //pa内存不够
pb="D";
// printf("%s",pb);
}
例二:如何进行浮点数四舍五入
float fun(float a,int n){
//四舍五入算法:保留n位小数,就是处理n+1位+5的结果
//先放大10^(n+1)倍,计算+5后强转int进行舍入
//最后缩小10^(n)倍,强转为float
int tmp ;
tmp =(int)(a* pow(10,n+1)+5)/10;
printf("%f",(float)tmp/ pow(10,n));
return (float)tmp/ pow(10,n);
}
例三:
#include
/*Eratasheenes 筛选法:求200以内的素数 */
void main(){
int prime[201] ={0}; //因为一个合数必定存在一个<=sqrt(x)的因数,如果不存在,则为素数
int d,k,i;
for(d =2;d<=sqrt(200);d++){ // 已知素数,反推删除合数
if(prime[d] ==0)
for( k=2*d;k<=200;k=k+d)
prime[k] =1; // 1:删除2的倍数 2:删除3的倍数 3:删除5的倍数
}
k=0;
for(i=2;i<=200;i++)
if(prime[i]==0){
printf("%d\t", i); //输出下标
k++;
if(k%5 ==0)
printf("\n");
}
}
例四:
/*互满数:每个数它的所有约数的和正好等于对方 */
#define N 3000
int factor(int j){
int k[40];
int n=0,s=0;
for(int i=1;i<j;i++){ //求不包含自己的因数
if(j%i==0){
k[n++] =i;
}
}
for(int i=0;i<n;i++){
s +=k[i];
}
return s;
}
void main(){
int s1,s2;
for(int m=2;m<N;m++){
s1= factor(m);
for(int n=m+1;n<N;n++){
s2= factor(n);
if((s1==n)&&(s2==m))
printf("%d 和%d是互满数:",m,n);
}
}
}
/*220 和284是互满数:
1184 和1210是互满数:
2620 和2924是互满数:*/
1:数组a[],指针p1,p2输出
2:Fib
3:结构体数组引用
1:字符串逆序修改:长度、循环控制、结束符
1:找出1~100内的数和它的平方,同时包含输入整数d
1:C语言的四种隐式类型转换(算术运算、赋值运算、实参形参、函数返回值)
2:C语言提高效率(指针、宏定义、位运算、嵌入汇编)
3:数组越界产生的后果:
4:数组
1:快排找第K小
2:存储区表格(常量区、全局区、main、func)
3:素数筛选求200以内
4:凯撒加密
5:逆序构建双向链表
改错题
1:数组指针未分配:malloc
2:冒泡排序,大数已排序在尾部,i=0,j=0开始
阅读理解
1:辗转gcd
2:字符串a中插入字符串b
3:三个不同函数在随机生成1~100随机数的区别:
函数功能,时间复杂度计算,优缺点
4:乱序数组求中位数,程序改进:
排序过程中swap太多;float数直接比较大小;偶数长度求中位数没有取2个数的均值;变量类型错误
填空题:
1:对student结构体排序
2:在字符数组str[]中,从第n个开始删除长度为len的字符
3:实现链表L,第m个到第n个节点的逆序
4:找通项,求和
简答题
1:C语言编程实现网络数据传输,使用TCP/IP协议还是HTTP协议
WinSock通讯的所有数据传输,都是通过套接字来完成的,套接字包含了两个信息,一个是IP地址,一个是Port端口号,使用这两个信息,就可以确定网络中的任何一个通讯节点。
使用Socket进行网络通信的过程:
① 服务器程序将一个套接字绑定到一个特定的端口,并通过此套接字等待和监听客户的连接请求。
② 客户程序根据服务器程序所在的主机和端口号发出连接请求。
③ 如果一切正常,服务器接受连接请求。并获得一个新的绑定到不同端口地址的套接字。
④ 客户和服务器通过读、写套接字进行通讯。
2:怎么对客户定制的软件进行测试
按功能分:黑盒测试(功能性:等价划分、边界值分析、错误推测)、白盒测试(结构性:语句覆盖、分支覆盖、条件覆盖、判定/条件覆盖、条件组合覆盖、点覆盖、边覆盖、路径覆盖)
按时间分:单元测试、子系统测试、系统测试、验收测试
编程
1:一个循环数组大小为n,有序,但不知道向哪个方向有序,也不知道起点在哪里,设计算法,找出数组里的最小值,要求时间复杂度为O(logn),超时不给分