本文主要是介绍xiyou2023年3G一面题解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
一、简单说下 int
、short int
、float
、double
、long int
、long long int
在 64 位机申请内存的大小。
64(左)与32位(右)的情况:实践出真知
#include <stdio.h>
int main(){printf("%d\n",sizeof(char)); //1 1printf("%d\n",sizeof(short int)); //2 2printf("%d\n",sizeof(int)); //4 4printf("%d\n",sizeof(long int)); //4 4printf("%d\n",sizeof(long long int)); //8 8printf("%d\n",sizeof(float)); //4 4printf("%d\n",sizeof(double)); //8 8printf("%d\n",sizeof(long double)); //16 12return 0;
}
可见除了long double
外其余变量在64位与32位所占字节数时相同的
另有测试得出加unsigned
对所占字节数无改变 而任意指针类型在64位下占8个,32位下占4个
二、使用 c 语言完成五种交换值的方式。
-
最常用的中间变量交换法
-
常规版
int tep=a; a=b; b=tep;
-
封装函数的指针进阶版
void swap(int *a,int *b){int tep=*a;*a=*b;*b=tep; }
-
宏替换无实际意义版
#define swap(a,b) do{int tep=a;a=b;b=tep;}while(0) //加do-while语句是为了防止与其他语句产生关联性
-
-
数学法
-
形式一
a=a+b; b=a-b; a=a-b;
-
形式二
a=a*b; b=a/b; a=a/b;
-
-
位运算
a=a^b; b=a^b; a=a^b;
-
利用符号优先级的巧妙方法
b在括号中被赋为a后并不影响原算式中b的值
-
形式一
a=b+(b=a)*0;
-
形式二
a=a+b-(b=a);
-
-
栈实现(
createstack
函数以及top
函数省去)压栈
push
弹栈pop
返回当前栈顶元素top
创建一个栈头createstack
typedef struct stack{int data;struct stack *next; }NODE,*Stack; void push(int n,Stack s){Stack node=(Stack)malloc(sizeof(NODE));assert(node);node->element=n;node->next=s->next;s->next=node; } void pop(Stack s){Stack node;if(s->next==NULL){printf("there is a false\n");return;}node=s->next;s->next=node->next;free(node); } void swap(int *a,int *b){Stack s=createstack();push(*a,s);push(*b,s);*a=top(s);pop(s);*b=top(s); }
先将a压入栈中,再压入b,将栈顶元素b的值赋给a,最后弹出b将栈顶元素a的值赋值给b
三、下面代码段将打印出多少个“=” ? 运用相关知识解释该输出。
#include <stdio.h>
int main(){
for(unsigned int i = 3;i>=0;i--){putchar('='); }
}
unsigned
的范围从0到一个正数,所以i的值永远不会为负数,死循环无限输出=
四、逻辑关系的爱恨情仇。简述这个逻辑的运算过程以及输出 answer 的值。
#include <stdio.h>
int count = 100;// 简述逻辑
//这里的count是一个全局变量,作用域和生存域都是整个代码区
int process(int num) { while (1) {//死循环,就算switch走完一遍没有目标也会继续重新执行,除非找到return离开函数switch (num) { case 0: //这是旅途的第一站,对同一个数进行两次相同的异或运算后该数不变,则此处实际是0|10=10//逻辑或是有1则1,全0才0,逻辑异或是相同则0,不同则1num = num ^ 99 ^ 88 ^ 99 ^ 88 | 10; case 15: //上面没有break,还在第一站,10^5=15//第二站进来15^5=10num = num ^ 5; break; case 8: //第四站,num=1*2的八次方,即256,随后这个表达式即num=num-(num++),num还没来得及加就被减为0了,随后加为1//左移实际上等价于*2的num次方num = 1 << num;num -= num++; break; case 10: //第三站,10-3=7,count=100+7=107,此后num变为8num = num + - 3; count = count + num++; break; case 2: //第六站,num变为1--num; case 5: //没有break,仍然是第六站,但有return,会跳出process函数,1<<1即2,answer的值也就明朗了return (1 << num); default: //没有1的情况,所以这里是第五站,各部位符号优先级相同,则从左往右算,1&1=1,条件为真,执行num+1,即num=2//逻辑与是有0则0,全1才1,三元表达式a?b:c意为a如果非0执行b,否则实行cnum = num & 1 ? num + 1 : num; } }
}
int main() { int start = 0; int answer = process(start); // 输出结果 printf("answer == %d\n", answer); return 0;
}
五、struct
与 union
的故事。
#include <stdio.h>
typedef struct str { short e1 ; //2占八分之二char e2 ; //1占八分之三char e3 [ 6]; //6原来的用不完,重开一个占八分之六double e5 ; //8原来的用不完,重开一个占满int e6 ; //4占八分之四
}str ; //则该结构体所占内存为32
typedef union uni { short e1 ; //2char e2 ; //1char e3 [ 6]; //6struct str e4 ; //32double e5 ; //8int e6 ; //4
}uni ; //则该联合体所占内存为32
int main(){ int x = sizeof (str); int y = sizeof (uni); printf ("x = %d ,y = %d \ n " , x , y); printf ("hello = %d \ n " , printf ("3G = %d \n " , --x == 23 && x-- == 22 && ++y == 9)); //逻辑运算只算了--x,x的值变为31//其次后面的printf返回9并把其本身的内容输出//最后前面的printf输出其本身的内容printf ("x1 = %d ,y1 = %d \ n " , x , y); return 0 ; }
此题考点一在于结构体的内存对齐规则☞结构体中的各部分所占内存块会与占内存最大的那个变量对齐
当然也有办法让这个规则失效,在预处理阶段使用#pragame pack()
可以使各部分强制按照()中的数对齐
注:括号中的数只能为1,2,4,8
考点二在于联合体的内存对齐规则☞联合体的内存为其各部分中所占内存最大的那部分
考点三在于逻辑运算的返回值
-
逻辑运算的返回值一般为1(真)或0(假),且存在短路现象
-
短路:
&&
前的结果为假则不进行其后的运算 同样||
前的结果为真则不进行其后的运算 -
x–先判后算,–x先算后判
考点四在于printf
的返回值,其返回值是其""
中的字符数
注:\n
和%d
都只占一个字符
也占一个字符
六、宏函数基础。
#include <stdio.h>
#define SQR(x) x*x
int main() { int i = 1 , j = 2 ; printf ("%d \ n " ,SQR ( i + j)); reutrn 0;
}
宏函数的替换很单纯 真的只是替换
所以SQR(i+j)
会被替换为i+j*i+j
遵循四则运算法则故答案为5
七、小雪今天也有学习指针。
//以下程序运行结果是什么?
#include <stdio.h>
int main() { int nums [ 2][ 5]={{ 2 , 4 , 6 , 8 , 10},{12 ,14 ,16 ,18 ,20}}; //nums是一个由两个小数组组成的大数组int *ptr1=(int*)(&nums + 1); //先对nums取地址,其实和nums一样,也是对大整体加1加到了数组外面int *ptr2=(int*)(&nums [ 0]+ 1); //对nums[0]进行取地址,导致其+1的对象变为了一个小数组,加到了下一个小数组的第一个//此处为什么进行强制类型转化将在下面说明printf ("*nums[0][4]+1 = %d \ n " ,nums [ 0][ 4]+ 1); //第一个小数组的第五个数字,即10,再加1为11+printf ("*(nums[0])+1 = %d \ n ",*(nums [ 0])+ 1); //此处先对nums[0]取值,实际是*(nums[0][0]),即2,再加1为3printf ("*(nums[0]+1) = %d \ n ",*(nums [ 0]+ 1)); //nums[0]即nums[0][0],加1后为nums[0][1],取值后为4printf ("*(nums[1]+1) = %d \ n ",*(nums [ 1]+ 1)); //同上取值后为14printf ("*(nums+1) = %d \ n ",*(nums + 1)); //此处对nums整个大整体加1,会加到一个未定义的区域,取值后无法估值printf ("*(ptr2 -1) = %d \ n ",*(ptr2 - 1)); //由于二维数组实际上内存是连续分布的,第二个小数组第一个减1后到了nums[0][4],即10printf ("*(ptr -1) = %d \ n ",*(ptr1 - 1)); //由数组外面不存在的“nums[2][0]”-1后回归到了nums[1][4],即20return 0 ;
}
关于ptr1
和ptr2
指向地址前先进行强制转化的问题:
nums
本质上是一个二级指针,是一个指向五个一级指针的二级指针,而ptr1
和ptr2
是两个一级指针,直接使一级指针指向二维数组会报错
进行强制类型转化时会发生数据的截断
八、上一个被欺负的指针找来了她的男朋友。
//以下程序运行结果是什么?
#include <stdio.h>int main() { int a [ 3][ 4 ] = {{ 1 , 2 , 3 , 4}, { 3 , 4 , 5 , 6}, { 5 , 6 , 7 , 8}}; int i ; int(* p)[ 4 ] = a,*q = a [ 0]; //由于(*p)首先是一个指针,所以p是一个指向数组的指针;易得q指向的a[0]为1for (i = 0 ; i < 3 ; i++) { if (i == 0 ) (* p)[i + i / 2 ] = *q + 1 ; //i+i/2还是0,将p指向第一个数组的第一个数改为了1+1=2else p++,++ q ; //i总共还能执行两次,p,q均向后移了两位,只是p是以一个数组的长度进行移动,而q是单个移动} for (i = 0 ; i < 3 ; i++) { printf ("a[%d][%d] = %d \ n " , i , i , a [ i][ i]); } //输出三个值,即a[0][0]=2,a[1][1]=4,a[2][2]=7//此处需要注意a[0][0]在之前已经被p改变为2printf ("%d, %d \ n " , *((int*) p), * q); //p其实也可以理解为一个二级指针,所以要先进行强制类型转换再取值,p此前移动到了第三个数组,强转后再取值为5//q移动后的值即3}
九、那就简单排个序吧 。
解释这段代码的作用,以及代码逻辑和相关用法,以及这些函数的特性与作用。
#include <stdio.h>
int int_cmp(const void * p1, const void * p2) { return (*( int *)p1 - *(int *) p2); } //将p强制转化后取值
//比较函数,经常改变形式以适用于qsort函数
//const void*是其适用性广泛的关键,可以任意强制转化来进行相应类型的比较
//如果p1比p2大则会返回1,相等返回0,否则返回-1
void _swap(void *p1, void * p2, int size) { int i = 0; for (i = 0; i< size; i++) { char tmp = *((char *)p1 + i); *(( char *)p1 + i) = *((char *) p2 + i); *(( char *)p2 + i) = tmp; }
}
//看上去写的很复杂,但实际上就是用中间变量交换两个变量的值
//值得注意的是如果p1和p2是两个数组,该交换函数仍然能交换它们所有值,适用性极强
void Qsort(void *base, int count, int size, int (*cmp)(void *, void *)) { //此处调用比较函数借助了一个函数指针cmp去调用函数,通常称其为回调函数//函数指针通常只用将函数原型里的函数名替换为*指针名即可//用函数指针去调用函数,指针只管调用,不用关心具体的函数内容,将调用与函数的关联性削弱//如果遇到了两个相似形式的函数,就可以直接用函数指针改变部分参数去同时调用两个函数int i = 0; int j = 0; for (i = 0; i< count - 1; i++) { for (j = 0; j < count-i-1; j++) { if (cmp ((char *) base + j*size , (char *)base + (j + 1)*size) > 0)//如果比较函数判断大于0,即前者比后者大就执行交换函数,该处比较的始终是两个相邻的元素,可见其本质是冒泡排序//两个for循环不断遍历最终完成排序{_swap(( char *)base + j*size, (char *)base + (j + 1)*size, size); } } }
}
//
int main() { int arr[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0 }; int i = 0; Qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof (int), (int (*)(void *, void *)) int_cmp); //qsort函数位于stdlib库中,原理是快速排序//qsort(要排序的数组,需要排序的元素个数,一个元素的大小,比较函数)for (i = 0; i< sizeof(arr) / sizeof(arr[0]); i++) { printf( "%d ", arr[i]); } printf("\n"); return 0;
}
初看时快排,实际上为冒泡,同时该题处处都在考察对适用泛用性的理解
十、我爱 学数学
下面的算式中,不同的汉字表示不同的数字,相同的汉字表示相同的数字,问这个算式中每个 汉字各代表什么数字?(提示:枚举)
(使用 C 语言向学长学姐解释解题思路及代码,请提前准备好代码,如果不会也没关系可以向 学长学姐讲述一下自己的想法)
爱 = () 数 = ()学 = ()啊 = ()
int x,y,z,t;
for(x=0;x<10;x++){for(y=0;y<10;y++){for(z=0;z<10;z++){for(t=0;t<10;t++){if(x*102+y*10+z*10+t*100==y+x*10+z*100+t*1000&&x!=y)printf("爱=(%d)\t数=(%d)\t学=(%d)\t啊=(%d)\n",t,z,x,y);}}}}
设元乘上各个位数相加,暴力求解即可,除去四个0的情况故加一个x!=y
的限制条件
十一、你了解几种排序算法?简单说一下。
- 冒泡排序(原始版)
void BubbleSort(int n,int*s){int tep;for(int j=0;j<n;j++){for(int k=1;k<n;k++){if(s[k-1]>s[k]){tep=s[k];s[k]=s[k-1];s[k-1]=tep;}}}
}
- 选择排序(原始版)
void SelectSort(int n, int* s){ for(int i=0;i<n-1;i++){ int min=i; for(int cnt=i+1;cnt<n;cnt++){ if(s[cnt]<s[min]){ min=cnt; } } //每轮找到一个最小值与该轮第一个值交换if(min!=i){ int temp=s[i]; s[i]=s[min]; s[min]=temp; } }
}
- 快速排序(原始版)
void quicksort(int *m,int n){if(n<2)return;//递归的结束条件int pleft=0,pright=n-1;int judge=1,middle=m[0];//middle为基准值,比它小的放在左边,比它大的放在右边while(pleft<pright){if(judge==1){if(m[pright]>=middle){pright--;continue;}//直到找到一个比middle小的需要放到左边的为止m[pleft]=m[pright];pleft++;judge=0;continue;}if(judge==0){if(m[pleft]<=middle){pleft++;continue;}//同上 找到一个大的需要放到右边的为止m[pright]=m[pleft];pright--;judge=1;continue;}}m[pleft]=middle;//将被拿出来的基准值放到空出来的位置上quicksort(m,pleft);//先将左边一直递归排序完quicksort(m+pleft+1,n-pleft-1);//排完左边后对右边一直递归排序
}
- 归并排序(递归原始版)
//在mosrt分为每个单份后的合并
void merge(int *arr,int *just,int left,int middle,int right){int l_pos=left;//左边的第一个元素int r_pos=middle+1;//右边的第一个元素int pos=left;//临时存放点while(l_pos<=middle&&r_pos<=right){//每次总是往临时数组中放入左右两个区间中更小的元素if(arr[l_pos]<arr[r_pos]){just[pos++]=arr[l_pos++];}else{just[pos++]=arr[r_pos++];}}//下面两个循环不会同时执行,只会出现一边排得更快导致另一边没排完需要继续排的结果while(l_pos<=middle){just[pos++]=arr[l_pos++];}while(r_pos<=right){just[pos++]=arr[r_pos++];}//将临时数组just中的值返回给arrwhile(left<=right){arr[left]=just[left];left++;}
}
//归并前将各个数字分开
void msort(int *arr,int *just,int left,int right){//保证递归后每个区间只有一个元素if(left<right){int middle=(left+right)/2;//将左边与中间元素分为一组//中间往后第一个元素与最后一个元素分为一组msort(arr,just,left,middle);msort(arr,just,middle+1,right);//将已经分为各个单一的数字归并merge(arr,just,left,middle,right);}
}
//归并前创建存储数组just
void merge_sort(int *arr,int n){int *just=(int*)malloc(n*sizeof(int));if(just){msort(arr,just,0,n-1);free(just);}else{printf("lost malloc");}
}
这篇关于xiyou2023年3G一面题解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!