2023年ggg一面题解

一、简单说下 intshort intfloatdoublelong intlong long int 在 64 位机申请内存的大小。

64(左)与32位(右)的情况:实践出真知

#include <stdio.h>
int main(){
    printf("%d\n",sizeof(char));	 		//1	   1
    printf("%d\n",sizeof(short int));		//2	   2
    printf("%d\n",sizeof(int));				//4	   4
    printf("%d\n",sizeof(long int));		//4	   4
    printf("%d\n",sizeof(long long int));	//8	   8
    printf("%d\n",sizeof(float));			//4	   4
    printf("%d\n",sizeof(double));			//8	   8
    printf("%d\n",sizeof(long double));		//16   12
    return 0;
}

可见除了long double外其余变量在64位与32位所占字节数时相同的

另有测试得出加unsigned对所占字节数无改变 而任意指针类型在64位下占8个,32位下占4个

二、使用 c 语言完成五种交换值的方式。

  1. 最常用的中间变量交换法

    • 常规版

      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语句是为了防止与其他语句产生关联性
      
  2. 数学法

    • 形式一

      a=a+b;
      b=a-b;
      a=a-b;
      
    • 形式二

      a=a*b;
      b=a/b;
      a=a/b;
      
  3. 位运算

    a=a^b;
    b=a^b;
    a=a^b;
    
  4. 利用符号优先级的巧妙方法

    b在括号中被赋为a后并不影响原算式中b的值

    • 形式一

      a=b+(b=a)*0;
      
    • 形式二

      a=a+b-(b=a);
      
  5. 栈实现(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,不同则1
            num = num ^ 99 ^ 88 ^ 99 ^ 88 | 10; 
        case 15:  //上面没有break,还在第一站,10^5=15
                //第二站进来15^5=10
            num = 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变为8
            num = 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,否则实行c
            num = num & 1 ? num + 1 : num; 
    	} 
	} 
}
int main() { 
    int start = 0; 
    int answer = process(start); // 输出结果 
    printf("answer == %d\n", answer); 
    return 0; 
}

五、structunion 的故事。

#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 ; //2
    char e2 ;  //1
    char e3 [ 6]; //6
    struct str e4 ; //32
    double e5 ; //8
    int 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为3
    
    printf ("*(nums[0]+1) = %d \ n ",*(nums [ 0]+ 1)); //nums[0]即nums[0][0],加1后为nums[0][1],取值后为4
    printf ("*(nums[1]+1) = %d \ n ",*(nums [ 1]+ 1)); //同上取值后为14
    printf ("*(nums+1) = %d \ n ",*(nums + 1)); //此处对nums整个大整体加1,会加到一个未定义的区域,取值后无法估值
    
    printf ("*(ptr2 -1) = %d \ n ",*(ptr2 - 1)); //由于二维数组实际上内存是连续分布的,第二个小数组第一个减1后到了nums[0][4],即10
    printf ("*(ptr -1) = %d \ n ",*(ptr1 - 1)); //由数组外面不存在的“nums[2][0]”-1后回归到了nums[1][4],即20
    return 0 ; 
}

关于ptr1ptr2指向地址前先进行强制转化的问题:

nums本质上是一个二级指针,是一个指向五个一级指针的二级指针,而ptr1ptr2是两个一级指针,直接使一级指针指向二维数组会报错

进行强制类型转化时会发生数据的截断

八、上一个被欺负的指针找来了她的男朋友。

//以下程序运行结果是什么?

 #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]为1
     for (i  = 0 ; i  < 3 ; i++) { 
         if (i == 0 ) (* p)[i  + i  / 2 ] = *q  + 1 ; //i+i/2还是0,将p指向第一个数组的第一个数改为了1+1=2
         else  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改变为2
     printf ("%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中的值返回给arr
	while(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");
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值