c语言基础题(3/3)

#2020面试试题题解

1. 请试着解释其输出。

int main(int argc , char *argv[]) {
  unsigned char a = 255;
  char ch = 128;
  a -= ch;
  printf("a = %d ch = %d\n", a, ch);
}
  • solution
  • 运行结果:a = 127 ch = -128
  • 分析:
  • 利用limits.h头文件,可以计算出各个数据类型的范围:
  • UCHAR_MAX=255,CHAR_MAX=127
  • 而超出的部分便从最小值再依次增加

2. 下面代码的运行输出结果是什么,并说说你的理解。

int main(int argc, char *argv[]) {
  char *str = "Xi You Linux Group 20";
  printf("%d\n", printf(str));
  return 0;
}

solution

  • 运行结果:Xi You Linux Group 2021
  • 这道题是对于printf的返回值与输出顺序的考察:
  • ①返回值:若成功则返回输出的字符数,输出出错则返回负值
  • ②输出顺序:printf函数从左往右读取,然后将先读取放到栈底,最后读取的放在栈顶,处理时候是从栈顶开始的.所我们看见的结果是,从右边开始处理的.

3. 这段代码的输出结果是什么?为什么会出现这样的结果?

int i = 2;
void func() {
  if(i != 0) {
  static int m = 0;
  int n = 0;
  n++;
  m++;
  printf("m = %d, n = %d\n", m, n);
  i--;
  func();
  } else {
  return;
  }
}
int main(int argc, char *argv[]) {
  func();
  return 0;
}

solution

  • 运行结果:
  • m = 1, n = 1
  • m = 2, n = 1

  • ①全局变量 :
  • 存储位置 : 存放在静态存储区中。因此他们的生存周期是固定的,存在于程序的整个运行过程中。
  • 全局变量的作用范围 : 一般是从定义位置开始到本程序文件的末尾。在此作用域内,全局变量可以为程序中各个函数所引用。
  • ②局部变量 :
  • 局部变量的有效范围是局限于函数内部的,形参也是局部变量局部变量的改变无法影响到主调函数

4. 下面程序会出现什么结果?为什么会出现这样的结果?

int main(int argc, char * argv[]) {
  char ch = 'A';
  int i = 65;
  unsigned int f = 33554433;
  *(int *)&f >>= 24;
  *(int *)&f = *(int *)&f + '?';
  printf("ch = %c i = %c f = %c\n", ch, i, *(int *)&f);
  return 0;
}

solution

  • 运行结果:ch = A,i = A,f = A

  • 分析:
  • ①强制类型转换:
  • unsigned 为4字节,int*为8字节
  • 对于*(int *)&f
  • &f是f的地址
  • (int *)&f使其强制转换为成指向int 类型数据的指针
  • *便是求解该指针所指向对象的值
  • ②位运算:
  • f>>=24
  • 在f的二进制代码下,左移24位,高位补0,低位去掉
  • 最后得到f=2,?的ASCII为63,i的ASCII为65

5. 下面代码的运行输出结果是什么,并说说你的理解。

int main(int argc, char *argv[]) {
  int a[2][2];
  printf("&a = %p\t&a[0] = %p\t&a[0][0] = %p\n", &a, &a[0], &a[0][0]);
  printf("&a+1 = %p\t&a[0]+1 = %p\t&a[0][0]+1= %p\n", &a+1, &a[0]+1, &a[0][0]+1);
  return 0;
}

solution

  • 运行结果:&a = 0x7fffffffdcd0 ,&a[0] = 0x7fffffffdcd0
  • &a[0][0] = 0x7fffffffdcd0 , &a+1 = 0x7fffffffdce0
  • &a[0]+1 = 0x7fffffffdcd8 , &a[0][0]+1= 0x7fffffffdcd4

分析:

数组名对应地址
arr[0]&arr[0][0]
arr[1]&arr[1][0]
…………
arr[4]&arr[4][0]

其次的有关于指针与数组名的运算

  • 进行+,-的运算,每次移动所指向对象所占的字节乘以+与-的大小的距离
  • 指针可以进行单目++,–运算
  • 数组名不可以进行单目运算,否则首地址位置一变,所指向对象就变了

6. 下列程序的功能是什么?有什么问题,你能找出问题并解决它吗?

int* get_array() {
  int array[1121]; 
  for (int i = 0; i < sizeof(array) / sizeof(int); i++) {
    array[i] = i;
  }
  return array;
}
int main(int argc, char *argv[]) { 
  int *p = get_array();
}

solution

  • 问题:函数返回了局部变量的地址

  • 解决:
  • array定义为全局变量

7. 下面代码的运行输出结果是什么,并说说你的理解。

int main(int argc, char *argv[]) {
  char str[] = "XiyouLinuxGroup"; 
  char *p = str; 
  char x[] = "XiyouLinuxGroup\t\106F\bamily";
  printf("%zu %zu %zu %zu\n", sizeof(str), sizeof(p), sizeof(x), strlen(x));
  return 0;
}

solution

  • 运行结果:16 8 25 24

  • 分析:
  • sizeof与strlen函数
  • 共同点:都是以字节为单位返回大小
  • 区别 :strlen是函数,而sizeof是算符
  • strlen:返回字符串的大小,所以以\0为截止
  • sizeof:返回对象所占内存的大小
  • \t(水平制表符),\b(退格),\106(ASCII为106的字符)

8. 如下程序,根据打印结果,你有什么思考?

