Linux兴趣小组面试题总结lt

本文是Linux兴趣小组面试题总结,涉及C语言多方面知识。介绍了大小端存储规则及相关例题,阐述const指针、static用法、sizeof与strlen区别、字节对齐规则、数组地址含义、形参传递方式、无符号与有符号数相加、宏定义参数展开规则,还提及程序运行的预处理过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Linux兴趣小组面试题总结

做了近几年的纳新面试题,感觉发现了新大陆,自己的菜emmm…虽然C语言是一门高级语言,但是它很接近底层,能直接操作内存。通过做这几份面试题,我对这句话有了更深的理解。

大小端

对于跨越多字节的程序对象,我们必须建立两个规则:这个对象的地址是什么,以及在内存中如何排列这些字节。在几乎所有的地址上,多字节对象都被存储为连续的字节序列,对象的地址为使用字节中最小的地址。
某些机器选择在内存中按照从最低有效字节的顺序到最高有效字节的顺序存储对象,而另一种机器则按照从最高有效字节到最低有效字节的顺序存储。前一种规则——最低有效字节在最前面的方式,称为 小端法 。后一种规则——最高有效字节在最前面的方式,称为 大端法
小端法 即高地址对高字节。大端法 即低地址对高字节。
比如整形十进制数字:305419896 ,转化为十六进制表示 : 0x12345678 。其中按着十六进制的话,每两位占8个字节。

int main()
{
	int a=0x12345678;
	printf("%x\n",a);
}
输出结果为12345678;

整型变量a小端模式内存分配如下:在这里插入图片描述

可看出小端模式的读出方式:从字节中最大的地址(首地址+内型字节数-1)到最小的地址。上题中从0x0003到0x0002到0x0001最后是0x0000。

例题 2015.1

int main()
{
	int c;
	memcpy(&c,"linux",4);
	printf("%d\n",c);
	return 0;
}
输出结果为:1970170188   

1970170188的十六进制值为0x756e694c

小端法内存分配如下:

