面试题笔记(一)

1.关于求质因数的程序 如20=2*2*5

2.

2

3

4

unsigned char *p1;

unsigned long *p2;

p1=(unsigned char *)0x801000;

p2=(unsigned long *)0x810000;

请问p1+5= 什么?
p2+5= 什么?

解析:p1指向字符型,一次移动一个字符型,1个字节;p1+5后移5个字节,16进制表示为5;

            p2指向长整型,一次移动一个长整型,4个字节,p2+5后移20字节,16进制表示为14。

 { char每次移动1个字节;short移动2个字节;int ,long ,float移动4个字节;double移动8个字节}

3.结构体中的字节对齐

先看个简单的例子(32位,X86处理器,GCC编译器):

    【例1】设结构体如下定义:

复制代码

 1 struct A{
 2     int    a;
 3     char   b;
 4     short  c;
 5 };
 6 struct B{
 7     char   b;
 8     int    a;
 9     short  c;
10 };

复制代码

     已知32位机器上各数据类型的长度为:char为1字节、short为2字节、int为4字节、long为4字节、float为4字节、double为8字节。那么上面两个结构体大小如何呢?

     结果是:sizeof(strcut A)值为8;sizeof(struct B)的值却是12。 

     结构体A中包含一个4字节的int数据,一个1字节char数据和一个2字节short数据;B也一样。按理说A和B大小应该都是7字节。之所以出现上述结果,就是因为编译器要对数据成员在空间上进行对齐。

3.1.2 对齐准则

     先来看四个重要的基本概念:

     1) 数据类型自身的对齐值:char型数据自身对齐值为1字节,short型数据为2字节,int/float型为4字节,double型为8字节。

     2) 结构体或类的自身对齐值:其成员中自身对齐值最大的那个值。

     3) 指定对齐值:#pragma pack (value)时的指定对齐值value。

     4) 数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中较小者,即有效对齐值=min{自身对齐值,当前指定的pack值}。

     基于上面这些值,就可以方便地讨论具体数据结构的成员和其自身的对齐方式。

     其中,有效对齐值N是最终用来决定数据存放地址方式的值。有效对齐N表示“对齐在N上”,即该数据的“存放起始地址%N=0”。而数据结构中的数据变量都是按定义的先后顺序存放。第一个数据变量的起始地址就是数据结构的起始地址。结构体的成员变量要对齐存放,结构体本身也要根据自身的有效对齐值圆整(即结构体成员变量占用总长度为结构体有效对齐值的整数倍)。

     以此分析3.1.1节中的结构体B:

     假设B从地址空间0x0000开始存放,且指定对齐值默认为4(4字节对齐)。成员变量b的自身对齐值是1,比默认指定对齐值4小,所以其有效对齐值为1,其存放地址0x0000符合0x0000%1=0。成员变量a自身对齐值为4,所以有效对齐值也为4,只能存放在起始地址为0x0004~0x0007四个连续的字节空间中,符合0x0004%4=0且紧靠第一个变量。变量c自身对齐值为 2,所以有效对齐值也是2,可存放在0x0008~0x0009两个字节空间中,符合0x0008%2=0。所以从0x0000~0x0009存放的都是B内容。

     再看数据结构B的自身对齐值为其变量中最大对齐值(这里是b)所以就是4,所以结构体的有效对齐值也是4。根据结构体圆整的要求, 0x0000~0x0009=10字节,(10+2)%4=0。所以0x0000A~0x000B也为结构体B所占用。故B从0x0000到0x000B 共有12个字节,sizeof(struct B)=12。

     之所以编译器在后面补充2个字节,是为了实现结构数组的存取效率。试想如果定义一个结构B的数组,那么第一个结构起始地址是0没有问题,但是第二个结构呢?按照数组的定义,数组中所有元素都紧挨着。如果我们不把结构体大小补充为4的整数倍,那么下一个结构的起始地址将是0x0000A,这显然不能满足结构的地址对齐。因此要把结构体补充成有效对齐大小的整数倍。其实对于char/short/int/float/double等已有类型的自身对齐值也是基于数组考虑的,只是因为这些类型的长度已知,所以他们的自身对齐值也就已知。 

     上面的概念非常便于理解,不过个人还是更喜欢下面的对齐准则。

     结构体字节对齐的细节和具体编译器实现相关,但一般而言满足三个准则:

     1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;

     2) 结构体每个成员相对结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);

     3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节{trailing padding}。

     对于以上规则的说明如下:

     第一条:编译器在给结构体开辟空间时,首先找到结构体中最宽的基本数据类型,然后寻找内存地址能被该基本数据类型所整除的位置,作为结构体的首地址。将这个最宽的基本数据类型的大小作为上面介绍的对齐模数。

     第二条:为结构体的一个成员开辟空间之前,编译器首先检查预开辟空间的首地址相对于结构体首地址的偏移是否是本成员大小的整数倍,若是,则存放本成员,反之,则在本成员和上一个成员之间填充一定的字节,以达到整数倍的要求,也就是将预开辟空间的首地址后移几个字节。

     第三条:结构体总大小是包括填充字节,最后一个成员满足上面两条以外,还必须满足第三条,否则就必须在最后填充几个字节以达到本条要求。

