西邮Linux兴趣小组2021纳新试题

1.大小和长度竟然不是一个意思, sizeof()strlen()有什么异同之处? 他们对于不同参数的结果有什么不同?请试举例子说明。

int main(void) 
{  
    char s[] = "I love Linux\0\0\0";  
    int a = sizeof(s);
    int b = strlen(s);
    printf("%d %d\n", a, b);
}

输出:16 12

sizeof和strlen的异同:

不同点:相同点:
sizeof是运算符,strlen是函数两者都可以计算字符串数组中元素的个数
sizeof计算所含空间字节数(注意不要忘记'\0') !!小tips:sizeof括号里只能运算,不能计算;strlen用于计算字符长度,遇'\0'停止,且不包含'\0'
sizeof在编译时就计算出结果,strlen在运行时才会计算出结果

2.箱子的大小和装入物品的顺序有关,test1test2都含有:1个short、1个int、1个double,那么sizeof(t1)sizeof(t2)是否相等呢?这是为什么呢?

struct test1 {  
    int a;
    short b; 
    double c;
};
truct test2 {  
    short b; 
    int a; 
    double c;
};
int main(void)
{  
    struct test1 t1;
    struct test2 t2;
    printf("sizeof (t1) : %d\n", sizeof(t1)); 
    printf("sizeof(t2): %d\n", sizeof(t2));
}

输出:16 16

  • 主要考察结构体内存对齐

首先是内存对齐的三个原则:
1.结构体变量的首地址,必须是结构体变量中的“最大基本数据类型成员所占字节数”的整数倍。
2.结构体变量中的每个成员相对于结构体首地址的偏移量,都是该成员基本数据类型所占字节数的整数倍
3.结构体变量的总大小,为结构体变量中 “最大基本数据类型成员所占字节数”的整数倍

回到本题中,t1为例,int占4个字节,short占2个字节,4是2的整数倍所以不用补齐,double占8个字节,前面已经有6个字节被占了,6不是8的整数倍,所以前面要补2个字节,所以double前面是8个字节,加上double本身的8个字节,一共16个字节!16个字节刚好是t1最大数据结构类型(double)的整数倍,所以整体不用补齐,所以最后答案就是16啦!

t2同理,自己试试吧


3. 哦,又是函数> 想必在高数老师的教导下大家十分熟悉函数这个概念。那么你了解计算机程序设计中的函数吗?请编写一个func函数,用来输出二维数组arr中每个元素的值。

/*在这里补全func函数的定义*/
int main(void) 
{  
    int arr[10][13]; 
    for (int i = 0; i < 10; i++){   
        for (int j = 0; j < 13; j++) {    
            arr[i][j] = rand();   
        }  
    }  
    func(arr,10,13);
}
void func(int arr[][13],int m,int n)//没有返回值,函数类型为void
{
    int i,j;
    for(i=0;i<m;i++){
        for(j=0;j<n;j++){
            printf("%d",a[i][j]);
        }
    }
}

4.就不能换个变量名吗?请结合下面的程序,简要谈谈传值传址的区别。简要谈谈你对C语言中变量的生命周期的认识

int ver = 123;
void func1(int ver) 
{  
    ver++; 
    printf("ver = %d\n", ver);//1026
}
void func2(int *pr) 
{  
    *pr = 1234; 
    printf("*pr = %d\n", *pr); //1234
    pr = 5678;  //error,不能将int型常量赋给地址
    printf("ver = %d\n", ver);//123
}
int main() 
{  
    int a = 0;
    int ver = 1025; 
    for (int a = 3; a < 4; a++) 
    {    
        static int a = 5; 
        printf("a = %d\n", a); //5 
        a = ver; 
        func1(ver); 
        int ver = 7;  
        printf("ver = %d\n", ver); // 7
        func2(&ver); 
    } 
    printf("a = %d\tver = %d\n", a, ver);//0 1025
}
  • 传值和传址的区别:

传值:

1.不改变变量,相当于产生了一个一模一样的形参,造成压栈。浪费空间大,并减低效率。

2.传参无法改变原来变量。

传址:

1.可以改变原来变量。

2.地址不开辟新的空间,节省空间。

  • 全局变量和局部变量:

