西邮Linux兴趣小组2021~2023年纳新题
2021年纳新题
1 sizeof与strlen的区别
#include<stdio.h>
#include<string.h>
int main()
{
char s[]="i love linux\0\0\0";
int a=sizeof(s);
int b=strlen(s);
printf("%d %d",a,b);
return 0;
}
运行结果为16和12
原因为strlen遇到’\0’结束,sizeof直接计算长度(加上隐藏的’\0’);
在代码中直接为s[]以字符串的形式赋值时会在最后添加一个隐式的’\0’;
所以sizeof(s)=12(正文内容)+3(3个’\0’)+1(自带的’\0’)=16,而strlen(s)只有正文(不包含手动写入的’\0’)的12;
2箱子大小和装入物品的顺序
#include<stdio.h>
struct test1{
int a;
short b;
double c;
}t1;
struct test2{
short b;
int a;
double c;
}t2;
int main()
{
printf("%zu",sizeof(t1));
printf("%zu",sizeof(t2));
return 0;
}
这道题显然是考察结构体内存对齐的知识
- 第一个结构体成员在结构体偏移量为零的地址
- 其他成员变量要对齐至对齐数整数倍的地址处
- 对齐数为编译器默认的一个对齐数与该成员大小之间的较小值
结构体总大小为最大对齐数的整数倍
由此观之
t1里int a占4个字节即0~ 3位置,short b占两个字节即4 ~ 5位置,又因为其他成员变量需要对齐至对齐数整数倍的地址处即空置6 ~ 7,第三个成员变量double c从第八位开始占8 ~ 15,总计占0 ~ 15共16个字节;
t2里short a占0 ~ 1,空置2 ~ 3,int b占4 ~ 7,double c占8 ~ 15,总计0 ~ 15共16个字节;
3.编写一个func函数用来输出二维数组arr的每个元素的值
#include<stdio.h>
void func(int arr[10][13]);
int main()
{
int arr[10][13]={
0};
for(int i=0;i<10;i++){
for(int j=0;j<13;j++){
arr[i][j]=rand();
}
}
func(arr);
return 0;
}
void func(int arr[10][13])
{
for(int i=0;i<10;i++){
for(int j=0;j<13;j++){
printf("%d\t",arr[i][j]);
}
printf("\n");
}
}
直接在函数中限制arr数组的大小进行输出;
也可以只标明列不标明行传递数组,在循环过程中限制行数目
void func(int arr[][13])
{
for(int i=0;i<10;i++){
for(int j=0;j<13;j++){
printf("%d\t",arr[i][j]);
}
printf("\n");
}
}
4.就不能换个变量名吗
浅谈你对传值和传址的区别
浅谈你对c语言中变量的生命周期的认识
#include<stdio.h>
int ver = 123;//全局变量,只有在文件结束时才会被释放
void func1(int ver)
{
ver++;//对传进来的值进行自增
printf("ver = %d\n", ver);
}
void func2(int *pr)
{
*pr = 1234;//对传进来的ver的地址解引用之后直接修改该地址上的值为1234
printf("*pr = %d\n", *pr);
pr = 5678;//显然对一个int类型的指针进行如此操作是不合适的
printf("ver = %d\n", ver);//此时输出的是全局变量ver,因为这个函数中传入的是一个名为ptr的int类型的指针
}
int main()
{
int a = 0;//此处定义的a和ver均为局部变量,仅在主函数中有效
int ver = 1025;
for (int a = 3; a < 4; a++) {
//在for循环中再次定义一个a
static int a = 5;//定义静态局部变量a,在下一次循环调用时迭代使用
printf("a = %d\n", a);//5
a = ver;
func1(ver);//调用func1传输ver的值,此处的值是主函数中定义的ver的值,即为1025
int ver = 7;
printf("ver = %d\n", ver);//7
func2(&ver);//调用func2传输ver的地址
}//退出循环之后循环中定义的变量被释放
printf("a = %d\tver = %d\n", a, ver);
return 0;
}
-
传值和传址的区别
传值直接将具体的传出去的变量的值发送过去,并保留原来的值(好比创建了一个副本,传入某个函数之后,即使如何改变副本,对原始数据并无影响)
传址则是传递该变量的存放地址对该变量的地址里存放的内容进行操作
-
变量的生命周期
主要分为全局变量,局部变量以及静态变量
- 全局变量:定义在所有函数之外的变量,作用域为整个程序
- 局部变量:定义在函数内部的变量,作用域仅限于该函数体内部,离开函数体便会释放
- 静态变量:变量在运行区间结束后不释放内存,地址不变
5.套娃真好玩
#include<stdio.h>
unsigned sum(unsigned n){
return n ? sum(n-1)+n : 0;
}
int main()
{
printf("%u\n",sum(100));
return 0;
}
先来看看所谓的三目运算符
表达式1?表达式2:表达式3;
若表达式1为真,则执行表达式二,反之执行表达式3
unsigned sum(unsigned n){
return n ? sum(n-1)+n : 0;
}
这段代码将从main函数中传入的100进行递归求和
第一次进入该函数时,n=100,进行上文提到的问号为主导的三目运算,n为非零数,执行表达式2,第二次调用,n=99,不断进行调用直至n=0,当n=0时执行表达式3,向上一级递归返回0,上一级递归则向再上一级返回0+1=1,再返回0+1+2=3,直至返回到n=100时,总表达式为0+1+2+3…+100=5050
6.算不对的算术
#include<stdio.h>
void func(void) {
short a = -2;
unsigned int b = 1;
b += a;
int c = -1;
unsigned short d = c * 256;
c <<= 4;
int e = 2;
e = ~e | 6;
d = (d & 0xff) + 0x2022;
printf("a=0x%hx\tb=0x%x\td=0x%hx\te=0x%x\n", a, b, d, e);
printf("c=0x%hhx\t\n", (signed char)c);
}
int main(){
func();
return 0;
}
- 按位与( & ):前后两数同一位均为1得到1
- 按位或( | ):前后两数同一位至少一个为1得到1
- 按位异或( ^ ):前后两数同一位均为1或0时取0
- 按位取反( ~ ):该数每一位1变0,0变1
short占两字节十六位
short a=-2,以补码形式储存在计算机中为1111 1111 1111 1110即为0xfffe
unsigned int b=1表示为0000 0000 0000 0000 0000 0000 0000 0001
进行b+=a之后变为1111 1111 1111 1111 1111 1111 1111 1111,又因为是无符号整型转化为16进制则为0xffffffff
int c=-1表示为1111 1111 1111 1111 1111 1111 1111 1111
左移四位变为1111 1111 1111 1111 1111 1111 1111 0000
在输出时对c进行强制转换得到1111 0000即为0xf0
unsigned short d=c * 256
在没有unsigned限定之下d=-256,原码表示为1000 0001 0000 0000,反码为1111 1110 1111 1111,补码为1111 1111 0000 0000,在unsigned情况下d=0xff00,后续对d与0xff进行按位与得到0,再加0x2022得0x2022
int e=2(0000 0000 0000 0000 0000 0000 0000 0010)并对e进行按位取反得到1111 1111 1111 1111 1111 1111 1111 1101,该数与6进行按位或操作得到1111 1111 1111 1111 1111 1111 1111 1111,即为0xffffffff
7. 指针和数组的恩怨情仇
#include<stdio.h>
int main()
{
int a[3][3]={
{
1,2,3},{
4,5,6},{
7,8,9}};
int (*b)[3]=a;
++b;
b[1][1]=10;
int *ptr=(int *)(&a+1);
printf("%d %d %d\n",a[2][1],**(a+1),*(ptr-1));
return 0;
}
首先声明并赋值一个3*3的二维数组a,b是一个指向一维数组的数组指针;
++b将b所指向的一维数组整体加1(例:原先b[0]指向a数组的第一行,进行该操作之后b[0]指向a数组的1第二行)
接着将10赋值给b[1] [1],实则是将原先a[2] [1]的8变为10;
紧接着声明一个int类型的指针并将其指向(&a+1).实则是指向整个a数组的下一个数组,从给出的程序看来则是紧接着a数组结束的下一个地址;
printf中的a[2] [1]前面解释过为10,**(a+1)则是将a数组首元素的地址+1后进行两次解引用,a+1指向了第二行的地址,第一次解引用指向第二行第一个元素的地址,第二次解引用得到储存在这个地址的具体数值为4, *ptr指向a数组的结尾的下一个地址,则ptr-1时a数组最后一个元素的地址, *(ptr-1)的值便是9;
8.移形换位之术
下面有a,b,c三个变量和几个函数
-
你能说出使用这三个变量的值或地址作为参数分别调用这五个函数,在语法上正确吗?
-
请找出下列代码中的错误
-
const int 和int const有区别吗,如果有,请说明他们的区别
-
const int *和int const *有区别吗,如果有,请说明他们的区别
#include<stdio.h> int a=1; int const b=2; const int c=3; void func0(int n) { n+=1; n=a; } void func1(int *n) { *n+=1; n=&a; } void func2(const int *n) { *n+=1; n=&a; } void func3(int *const n) { *n+=1; n=&a; } void func4(const int *const n) { *n+=1; n=&a; } int main() { int n=0; func0(n); func1(&n); func2(&n); func3(&n); func4(&n); return 0; }
- const int 与int const没有区别,二者均表达的是声