int add(int *x, int y) {
  return *x = (*x^y) + ((*x&y)<<1);
}
int a;
int main(int argc, char *argv[]) {
  int b = 2020;
  if(add(&b, 1) || add(&a, 1)) {
  printf("XiyouLinuxGroup%d\n", b);
  printf("Waiting for y%du!\n", a);
  }
  if(add(&b, 1) && a++) {
  printf("XiyouLinuxGroup%d\n", b);
  printf("Waiting for y%du!\n", a);
}
  return 0;
} 

solution

  • 运行结果:
  • XiyouLinuxGroup2021
  • Waiting for y0u!

  • 分析:
  • a为全局变量,若未初始化,则自动赋0
  • 主要考察位运算:

符号描述运算规则实例
&两个都为1,结果为10001 & 0001 = 1
|两个位都为0时,结果才为0。0000|0001=0001
^异或两个位相同为0,相异为10001 ∧ 0001 = 0000
~取反0变1,1变0。∼ 0 = 1 , ∼ 1 = 0
<<左移各二进位全部左移若干位,高位丢弃,低位补0。0001 < < k = 0100
>>右移各二进位全部右移若干位,对无符号数,高位补0,有符号数,右移补1 11。0100 > > k = 0001

9. 在下段程序中,我们可以通过第一步打印出a的地址,假如在你的机器上面打印结果是0x7ffd737c6db4;我们在第二步用scanf函数将这个地址值输入变量c中;第三步,随机输入一个数字,请问最终输出了什么结果,你知道其中的原理吗?

void func() { 
  int a = 2020;
  unsigned long c;
  printf("%p\n", &a);
  printf("我们想要修改的地址:");
  scanf("%lx", &c);
  printf("请随便输入一个数字:");
  scanf("%d", (int *)c);
  printf("a = %d\n", a);
}

  • solution
  • 分析
  • 原理:人工指针!!!
  • 第一步得到了a的地址
  • 第二步把a的地址赋值给c,那么c的值便是a的地址
  • 第三步,先进行强制类型转换,使数据类型相匹配,然后把输入的值存储到a的地址上去
  • 结果a的值变为了输入的值

10. 请问一个C语言程序从源代码到可执行文件中间会进行哪些过程,你能简单描述一下每个环节都做了什么事情吗?


  • 1.预处理
  • 预处理负责对源代码进行文本处理。主要处理代码中以字符#开头的命令。
  • 有以下几个处理的地方:
  • (一)解析所有的条件预处理指令。
  • (二)解析定义的宏,将代码中使用的宏进行替换。
  • (三)删除注释
  • (四)解析 #include,将引入的头文件拷贝到当前命令位置
  • 完成这些后,以从c为后缀的文本文件生成了以.i为后缀的文本文件。
  • 2.编译
  • 由.i的文本文件生成.s的文本文件。此文件为以汇编语句完成的代码
  • 3.汇编
  • 经过汇编阶段把汇编语句转换为机器指令,由.s的文本文件生成.o的二进制文件。
  • 4.链接
  • 链接器将所有二进制的目标文件和系统组件组合成一个可执行文件(.exe)
  • 汇编结束后每个源文件都会生成一个.o文件,这里的目标文件就是这些.o文件

11. 请解释一下这行代码做了什么?

puts((char*)(int const[]){
0X6F796958,0X6E694C75,0X72477875,
0X3270756F,0X313230,0X00000A
});

solution

  • 运行结果:XiyouLinuxGroup2021

  • 分析:
  • ①int const []是一个指向{……}的指针
  • ②强制类型转换为(char*)型
  • ③puts(地址)为输出函数

对于一段16进制的解析:
对于一个,如(0X6F96958)16进制数,在计算机中以二进制来储存:0000 0110 1111 1001 0110 1001 0101 1000

二进制代码ASCII编码字符
00000110111o
11111001121y
01101001105i
0101100088X

12. 请随机输入一串字符串,你能解释一下输出结果吗?

int main(int argc, char *argv[]) {
  char str[1121];
  int key;
  char t;
  fgets(str, 1121, stdin);
  for(int i = 0; i < strlen(str) - 1; i++) {
    key = i;
    for(int j = i + 1; j < strlen(str); j++) {
      if(str[key] > str[j]) {
        key = j;
      }
    } 
    t = str[key];
    str[key] = str[i];
    str[i] = t;
  } 
  puts(str);
  return 0;
}

solution

  • ①fgets为字符串输入函数,输入一个字符串存到str
  • ②两个for循环,依次比较第i与i+1的大小,然后选出asci码最小字符所对应的数组下标
  • ③把最小的字符与倒数第二个字符交换
  • ④得到新的数组str

13. 用循环和递归求Fibonacci数列,你觉得这两种方式那种更好?说说你的看法。如果让你求Fibonacci数列的第100项,你觉得还可以用常规的方法求解吗?请试着求出前100项的值(tip大数运算)。

#include <stdio.h>
int fib(int n){
	if (n == 1){
		return 1;
	}
	if (n == 2){
		return 1;
	}
	return fib(n - 1) + fib(n - 2);
}

int main()
{
	int n = 0;
	printf("请输入要求第几个数字:");
	scanf("%d", &n);
	printf("%d\n", fib(n));
	return 0;
}


  • 由于int为4字节,即使用long long 100位也会超出范围
  • 所以此时应该用高精度运算,即创建一个数组,来模拟加法运算

14. Linux 实操题

请通过命令创建一个目录,在该目录中创建几个后缀为.Linux的文件,然后通过命令查询这几个文件的基本属性信息(如文件大小,文件创建时间等),之后使用命令查看该目录下文件名含有“.Linux”的文件的数量(不包括子目录下的文件),把得到的数字写入到一个文件中,最后删除此目录。


  • 创建目录:mkdir
  • 创建文件:touch 1.lunxu
  • 查看命令:ls
  • 删除:rm
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值