变量分为局部与全局,局部变量又可称之为内部变量。 由某对象或某个函数所创建的变量通常都是局部变量,只能被内部引用,而无法被其它对象或函数引用全局变量既可以是某对象函数创建,也可以是在本程序任何地方创建。

划重点!!

局部变量的生命周期和作用域都是在大括号——块内的,块里定义了和外面同名的变量则会掩盖外面的同名变量,并且不能在一个块内定义同名的变量


5.套娃真好玩!请说明下面的程序是如何完成求和的?

unsigned sum(unsigned n) //无符号整形
{ 
    return n ? sum(n - 1) + n : 0;
}
int main(void) 
{ 
    printf("%u\n", sum(100));
}
//求和100+99+98+...+1的表达式
表达式1?表达式2:表达式3
解释:若表达式1成立,则执行表达式2,否则执行表达式3

6.算不对的算术

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); 
    //a=0xfffe,b=0xffffffff,d=0x2022,e=0xffffffff
    printf("c=Ox%hhx\t\n", (signed char)c);
    //c=0xf0
}
1."|" 或运算符 有1为1,全0为0.
2."&" 与运算符 有0为0,全1为1.
3.“^" 异或运算符 相同为0,不同为1.
4.“<<" 左移运算符 可以理解为去掉最左边的数,在最右边补0,往左移一位
5.“>>" 右移运算符 可以理解为去掉最右边的数,在最左边补0,往右移一位
6.“~” 非运算符 相当于取反
7.“%#X” #表示完整呈现所有数值的位数,x表示16进制

7.指针和数组的恩怨情仇

int main(void)
{  
    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);  //刚开始指向第一行,+1后指向下一行
    printf("%d %d %d \n", a[2][1], **(a + 1), *(ptr - 1));//10  4  9
}

8.移形换位之术。

下面有abc三个变量和4个相似的函数。

你能说出使用这三个变量的值或地址作为参数分别调用这5个函数,在语法上是否正确吗? 请找出下面的代码中的错误。

const intint const是否有区别?如果有区别,请谈谈他们的区别。