地址数据
0x00000x4c('l’的ASCAII码值)
0X00010x69('i’的ASCAII码值)
0X00020x6e('n’的ASCAII码值)
0X00030x75('u’的ASCAII码值)
0X00040x78('x’的ASCAII码值)

int 占4个字节,所以为0x0000到0x0003
读出:首地址(0x0000)+sizeof(int)-1=0x0003,从高地址到低地址读出,以16进制形式读出:0x756e694c,10进制数为1970170188。
例题 2017.17

struct node
{
 	char a;
 	short b;
 	int c;
}
int main()
{
	struct node s;
	memset(&s,0,sizeof(s));
	s.a=3;
	s.b=5;
	s.c=7;
	struct node *pt=&s;
	printf("%d\n",*(int *)pt);
	printf("%lld\n",*(long long*)pt);
	return 0;
}
输出结果为
327683
30065098755

327683的16进制为0x50003。
小端法内存分配如下:
在这里插入图片描述
读出
1.地址(0x0000)+sizeof(int)-1=0x0003,从高地址到低地址读出,以16进制形式读出:0x00050003,10进制数为327683
2.地址(0x0000)+sizeof(long long)=0x0007,从高地址到低地址读出,以16进制形式读出:0x0000000700050003,即0x700050003,10进制数为30065098755.

例题2017.18

  4 #define NAME(n) x##n
  5 #define PRINTVAL(y,...) printf("x"#
  6 int main()
  7 {
  8     int NAME(1);
  9     short *NAME(2)=(short *)&NAME(1);
 10     char  *NAME(3)=(char  *)&NAME(1);
 11     NAME(1)=0;
 12     *NAME(2)=-1;
 13     PRINTVAL(1,"%x\n",NAME(1));
 14     PRINTVAL(2,"%x\n",*NAME(2));
 15     PRINTVAL(3,"%x\n",*NAME(3));
 16 }       
输出结果为:ffff
		  ffffffff
	      ffffffff

读入:NAME(1)=x1; *NAME(2)=*x2=x1; *NAME(3)=*x3=x1;
x1 为int 类型,小端模式。
指针的类型是指被指向的所占内存大小,与自身无关,x86-64,Linux-gcc编译下指针自身被分配8个字节,short占2位,char占1位。
x2为 short 类型指针,所占内存为0x0000和0x0001
x3为 char 类型指针,所占内存为0x0000。
*x2=-1,-1的补码为0xffff,所以0x0000和0x0001被改为为0xffff。在这里插入图片描述
读出:
int 类型以小端模式读出:0x0000ffff即ffff;
*x2 为short类型,读两个字节(0x0000和0x0001)为0xffff,%x 读出默认4字节,补两个字节,符号扩展添加最高有效位的副本。输出为0xffffffff。
*x3为char类型,读一个字节(0x0000),补三个字节。输出为0xffffffff。


例题2018.9

int main()
{
	char n[]={1,0,0,0};
	printf("%d\n",*(int *)n);
	return 0;
}

输出结果为:1;//结果简单吧,其实并不简单哦*~* !

在这里插入图片描述
读入:数组的存放在内存中是从低到高按顺序的,大小端不适用哦。
读出:强制转换为int *类型,采用大小端模式读出。
看到这,你是不是在想,这人是不是傻逼啊。哪有那么复杂~~…

int main()
{
	char n[]={0x12,0x34,0x56,0x78};
	printf("%x\n",*(int *)n);
	return 0;
}

你猜他的输出是什么?

输出结果为:78563412

例题2018.12

int main()
{
	FILE *fp=fopen("Linux.txt","wb"); //注意是以二进制形式写入
	long long a=0x78756e694c;
	fwrite(&a,sizeof(a),1,fp);
	fclose(fp);
	return 0;
}

文件(运行程序前在源文件所在路径有文件linux.txt)中将会存入"linux"字符串。
读入:longlong 类型占8个字节,内存分配如下表:

地址数据
0x00000x4c('l’的ASCAII码值)
0X00010x69('i’的ASCAII码值)
0X00020x6e('n’的ASCAII码值)
0X00030x75('u’的ASCAII码值)
0X00040x78('x’的ASCAII码值)
0X00050x00
0X00060x00
0X00070x00

所以读出为Linux。00为字符串结束符,不打印。

const指针

例题2015.10

int a=3;
const int *p1;   //不能改变p1所指向的值
int const *p2;   //不能改变p2所指向的值
int *const p3=&a; //不能改变p3的地址,即p3的指向,但能改变a的值。

2017.7

const char *p;
char const *p;
char *const p;
const char *const p;  //既不能改变p3所指向的值,也不能改变p3的指向(p3的值);

2018.5

char y[]="XiyouLinuxGroup",x[]="2018";
char *const p1=y;
const char *p2=y;
p1=x;     //是错的
p2=x;
*p1='x';	
*p2='x';	//是错的

static问题

例题2015.2
static用法包括:
(1)局部变量:程序运行过程中不释放内存。
(2)全局变量和函数:限定只能在本文件中使用,别的文件无法使用;如果不限定,大型文件合并过程中可能出现名称冲突。所以全局变量前加static是一个良好的编程习惯。
例题2015.2

int *func(void)  //static局部变量用法
{
	static int a=1;
	a++;
	return &a;
}
int main()
{
	int *b;
	b=func();
	printf("%d\n",*b);
	b=func();
	printf("%d\n",*b);
	return 0;
}
输出结果: 2
	     3
extern a;
void func()
{
	printf("a=%d\n",a);
}
int a=10;
int main()
{
	func();
	return 0;
}

是不是很好奇我为什么把这个代码放在这里,是不是感觉没啥关系?
当然有关系,如果全局变量或者函数前没有加static关键字,用extern关键字来声明
例题 2017.5

static 全 局 变 量 与 普 通 的 全 局 变 量 有 什 么 区 别?
static 局部变量和普通局部变量有什么区别? static
函数与普通函数有什么区别?

例题 2018.3

static int a=2018;
stativ void func()
{
	static int b;
	printf("a=%d,b=%d\n",a++,++b);
}
int main()
{
	func();
	func();
	func();
	return 0;
}
输出结果为:a=2019,b=1;
		  a=2019,b=2;
		  a=2019,b=3;

static 全局变量改变作用域
static 局部变量改变存储期

sizeof与strlen问题

sizeof运算符,在编译时即计算好了 ,参数可以是数组,指针,类型,对象函数。是关键字,在头文件中typedef 为 unsigned int。功能是:获得保证能容纳实现所建立的最大对象的字节大小。

strlen 是函数,要在运行时才计算,参数必须是字符型指针。当数组把它作为参数传入时,实际上数组 就化成指针 了。功能是返回字符串的长度。该字符串可能是自己定义的,也可能是内存中随机的。该函数实际完成的功能是从代表该字符串的第一个地址开始遍历,知道遇到结束符NULL,返回值不包括NULL .

例题2015.3

void func(char *a)
{
    printf("%lu\n",sizeof(a));
    printf("%lu\n",strlen(a));
}
int main()
{
	char a[]="hello world";
	func(a);
	printf("%lu\n",sizeof(a));
	printf("%lu\n",strlen(a));
	return 0;
}
输出结果为:8		//传入函数是指针化了,输出为指针内存大小,linux下指针为8字节。
		  11	//当传入strlen函数是就化为指针了,所以都为11字节,不包括NULL(字符串结束符)。
		  12	//获得保证能容纳实现所建立的最大对象的字节大小,在这为数组,包括NULL。
		  11

例题2015.13

int main()
{
   char *str1 = "WelcomeTo\0XiyouLinux";
   char str2[]= "WelcomeTo\0XiyouLinux";
   printf("%d\n",printf("%s",str1));
   printf("%d,%d\n",strlen(str1),strlen(str2));
   printf("%d,%d\n",sizeof(str1),sizeof(str2));
}
输出结果为:WelcomeTo9	//9为printf返回值
		  9,9			//遇到'\0'就结束读取
		  8,22			//第一个为指针,第二个一直到字符串结束,包括结束符。

例题2017.1

int main()
{
    int t=4;
    printf("%lu\n",sizeof(t--));	//sizeof运算符,在编译时即计算好了 ,t的值是不变的。
    printf("%lu\n",sizeof(ab c\nt\012\xa1*2));
}
输出结果为:4
		  11

例题2018.2

int main()
{
	int a[3][2]={2,0,1,8};
    char *str=(char *)malloc(sizeof(char)*20);
    strcpy(str,"\0101\\xb2");
    printf("%zu\n",sizeof(a));
    printf("%zu %d\n",sizeof(a[1][1]=0),a[1][1]);
    printf("%zu %zu\n",sizeof(str),strlen(str));
    return 0;
}
输出结果为:24	//一个int类型的占4个类型,所以共24个字节
		  4 8	//sizeof编译阶段即计算好了,所以a[1][1]的值不改变
		  8 6	//sizeof计算指针大小,strlen计算str数组内字节数'\010','\\','x','b','2'各占一个

字节对齐

结构体中数据成员对齐规则:

​ 1. 结构体(struct)联合(union) 的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员的大小或该成员的子成员大小(只要该成员有子成员)的整数倍开始 .

​ 2.结构体作为成员:如果一个结构体里有某些结构体成员,则结构体成员要从***内部最大元素大小的整数倍地址开始存储***。

​ 3.收尾工作:结构体的总大小,也就是sizeof的结果,必须是内部最大成员的整数倍,不足要补齐

例题2018.11 (2015.7)

struct icd
{
	int a;
	char b;
	double c;
};
struct cdi
{
    char a;
    double b;
    int c;
};
int main()
{
    printf("%zu %zu\n",sizeof(struct icd),sizeof(struct cdi));
    return 0;
}

输出结果为:16 24

在这里插入图片描述

数组地址含义问题

例题2015.6(二维数组)

int main()
{
    int a[3][4];
    printf("&a=%p\t&a+1%p\n",&a,&a+1);
    printf("a=%p\ta+1%p\n",a,a+1);
    printf("&a[0]=%p\t&a[0]+1%p\n",&a[0],&a[0]+1);
    printf("a[0]=%p\ta[0]+1%p\n",a[0],a[0]+1);
    printf("&a[0][0]=%p\t&a[0][0]+1%p\n",&a[0][0],&a[0][0]+1);
    
}
输出结果为://地址结果为随机值,但之间是一种必然。
&a=0x7ffc9fb2df40		&a+1=0x7ffc9fb2df70		 //int[][],代表整个数组
a=0x7ffc9fb2df40		a+10=x7ffc9fb2df50		 //int[]类型 代表一行,a与&a[0]含义相等
&a[0]=0x7ffc9fb2df40	&a[0]+1=0x7ffc9fb2df50	
a[0]=0x7ffc9fb2df40		a[0]+1=0x7ffc9fb2df44	 //一个int类型,a[0]+1与&a[0][0]+1的含义相等
&a[0][0]=0x7ffc9fb2df40	&a[0][0]=+10x7ffc9fb2df44

观察可知,第一列的地址值是相同的。

例题2015.12(一维数组) (2017.11)(2018.6)

int main()
{
    int a[5]={1,2,3,4,5};
    int *ptr=(int *)(&a+1);				//&a代表整个数组
    printf("%d,%d\n",*(a+1),*(ptr-1));
}
输出结果为:1,5

形参传递

例题2018.13(2015.19)(2017.3)

1.值传递,指函数调用时,为形参分配存储单元,并将实参的值复制到形参,函数调用结束后,形参所占内存单元 被释放,值消失。特点是:形参和实参各占不同的内存单元,函数对形参值的改变不会改变实参的值,这就是参数的单向传递规则

2.地址传递,是指在函数调用是,将实参数据的存储地址作为参数传递给形参。其特点是:形参和实参占用同样的内存单元函数中对形参值的改变也会改变实参的值。因此,函数参数的地址传递方式可以实现调用函数与被调函数之间的双向数据传递

typedef struct a
{
    char  name[20];	//改之前char *name;或者不改变name的类型,改变name的赋值方式,直接复制。
    int num;
}A;
A * func(A *a)
{
    a=(A *)malloc(sizeof(A));
    strcpy(a-name,"XiyouLinuxGroup");//strcpy只能操作数组,无法操作指针
    a->num=2018;
    return a;		//返回地址,建立形参与实参的传递
}
int main()
{
    A *a;
    a=func(a);
    printf("%s %d\n",a->name,a->num);
    return 0;
}
输出结果为:XiyouLinuxGroup 2018

无符号与有符号数相加

例题2015.4(2017.10)

在计算机中,有符号数以二进值补码表示,无符号以原码表示

6的二进数为0000 0000 0000 0110,-20的补码为1111 1111 1110 1100

相加转化为无符号数为1111 1111 1111 1110显而易见大于6的补码1111 1111 1111 1010

void func()
{
	unsigned int a=6;
	int b=-20;
	(a+b>6)?puts(">6"):puts("<6");
}
输出结果为:>6

宏定义参数

例题 2018.10(2015.8)

#是把宏参数变为一个字符串 ##是把两个宏参数连接在一起

如何展开宏参数的规则:在展开宏参数时,如果形参有**#或##不进行宏参数的展开,否则先展开宏参数,再展开当前宏**。

#define YEAR 2018
#define LEVELONE(x) "XiyouLinux"#x"\n"
#define LEVELTWO(x) LEVELONE(x)
#define MULTIPLY(x,y) x*y
int main()
{
    int x=MULTIPLY(1+2,3);
    printf("%d\n",x);
    printf(LEVELONE(YEAR));
    printf(LEVELTWO(YEAR));
}
输出结果为:7
		 XiyouLinuxYEAR
		 XiyouLinux2018

程序运行过程

在这里插入图片描述
预处理过程中,包括注释和宏展开。

基于数据挖掘的音乐推荐系统设计与实现 需要一个代码说明,不需要论文 采用python语言,django框架,mysql数据库开发 编程环境:pycharm,mysql8.0 系统分为前台+后台模式开发 网站前台: 用户注册, 登录 搜索音乐,音乐欣赏(可以在线进行播放) 用户登陆时选择相关感兴趣的音乐风格 音乐收藏 音乐推荐算法:(重点) 本课题需要大量用户行为(如播放记录、收藏列表)、音乐特征(如音频特征、歌曲元数据)等数据 (1)根据用户之间相似性或关联性,给一个用户推荐与其相似或有关联的其他用户所感兴趣的音乐; (2)根据音乐之间的相似性或关联性,给一个用户推荐与其感兴趣的音乐相似或有关联的其他音乐。 基于用户的推荐和基于物品的推荐 其中基于用户的推荐是基于用户的相似度找出相似相似用户,然后向目标用户推荐其相似用户喜欢的东西(和你类似的人也喜欢**东西); 而基于物品的推荐是基于物品的相似度找出相似的物品做推荐(喜欢该音乐的人还喜欢了**音乐); 管理员 管理员信息管理 注册用户管理,审核 音乐爬虫(爬虫方式爬取网站音乐数据) 音乐信息管理(上传歌曲MP3,以便前台播放) 音乐收藏管理 用户 用户资料修改 我的音乐收藏 完整前后端源码,部署后可正常运行! 环境说明 开发语言:python后端 python版本:3.7 数据库:mysql 5.7+ 数据库工具:Navicat11+ 开发软件:pycharm
MPU6050是一款广泛应用在无人机、机器人和运动设备中的六轴姿态传感器,它集成了三轴陀螺仪和三轴加速度计。这款传感器能够实时监测并提供设备的角速度和线性加速度数据,对于理解物体的动态运动状态至关重要。在Arduino平台上,通过特定的库文件可以方便地与MPU6050进行通信,获取并解析传感器数据。 `MPU6050.cpp`和`MPU6050.h`是Arduino库的关键组成部分。`MPU6050.h`是头文件,包含了定义传感器接口和函数声明。它定义了类`MPU6050`,该类包含了初始化传感器、读取数据等方法。例如,`begin()`函数用于设置传感器的工作模式和I2C地址,`getAcceleration()`和`getGyroscope()`则分别用于获取加速度和角速度数据。 在Arduino项目中,首先需要包含`MPU6050.h`头文件,然后创建`MPU6050`对象,并调用`begin()`函数初始化传感器。之后,可以通过循环调用`getAcceleration()`和`getGyroscope()`来不断更新传感器读数。为了处理这些原始数据,通常还需要进行校准和滤波,以消除噪声和漂移。 I2C通信协议是MPU6050与Arduino交互的基础,它是一种低引脚数的串行通信协议,允许多个设备共享一对数据线。Arduino板上的Wire库提供了I2C通信的底层支持,使得用户无需深入了解通信细节,就能方便地与MPU6050交互。 MPU6050传感器的数据包括加速度(X、Y、Z轴)和角速度(同样为X、Y、Z轴)。加速度数据可以用来计算物体的静态位置和动态运动,而角速度数据则能反映物体转动的速度。结合这两个数据,可以进一步计算出物体的姿态(如角度和角速度变化)。 在嵌入式开发领域,特别是使用STM32微控制器时,也可以找到类似的库来驱动MPU6050。STM32通常具有更强大的处理能力和更多的GPIO口,可以实现更复杂的控制算法。然而,基本的传感器操作流程和数据处理原理与Arduino平台相似。 在实际应用中,除了基本的传感器读取,还可能涉及到温度补偿、低功耗模式设置、DMP(数字运动处理器)功能的利用等高级特性。DMP可以帮助处理传感器数据,实现更高级的运动估计,减轻主控制器的计算负担。 MPU6050是一个强大的六轴传感器,广泛应用于各种需要实时运动追踪的项目中。通过 Arduino 或 STM32 的库文件,开发者可以轻松地与传感器交互,获取并处理数据,实现各种创新应用。博客和其他开源资源是学习和解决问题的重要途径,通过这些资源,开发者可以获得关于MPU6050的详细信息和实践指南
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值