4.sizeof问题

  1. void Func(char str_arg[100])

  2.  {    

  3.    printf("%d\n",sizeof(str_arg)); 

  4.  }  

  5. int main(void

  6.  {     

  7.  char str[]="Hello";   

  8.  printf("%d\n",sizeof(str));

  9.   printf("%d\n",strlen(str));    

  10.  char *p = str;    

  11.  printf("%d\n",sizeof(p));  

  12. Func(str);

  13.  }  

  14. 答案:6,5,4,4

  15. 当数组当函数参数传递的时候,已经退化为指针,也就是说str_arg会是指针,所以sizeof(指针)=4.

  16. 另一个例子:

  17. #include<stdio.h>

    #include<string.h>

    void Func(char str_arg[2])  

    {  

        int m = sizeof(str_arg);     //指针的大小为4  

        int n = strlen(str_arg);     //对数组求长度,str_arg后面的那个2没有任何意义,数组已经退化为指针了  

        printf("%d\n",m);  

        printf("%d\n",n);  

    }  

    int main(void)  

    {  

        char str[]="Hello";  

        Func(str);  

    }  

  18. 可以得知,为4,5,因为str_arg已经退化为指针了,所以后面的那个2是没有意义的。

5.等概率输出

由这个for循环循环n次,且在满足条件时才输出i,可知,输出m个不同值的要求已满足,因为每次输出的都是i值,而i值每次都是不一样的,m--保证了程序在输出了m个值后就停止循环。

在i=0时,rand()%(n-i)的取值范围为0到n-1,共n个数,此时要输出0只需要rand()%(n-i)小于m,故i=0被输出的概率为m/n;

在i=1时,rand()%(n-i)的取值范围为0到n-2,共n-1个数,若i=0没有被输出,则m--未被执行,此时i=1被输出的概率为m/(n-1),若i=0已经被输出了,则m变为m-1,此时i=1被输出的概率为(m-1)/(n-1);由概率论的知识,可知此时i=1被输出的概率为

P=(1-m/n)*(m/(n-1))+m/n*((m-1)/(n-1))=m/n;以此类推,可知每个数被输出的概率都为m/n

6.

函数外全局变量,系统初始化为0,函数内局部变量,是随机值,局部变量需要初始化才能使用

7.枚举变量

在函数外部定义时为0而不是成员的值,内部调用时为随机值

8.32位机器与64位机器的区别

16位平台

char         1个字节8位

short        2个字节16位

int            2个字节16位

long         4个字节32位

指针         2个字节

32位平台

char         1个字节8位

short        2个字节16位

int            4个字节32位

long         4个字节

long long 8个字节

指针         4个字节

64位平台

char         1个字节

short        2个字节

int            4个字节

long         8个字节(区别)

long long 8个字节

指针        8个字节(区别)

9.数组名当做函数参数传递时弱化成指针,占4和字节

注意sizeof字符串时需要加上\0,所以是23

对字符串进行sizeof操作的时候,会把字符串的结束符"\0"计算进去的,进行strlen操作求字符串的长度的时候,不计算\0的。
数组作为函数参数传递的时候,已经退化为指针了,Func函数的参数str_arg只是表示一个指针,那个100不起任何作用的。

10.形参在函数结束后会被释放

答案:D

函数char *myString()中没有使用new或者malloc分配内存,所有buffer数组的内存区域在栈区

随着char *myString()的结束,栈区内存释放,字符数组也就不存在了,所以会产生野指针,输出结果未知 

11.C语言是一种结构化程序设计语言

12.

C++中的struct对C中的struct进行了扩充,它已经不再只是一个包含不同数据类型的数据结构了,它已经获取了太多的功能。
struct能包含成员函数吗? 能!
struct能继承吗? 能!!
struct能实现多态吗? 能!!!
 

既然这些它都能实现,那它和class还能有什么区别?

最本质的一个区别就是默认的访问控制: 

默认的继承访问权限

struct是public的,class是private的。

13.

一个变量的地址即为该变量的“指针”。指针变量是专门用来存放另一变量的地址(即指针)的,指针变量的值是地址(即指针)。

指针是一个地址,而指针变量是存放地址(即指针)的变量,别搞混了。

14.用户可以自己声明的类型还有 结构体类型 (structure)、共用体类型(union)、 枚举类型 (enumeration)、类类型(class )等,这些统称为用户自定义类型(user-defined type,UDT)。

15.

猜你喜欢

转载自blog.csdn.net/cb673335723/article/details/81115157
今日推荐