const int *int const *`是否有区别?如果有区别,请谈谈他们的区别

int a = 1;
int const b = 2;
const int c = 3;
void funco(int n) 
{  
    n += 1; 
    n = a;
}
void func1(int *n) 
{  
    *n += 1;  
    n = &a;
}
void func2(const int *n)//此函数有错,const修饰指针,表示指针指向的值不能被改变
{  
    *n += 1;//不能被赋值
    n = &a;
}
void func3(int *const n)//有错,const修饰地址,表明指针的地址不能被改变
{  
    *n += 1; 
    n = &a;//地址不能被改变
}
void func4(const int *const n)//指针指向的值和指针的地址都不能被改变
{ 
    *n += 1; //不能被赋值
    n = &a;//地址不能被改变
}
1.const int和 int const都表示int类型的变量不能被修改
2.const int和int const都表示指针指向的值不能被修改

[const总是修饰离他后面最近的变量名或数据类型​]  


  • 听说翻转字母大小写不影响英文的阅读?

    请编写convert函数用来将作为参数的字符串中的大写字母转换为小写字母,将小写字母转换为大写字母。返回转换完成得到的新字符串。

    char *convert(const char *s);
    int main(void) 
    {  
        char *str = "XiyouLinux Group 2022"; 
        char *temp = convert(str); 
        puts(temp);
    }
    char *convert(const char *s)
    {
        int i;
        char *p=(char*)malloc(sizeof(char)*strlen(s));
        //动态分配空间
        p[21]='\0';//增加终止符
        strcat(p,s);//拼接
        for(i=0;i<21;i++){
            if(p[i]>='A'&&p[i]<='Z'){
                p[i]=p[i]+'a'-'A';
            }else if(p[i]>='a'&&p[i]<='z'){
                p[i]=p[i]-'a'+'A';
            }
        }
        free(p);//释放空间
        return p;
    }

    10.交换礼物的方式

    请判断下面的三种Swap的正误,分别分析他们的优缺点。你知道这里的do {...} while(0)的作用吗?你还有其他的方式实现Swap功能吗?

    #define Swap1(a, b, t) 
    do { 
        t = a; 
        a = b;             
        b = t;             
    } while (0)//正确
    #define Swap2(a, b) 
    do {              
        int t = a;      
        a = b;         
        b = t;          
    } while (0)//正确
    void Swap3(int a, int b) 
    {  
        int t = a; 
        a = b; 
        b = t;
    }//错误,只进行了值传递
    //形参在函数调用完之后自动释放空间,main函数里并不会发生交换
    do{
    ​
    }while()的作用是为了在宏定义中实现局部作用域
    用大括号括起来表示是一个整体,是swap里语句,不然可能发生错误
    用大括号也能实现但是在预处理被替换时可能会有格式问题,例如:
    
    #define Swap2(a, b) 
    {              
        int t = a;      
        a = b;         
        b = t;          
    } 
    int main(void)
    {
        int a,b;
        scanf("%d %d",&a,&b);
        if(a>b){
            swap(a,b);
        }
    }
    //在预处理时被替换后
    int main(void)
    {
        int a,b;
        scanf("%d %d",&a,&b);
        if(a>b){
           int t = a;      
        a = b;         
        b = t; 
        };//swap后会出现分号,编译通不过
    }


    11.据说有个东西叫参数

    你知道argcargv的含义吗?请解释下面的程序。你能在不使用argc的前提下,完成对argv的遍历吗?

    int main(int argc, char *argv[]) 
    {  
        printf("argc = %d\n", argc);
        for (int i = 0; i < argc; i++) 
            printf("%s\n", argv[i]);
    }
    1.argc和argv中的arg是参数,其中argc是int类型的整数,用于统计运行程序时送给main函数的命令行参数的个数
    2.*argv[]中为输入的字符串内容,它是一个指向字符串的指针
    3.argc中总是存在一个参数即程序全名,这也是为什么argc的值为1的原因
    4.不会出现死循环,因为当argc加到一定值后会发生溢出,会变为负数
    #include<stdio.h>
    int main(void)
    {
        int i=0;
        while(argv[i]!=NULL){
            printf("%s ",argv[i++]);
        }
        
        return 0;
        
    }

    12.人去楼空

    这段代码有是否存在错误?谈一谈静态变量与其他变量的异同。

    int *func1(void) 
    {  
        static int n = 0; //静态变量的生命周期从开始到本函数结束
        n = 1; 
        return &n;
        //函数形参在函数结束时自动释放,但如果声明为static则值一直存在空间不会被释放
    }
    int *func2(void) 
    {  
        int *p = (int *)malloc(sizeof(int)); //动态分配空间
        *p = 3;  
        return p;
    }
    int *func3(void) 
    {  
        int n = 4;  
        return &n;//不能返回局部变量地址这是很危险的
    }
    int main(void) 
    {  
        *func1() = 4;  
        *func2() = 5; 
        *func3() = 6;

    静态变量的作用:

    • 隐藏与隔离的作用

    • 保持内容的持久性

    • 默认初始化为0


    13.奇怪的输出

    int main(void)
    { 
        int data[] = {0x636c6557, 0x20656d6f, 0x78206f74,0x756f7969, 0x6e694c20, 0x67207875,0x70756f72, 0x32303220, 0x00000a31}; 
        puts((const char*)data);
    }
    ​

    输出:Welcome to xiyou linux group 2021

    详见【无标题】西邮Linux兴趣小组2022纳新试题_一定.的博客-优快云博客第12题


    14.请谈谈对从「C语言文件到可执行文件」的过程的理解

1.预处理:在此阶段,头文件的展开(#include); - 宏替换(#define); - 条件编译的处理(#if #endif等); - 去注释; 将.c文件变为预处理后的.i临时文件
2.编译: 词法分析; - 语义分析; - 语法分析; - 符号汇总; 将对每个.i文件单独转化为.s文件
3.汇编: 形成符号表; 将.s的汇编文件变为.o的目标文件
4.链接:合并段表; - 合并符号表和符号表的重定位; 把每个目标文件.o合并成为一个可执行程序(Linux默认是a.out文件)

详见谈谈C语言从源文件变为可执行文件之间发生的过程详解(C程序的编译链接运行)_呋喃吖的博客-优快云博客_如何把c语言源程序变成应用程序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值