经典C/C++面试题大集合

最新C/C++经典面试题(50道附答案) 今天分享给大家的是比较基础全面的C/C++经典面试题,提供给有需要的同学们学习! 阅读详情

一、关键字  const volatile static define
        1.关键字static的作用是什么?
              这 个简单的问题很少有人能回答完全。在C语言中,关键字static有三个明显的作用: 
                1). 在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。 
                2). 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。 
                3). 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。 
                大多数应试者能正确回答第一部分,一部分能正确回答第二部分,同是很少的人能懂得第三部分。这是一个应试者的严重的缺点,因为他显然不懂得本地化数据和代码范围的好处和重要性。
         
      2.static全局变量与普通的全局变量有什么区别?static局部变量和普通局部变量有什么区别?static函数与普通函数有什么区别?

  全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。
            这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 
            而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,
            只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。
        
          从以上分析可以看出, 把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。
            把全局变量改变为静态变量后是改变了它的作用域, 限制了它的使用范围。
        
          static函数与普通函数作用域不同。仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。
            对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件
        
          static全局变量与普通的全局变量有什么区别:static全局变量只初使化一次,防止在其他文件单元中被引用;
          static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据上一次结果值;
          static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝. 
     
       3.const
            const int a; 
            int const a;    //前两个的作用是一样,a是一个常整型数。
            const int *a;   //a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以
            int * const a;  //a是一个指向整型数的常指针 (也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)
            int const * a const;  //a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)

            为何用const:
            1). 关键字const的作用,告诉别人是只读的常量。
            2). 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。 
            3). 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。
          -----------------------
           int a=248; b=4;
     int const c=21;
     const int *d=&a;
     int *const e=&b;
     int const *f const =&a;
   
     请问下列表达式哪些会被编译器禁止?为什么?
     *c=32;d=&b;*d=43;e=34;e=&a;f=0x321f;
   
     答案:
         *c 这是个什么东东,禁止
     *d 说了是const, 禁止
     e = &a 说了是const 禁止
     const *f const =&a; 禁止
 
       
  4.Volatile
         谈谈你对Volatile 的理解

                一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,
            优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子: 
            1). 并行设备的硬件寄存器(如:状态寄存器) 
            2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables) 
            3). 多线程应用中被几个任务共享的变量 
            回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所用这些都要求volatile变量。
            不懂得volatile内容将会带来灾难。 
          }
            
            假设被面试者正确地回答了这是问题(嗯,怀疑这否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。 
            1). 一个参数既可以是const还可以是volatile吗?解释为什么。 
            2). 一个指针可以是volatile 吗?解释为什么。 
            3). 下面的函数有什么错误: 

                int square(volatile int *ptr) 
                { 
                    return *ptr * *ptr; 
                } 
            
            下面是答案: 
                1). 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。 
                2). 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。 
                3). 这段代码的有个恶作剧。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码: 
                int square(volatile int *ptr) 
                { 
                    int a,b; 
                    a = *ptr; 
                    b = *ptr; 
                    return a * b; 
                } 
                由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下: 
                long square(volatile int *ptr) 
                { 
                    int a; 
                    a = *ptr; 
                    return a * a; 
                }
         
    
       5.define
           用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)

                #define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL 
                我在这想看到几件事情: 
                1). #define 语法的基本知识(例如:不能以分号结束,括号的使用,等等) 
                2). 懂得预处理器将为你计算常数表达式的值,因此,直接写出你是如何计算一年中有多少秒而不是计算出实际的值,是更清晰而没有代价的。 
                3). 意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这个常数是的长整型数。 
                4). 如果你在你的表达式中用到UL(表示无符号长整型),那么你有了一个好的起点。记住,第一印象很重要
        
                    
        6.写一个“标准”宏,这个宏输入两个参数并返回较小的一个。 
  .   #define Min(X, Y) ((X)>(Y)?(Y):(X))                 //宏中小心地把参数用括号括起来
         
            
          7.不用中间变量,实现两变量的交换
                #define swap(x,y) \
                                    x=x+y;\
                                    y=x-y;\
                                    x=x-y    //注意:结尾没有分号
            
                            
         8.const与#define 相比,有何优点?
                const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,
                没有类型安全检查,并且在字符替换可能会产生意料不到的错误。
        有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试                    
                
            
         9.inline 和define 对比 
         inline代码放入预编译器符号表中,高效;它是个真正的函数,调用时有严格的参数检测
         
    
二、指针和数组
 
      1.指针数组的定义  
         用变量a给出下面的定义 

          a) 一个整型数(An integer) 
          b) 一个指向整型数的指针(A pointer to an integer) 
          c) 一个指向指针的的指针,它指向的指针是指向一个整型数(A pointer to a pointer to an integer) 
          d) 一个有10个整型数的数组(An array of 10 integers) 
          e) 一个有10个指针的数组,该指针是指向一个整型数的(An array of 10 pointers to integers) 
          f) 一个指向有10个整型数数组的指针(A pointer to an array of 10 integers) 
          g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument and returns an integer) 
          h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数( An array of ten pointers to functions that take an integer argument and return an integer )
          
           
          答案是: 
          a) int a; // An integer 
          b) int *a; // A pointer to an integer 
          c) int **a; // A pointer to a pointer to an integer 
          d) int a[10]; // An array of 10 integers 
          e) int *a[10]; // An array of 10 pointers to integers 
          f) int (*a)[10]; // A pointer to an array of 10 integers 
          g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer 
          h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer 
     
       
       2.引用与指针有什么区别?
        1) 引用必须被初始化,指针不必。
        2) 引用初始化以后不能被改变,指针可以改变所指的对象。
        3) 不存在指向空值的引用,但是存在指向空值的指针。
    
       
       3.char * const p;  char onst * p   const char *p   三个有什么区别?
         char * const p; //常量指针,p的值不可以修改
         char const * p; //指向常量的指针,指向的常量值不可以改
         const char *p; //和char const *p
      

       4.要对绝对地址0x100000赋值,我们可以用(unsigned int*)0x100000 = 1234;  那么要是想让程序跳转到绝对地址是0x100000去执行,应该怎么做?
            
     答案:*((void (*)( ))0x100000 ) ( );
     首先要将0x100000强制转换成函数指针,即:
     (void (*)())0x100000
     然后再调用它:
     *((void (*)())0x100000)();
     用typedef可以看得更直观些:
     typedef void(*)() voidFuncPtr;
     *((voidFuncPtr)0x100000)();  
   
       
      5.指针数组的大小
          char str1[] = "abc";
         char str2[] = "abc";
       
         const char str3[] = "abc";
         const char str4[] = "abc";
       
         const char *str5 = "abc";
         const char *str6 = "abc";
       
         char *str7 = "abc";
         char *str8 = "abc";
       
         cout << ( str1 == str2 ) << endl;
         cout << ( str3 == str4 ) << endl;
         cout << ( str5 == str6 ) << endl;
       
         cout << ( str7 == str8 ) << endl;
       
          打印结果是什么?
           
           
        解答:
            结果是:0 0 1 1 str1,str2,str3,str4是数组变量,它们有各自的内存空间;而str5,str6,str7,str8是指针,它们指向相同的常量区域
         
          6. 以下代码中的两个sizeof用法有问题吗?
           
             void UpperCase( char str[] ) // 将 str 中的小写字母转换成大写字母
             {
                 for( size_t i=0; i<sizeof(str)/sizeof(str[0]); ++i )
                    if( 'a'<=str[i] && str[i]<='z' )
                      str[i] -= ('a'-'A' );
             }
           
             char str[] = "aBcDe";
             cout << "str字符长度为: " << sizeof(str)/sizeof(str[0]) << endl;
             UpperCase( str );
             cout << str << endl;
           
          解答:
               函数内的sizeof有问题。
               根据语法,sizeof如用于数组,只能测出静态数组的大小,无法检测动态分配的或外部数组大小。
               函数外的str是一个静态定义的数组,因此其大小为6,
               函数内的str实际只是一个指向字符串的指针,没有任何额外的与数组相关的信息,因此sizeof作用于上只将其当指针看,一个指针为4个字节,因此返回4。    
     
         
          int main()
          {
              char a[10];
              char b[] = "hello";
              printf("%d\n", sizeof(a));
              printf("%d\n", strlen(a));
              

              printf("%d\n", sizeof(b));
              printf("%d\n", strlen(b));  
          }
          
          解答:
            10  10   6    5
            //strlen 遇\0 结束, \0不计入长度  所以在为char型的时候还是不一样的
       

              
   7.从存储角度看,指针数组
          
          .  main()
           {
            int a[5]={1,2,3,4,5};
            int *ptr=(int *)(&a+1);
            printf("%d,%d",*(a+1),*(ptr-1));
           }
         
            输出结果是什么?
            
           解答
              输出:2,5
         
           *(a+1)就是a[1],*(ptr-1)就是a[4],执行结果是2,5
           &a+1不是首地址+1,系统会认为加一个a数组的偏移,是偏移了一个数组的大小(本例是5个int)
           int *ptr=(int *)(&a+1);
           则ptr实际是&(a[5]),也就是a+5
           
           原因如下:
         
           &a是数组指针,其类型为 int (*)[5];
           而指针加1要根据指针类型加上一定的值,不同类型的指针+1之后增加的大小不同。
           a是长度为5的int数组指针,所以要加 5*sizeof(int)
           所以ptr实际是a[5]
           但是prt与(&a+1)类型是不一样的(这点很重要)
           所以prt-1只会减去sizeof(int*)
         
           a,&a的地址是一样的,但意思不一样
             a是数组首地址,也就是a[0]的地址,&a是对象(数组)首地址,
             a+1是数组下一元素的地址,即a[1],&a+1是下一个对象的地址,即a[5].
             
   
                int main()    
                {    
                   int a[5] = { 1, 2, 3, 4, 5 };  
                   int *p1  = (int*)(&a + 1);    
                   int *p2  = (int *)((int)a + 1);    
                 
                   printf("0x%x, 0x%x\n", p1[-1], *p2);  
                 
                   return 0;    
                }          
            
             变量a的内存地址为:0x0034fc50  问printf输出值是多少
             
             解答 
                 首先分析p1
                  
                 &a为数组指针,类型是"int (*)[5]",数组指针是指向数组首元素的地址的指针
                  
                 &a的内存地址为:0x0034fc50
                  
                 &a + 1指针加一,向后移动一个元素,这里的元素类型是"int (*)[5]",其空间大小为20字节,因此
                  
                 &a + 1的内存地址为:0x0034fc64(0x0034fc50 + 0x14)
                  
                 p1的内存地址为:0x0034fc64
                  
                 p1[-1]数组下标为负数,向前移动一个元素(注:数组越界在编译时与运行时都不会报错,除非操作非法内存)
                  
                 p1[-1]元素所在的地址为:0x0034fc60
                  
                 因此,可知p1[-1]的地址为a + 4的地址
                     
                 p1[-1] == *(a + 4) == 5
                   
                 继续分析p2
                  
                 (int)a的值为0x0034fc50
                  
                 (int)a + 1等于0x0034fc51
                  
                 分析内存结构,测试环境字节序为little endian
                  
                 
                 01 00 00 00 02 00 00 00 03 00 0...
                  
                 *p2等于0x2000000
            
      
 8.函数调用中传递指针 
      #include <stdio.h>
     #include <stdlib.h>
   
     void getmemory(char *p)
     { 
      p=(char *) malloc(100);
      strcpy(p,"hello world");
     } 
   
     int main( )
     {
      char *str=NULL;
      getmemory(str);
      printf("%s/n",str);
      free(str);
      return 0;
     }
   
    解答
           程序崩溃,getmemory中的malloc 不能返回动态内存, free()对str操作很危险  
           getmemory中p是形参,是一个指针变量,getmemory(str)调用后,传入的是指针变量保存的对象地址,
           p=(char *) malloc(100)实际上是把申请的动态内存空间的首地址付给p指向的地址(即str指向的地址null),这个是错误的。
           应该修改成指向指针的指针 void getmemory(char **p),
           这样malloc返回的地址付给*p(即str变量本身)。
      

       
                      
         void GetMemory(char **p,int num)
         {                          //p,指向指针的指针,*p,p指向的指针(即str),**p,最终的对象,str指向的单元
          *p=(char *)malloc(num); //申请空间首地址付给传入的被p指向的指针,即str
         }  
         
         int main()
         {
            char *str=NULL;
          GetMemory(&str,100);   //传入指针变量本身的地址
          strcpy(str,"hello");
          free(str);
       
          if(str!=NULL)
          {
              strcpy(str,"world");
          }   
              
          printf("\n str is %s",str);  
         
         } .
       
         问输出结果是什么?
       
        解答
             答案:输出str is world。
           
             free 只是释放的str指向的内存空间,它本身的值还是存在的.所以free之后,有一个好的习惯就是将str=NULL.
           此时str指向空间的内存已被回收,如果输出语句之前还存在分配空间的操作的话,这段存储空间是可能被重新分配给其他变量的,
           尽管这段程序确实是存在大大的问题(上面各位已经说得很清楚了),但是通常会打印出world来。
           这是因为,进程中的内存管理一般不是由操作系统完成的,而是由库函数自己完成的。
           
             当你malloc一块内存的时候,管理库向操作系统申请一块空间(可能会比你申请的大一些),然后在这块空间中记录一些管理信息(一般是在你申请的内存前面一点),
           并将可用内存的地址返回。但是释放内存的时候,管理库通常都不会将内存还给操作系统,因此你是可以继续访问这块地址的。
      
三、存储情况    
  1.内存分配方式以及它们的区别
            分析:
            1  从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。
            2  在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。
            3  从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。
                动态内存的生存期由程序员决定,使用非常灵活,但问题也最多。
    }       
             
   2.Heap与stack的差别。
             答:Heap是堆,stack是栈。Stack的空间由操作系统自动分配/释放,Heap上的空间手动分配/释放。Stack空间有限,Heap是很大的自由存储区
            C中的malloc函数分配的内存空间即在堆上,C++中对应的是new操作符。程序在编译期对变量和函数分配内存都在栈上进行,且程序运行过程中函数调用时参数的传递也在栈上进行
            
 3.堆栈溢出
            堆栈溢出就是不顾堆栈中分配的局部数据块大小,向该数据块写入了过多的数据,导致数据越界。结果覆盖了老的堆栈数据
            ========内存中分配
                高地址|-----------
                      | 内核空间
                      |----------   0xc0008000   内核启始地址(虚拟地址) 
                      |stack 向下分配     //如局部变量 系统自动分配释放
                      |   
                      |
                      |
                      |
                      |heap  向上分配     //如malloc  程序员自己负责分配释放
                      |.bss  未初始化的全局变量,执行文件中仅记录大小,由系统初始化   //如  int  g_val;
                      |.data 数据段(已初始化的全局变量)则为数据分配空间,数据保存在执行文件中  //如 int g_val=3;    func {static val=5;}
                      |.text 代码段       
                 低地址------------     
           
           
           
           例:
        
            #include <stdio.h> 
            int func (int a,int b ) 
            { 
              char name[6]; 
              int sum;
              printf("Please type your name: "); 
              gets(name); 
              printf("Hello, %s!", name);
              sum =  a+b;
              return sum; 
            } 

            int main ( ) 
            { 
                func(1,2);
                return 0; 
            }

            
            esp 栈顶指针 //进出栈时它会变化   
            ebp 栈底指针
            eip 指令指针 
            
            输入 Hello
            ------
            |2      //c中函数遵循是反向压栈顺序
            |1   
            |ret    //函数的返回地址   
            |EBP    //前一个堆栈帧的指针
            |-------------------------------ebp
            |name[5]
            |o   name[4]
            |l   name[3]  
            |l   name[2]
            |e   name[1]
            |H   name[0]
            |sum
            --------------------------------esp
              
            当输入 Hello AAAAAAAAAAAA 覆盖了返回地址ret,溢出
            ------
            |A       
            |A
            |A
            |A
            |o
            |l
            |l
            |e
            |H
            |sum
            ----  
            
                堆栈用于保存临时变量,传递参数,保存线程状态。
              如果堆栈溢出,可能出现打印错误信息,单步执行到了奇怪的地方。
              计算函数调用树的深度,注意调用的库使用最大堆栈数。
              观察堆栈使用情况。可以在内存中写入特定的字符,看那些被更改了
              在堆栈的溢出去,设定警戒保护内存。如果访问到警戒内存,系统将报错  
              
              防止堆栈溢出, 
                利用编译器,在EBP下加检测码,当被覆盖时报错
              
      、
    
  4.内存泄漏
            原因:
                malloc free未配对
                隐式内存泄漏。程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存,需要运行几天才导致最终耗尽系统的所有内存
            检测原理:  
                    每当成功的分配一块内存后,就把它的指针加入一个全局的list中;每当释放一块内存,再把它的指针从list中删除。
                这样,当程序结束的时候,list中剩余的指针就是指向那些没有被释放的内存
            检测工具:    linux用 valgrind检测
            
            如何避免:
            1. 编程阶段
                ×用malloc或new申请内存之后,立即检查指针值是否为NULL(防止使用指针值为NULL的内存)
                
                ×动态内存的申请与释放是否配对(防止内存泄漏)
                
                ×是否出现野指针,例如用free或delete释放了内存之后,忘记将指针设置为NULL
                
            2. 检测工具
                静态 -- BugDetective
                动态 -- valgrind   (可从源码中提取部分精华加入我们程序中,增加程序自检能力)
            3. 编写用例
                编写脚本,使最可能造成泄漏的模块运行到极限情况,把内存使用情况存入文件。最后看文件情况  

 
    
    5.越界
            找错题 
    
            试题1:  
            void test1()
            {
             char string[10];
             char* str1 = "0123456789";
             strcpy( string, str1 );
            }              
            试题2: 
            void test2()
            {
             char string[10], str1[10];
             int i;
             for(i=0; i<10; i++)
             {
              str1[i] = 'a';
             }
             strcpy( string, str1 );
            } 
            
            试题3: 
            void test3(char* str1)
            {
             char string[10];
             if( strlen( str1 ) <= 10 )
             {
              strcpy( string, str1 );
             }
            } 

 
      解答: 
       试题1字符串str1需要11个字节才能存放下(包括末尾的’\0’),而string只有10个字节的空间,strcpy会导致数组越界; 
       对试题2,如果面试者指出字符数组str1不能在数组内结束可以给3分;如果面试者指出strcpy(string, str1)调用使得
                从str1内存起复制到string内存起所复制的字节数具有不确定性可以给7分,在此基础上指出库函数strcpy工作方式的给10 分; 
       对试题3,if(strlen(str1) <= 10)应改为if(strlen(str1) < 10),因为strlen的结果未统计’\0’所占用的1个字节。            
      
         
       int main()
     {
      char a;
      char *str=&a;
      strcpy(str,"hello");
      printf(str);
      return 0;
     }
   
     解答
           没有为str分配内存空间,将会发生异常。问题出在将一个字符串复制进一个字符变量指针所指地址。虽然可以正确输出结果,但因为越界进行内在读写而导致程序崩溃。
           #include <stdio.h>
           #include <stdlib.h>
           #include <string.h>
           
           int main()
           {
               char a;
               char *str = &a;
               str = malloc(6*sizeof(int));
               strcpy(str, "hello");
               printf(str);
               return 0;
           }            
             }
      
        
  6.大小端
          {//请写一个C函数,若处理器是Big_endian的,则返回0;若是Little_endian的,则返回1
                int checkCPU()
                {
                 {
                  union w
                  { 
                   int a;
                   char b;
                  } c;
                  c.a = 1;
                  return (c.b == 1);
                 }
                } 
                
                剖析:
                
                   嵌入式系统开发者应该对Little-endian和Big-endian模式非常了解。
                采用Little-endian模式的CPU对操作数的存放方 式是从低字节到高字节,而Big-endian模式对操作数的存放方式是从高字节到低字节。
                例如,16bit宽的数0x1234在Little- endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:
                
                内存地址 存放内容 
                0x4000 0x34 
                0x4001 0x12 
                
                  而在Big-endian模式CPU内存中的存放方式则为:
                
                内存地址 存放内容 
                0x4000 0x12 
                0x4001 0x34 
                
                  32bit宽的数0x12345678在Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:
                
                内存地址 存放内容 
                0x4000 0x78 
                0x4001 0x56 
                0x4002 0x34 
                0x4003 0x12 
                
                  而在Big-endian模式CPU内存中的存放方式则为:
                
                内存地址 存放内容 
                0x4000 0x12 
                0x4001 0x34 
                0x4002 0x56 
                0x4003 0x78 
                
                  联合体union的存放顺序是所有成员都从低地址开始存放,面试者的解答利用该特性,轻松地获得了CPU对内存采用Little-endian还是Big-endian模式读写
          }        
    } 
    
    {//---字节对齐
            struct s1
      {
       int i: 8;
       int j: 4;
       int a: 3;
       double b;
      };
      struct s2
      {
       int i: 8;
       int j: 4;
       double b;
       int a:3;
      };
    
      printf("sizeof(s1)= %d\n", sizeof(s1));
      printf("sizeof(s2)= %d\n", sizeof(s2));
        解答
          16 , 24
          第一个struct s1
          {
            int i: 8;
            int j: 4;
            int a: 3;
            double b;
          };
         
          理论上是这样的,首先是i在相对0的位置,占8位一个字节,然后,j就在相对一个字节的位置,由于一个位置的字节数是4位的倍数,因此不用对齐,就放在那里了,
            然后是a,要在3位的倍数关系的位置上,因此要移一位,在15位的位置上放下,目前总共是18位,折算过来是2字节2位的样子,由于double是8字节的,
            因此要在相对0要是8个字节的位置上放下,因此从18位开始到8个字节之间的位置被忽略,直接放在8字节的位置了,因此,总共是16字节。
        
          第二个最后会对照是不是结构体内最大数据的倍数,不是的话,会补成是最大数据的倍数。

    
             typedef union
            {
                long i;
                int k[5];
                 char c;
            } DATE;  
            
            struct data
            {
              int cat;
              DATE cow;
              double dog;
            } too;
               
            DATE max; 
            
            则语句 printf("%d",sizeof(struct data)+sizeof(max));的执行结果是___,
            
            解答  
               52
               
              考点:区别struct与union.(一般假定在32位机器上)  
              答:DATE是一个union, 变量公用空间. 里面最大的变量类型是int[5], 占用20个字节. 所以它的大小是20. data是一个struct,
               每个变量分开占用空间. 依次为int4 + DATE20 + double8 = 32. 所以结果是 20 + 32 = 52. 当然...在某些16位编辑器下, 
               int可能是2字节,那么结果是 int2 + DATE10 + double8 = 20
  
       
    7.算法
      strcpy    编写一个标准strcpy函数的总分值为10 
            void strcpy( char *strDest, char *strSrc )      //2分 
            {
              while( (*strDest++ = * strSrc++) != ‘\0’ );
            } 
            
            void strcpy( char *strDest, const char *strSrc ) //将源字符串加const,表明其为输入参数,加2分 
            {
              while( (*strDest++ = * strSrc++) != ‘\0’ );
            } 
                          
            void strcpy(char *strDest, const char *strSrc)  //对源地址和目的地址加非0断言,加3分 
            { 
             assert( (strDest != NULL) && (strSrc != NULL) );
             while( (*strDest++ = * strSrc++) != ‘\0’ );
            } 
             
            char * strcpy( char *strDest, const char *strSrc )  ////为了实现链式操作,将目的地址返回,加3分!
            {
             assert( (strDest != NULL) && (strSrc != NULL) );
             char *address = strDest;
             while( (*strDest++ = * strSrc++) != ‘\0’ );
              return address;
            } 

     
      从2分到10分的几个答案我们可以清楚的看到,小小的strcpy竟然暗藏着这么多玄机,真不是盖的!需要多么扎实的基本功才能写一个完美的strcpy啊!             
      }    
        
      memcpy   写一个函数,功能:完成内存之间的拷贝
      void memcpy(void* pvTo, void* pvFrom, size_t size)
      {
        void* pbTo = (byte*)pvTo;
        void* pbFrom = (byte*)pvFrom;
        ASSERT(pvTo != NULL && pvFrom != NULL); //检查输入指针的有效性
        ASSERT(pbTo>=pbFrom+size || pbFrom>=pbTo+size);//检查两个指针指向的内存是否重叠
        while(size-->0)
        *pbTo++ == *pbFrom++;
        return(pvTo);
      }
     

       strcat 
            char *  strcat (char * dst, const char * src)
            {
                char * cp = dst;
                while( *cp )
                  cp++; /* find end of dst */
                while( *cp++ = *src++ ) ; /* Copy src to end of dst */
                return( dst ); /* return dst */
            }    
        }
        
        {//实现字符串倒序显示
            int main()
      {
       char* src = "hello,world";
       int len = strlen(src);
       char* dest = (char*)malloc(len+1);//要为\0分配一个空间
       char* d = dest;
       char* s = &src[len-1];//指向最后一个字符
       while( len-- != 0 )
       *d++=*s--;
       *d = 0;//尾部要加\0
       printf("%s\n",dest);
       free(dest);// 使用完,应当释放空间,以免造成内存汇泄露
       return 0;
      }
            
         

C语言【23道】经典面试题【下】 必知必会的面试经典题 阅读详情

相关推荐

精选 30 个 C++ 面试题(含解析)

点击上方“C语言与CPP编程”,选择“关注/置顶/星标公众号”干货福利,第一时间送达!大家好,我是唐唐。今天给大家分享一些经典的C++面试题。1.new、delete、malloc、fre...

编程与实战的博客 1万+

史上最全C/C++面试、C++面经八股文,一文带你彻底搞懂C/C++面试、C++面经!

目录1.讲一讲封装、继承、多态是什么?2.多态的实现原理(实现方式)是什么?以及多态的优点(特点)?3.final关键字的作用是什么?4.虚函数是怎么实现的?它存放在哪里在内存的哪个区?什么时候生成的5.智能指针的本质是什么,它们的实现原理是什么?6.匿名函数的本质是什么?他的优点是什么?7.右值引用是什么,为什么要引入右值引用?8.左值引用和指针的区别?9.指针是什么?10.weak_ptr真的不计数?是否有计数方式,在哪分配的空间。11.malloc的内存分配的方式,有什么缺点?

songbijian的博客 27万+

C++经典面试题100例及答案

答:把一些具有共性的对象归类后形成一个集合,也就是所谓的类。答:这样可以提高编译效率,因为分开的话只需要编译一次生成对应的.obj文件后,再次应用该类的地方,这个类就不会被再次编译,从而大大的提高了编译效率。答:这种函数会自动为内联函数,这种函数在函数调用的地方在编译阶段都会进行代码替换。答:常对象是指在任何场合都不能对其成员的值进行修改的对象。答:所谓的回调函数,就是预先在系统的对函数进行注册,让系统知道这个函数的存在,以后,当某个事件发生时,再调用这个函数对事件进行响应。

usstmiracle的博客 3万+

50个C/C++面试题

面试题1:变量的声明和定义有什么区别  为变量分配地址和存储空间的称为定义,不分配地址的称为声明。一个变量可以在多个地方声明,但是只在一个地方定义。加入extern修饰的是变量的声明,说明此变量将在文件以外或在文件后面部分定义。  说明:很多时候一个变量,只是声明不分配内存空间,直到具体使用时才初始化,分配内存空间,如外部变量。  面试题2:写出bool 、int、 float、

沙漠中的一粒沙 1万+

手把手教你用DCA1000EVM解析IWR6843毫米波雷达数据(附MATLAB代码)

本文提供了一份详尽的实战指南,手把手教你使用DCA1000EVM采集并解析TI毫米波雷达IWR6843的原始数据。内容涵盖硬件连接配置、mmWave Studio参数设置、LVDS数据流结构解析,并附有核心MATLAB代码,详细演示了从原始二进制文件到距离-速度谱的信号处理全链路,帮助开发者快速掌握数据解析与处理的关键步骤。

terraform7cloud的博客 227

【无标题】

可以看到这是一个带过滤的rce,并且还需要绕过空格,通过·查询资料得到了${IFS},$IFS$9,<,$IFS。然后f,l,a,g都被过滤了,可以用中括号[]来进行正则匹配,把GET传的参数改为?cmd=uniq /[e-h][k-n][^-b][e-h],就得到了flag。cmd={%0A"cmd":"/bin/cat /home/rceservice/flag"%0A}可以看到l被过滤了ls用不了了可以用dir用GET传参cmd=dir /,POST传参shell=system。

2501_94507425的博客 303

常见嵌入式/C/C++面试题100+集合(含参考答案)-更新中

为变量分配地址和存储空间的称为定义,不分配地址的称为声明。一个变量可以在多个地方声明,但是只在一个地方定义。加入extern修饰的是变量的声明,说明此变量将在文件以外或在文件后面部分定义。说明:很多时候一个变量,只是声明不分配内存空间,直到具体使用时才初始化,分配内存空间,如外部变量。左右子树都是平衡二叉树 且左右子树的深度差值的绝对值不大于 1。可以,在不同的C文件中以static形式来声明同名全局变量。可以在不同的C文件中声明同名的全局变量,前提是其中只能有一个C。

虫无涯的博客 2万+

Codex 命令行高效操作指南

本文详细介绍了Codex命令行工具的高效使用方法,包括安装配置、核心参数解析和实战技巧。通过`codex complete`、`codex explain`等指令,开发者可以快速生成代码、调试错误和重构项目,显著提升开发效率。特别推荐使用`--temperature`和`--max-tokens`参数优化生成结果。

weixin_29201313的博客 530

C/C++经典面试题

1.下面大家看一个小程序 看看输出什么 #include   int main() {    int a[5][5];    int (*p)[4];     p= a[0];    printf("%d\n", &p[3][3] - &a[3][3]);    return 0; } 析: 这个问题我来说说 首先这个问题考大家指针运算 所以大家要知道下

seekcreation的专栏 2572

解决vscode无法运行npm命令的问题(cmd可行)

Powershell以及vscode中npm命令使用不了

无痕坚守本心的博客 2750

从仿真到真机:如何将XTDrone的VINS-Fusion+RTABMap建图方案部署到真实无人机上?

本文详细介绍了如何将XTDrone的VINS-Fusion+RTABMap建图方案从仿真环境迁移到真实无人机上。内容涵盖硬件选型、传感器配置、VINS-Fusion调优、RTABMap性能优化以及系统集成等关键步骤,特别针对三维稠密重建在真实环境中的挑战提供了实用解决方案。

weixin_29056781的博客 242

【c++】经典面试题吐血整理

c++ 程序从源程序到生成可执行文件的过程 源程序 编译预处理:处理宏定义,头文件,特殊符号之类的 编译:处理关键字,确认所有指令是否符合语法 优化:删除公共表达式,循环优化之类的,优化之后得到汇编程序 汇编:将汇编程序翻译成目标机器指令 链接:将多个目标文件及所需要的库连接成最终的可执行目标文件 静态链接: 时机:在形成可执行文件之前链接 过程:将函数对应的目标文件要一份副本,将所有的程序模块连接成一个单独的可执行文件 优点:执行速度快 缺点: 浪费空间(同一个目标文件在内存中可能存在多个副本) 更新困难

vector的博客 2万+

加速度计解算姿态角

姿态角(欧拉角) 欧拉角主要用于描述刚体在固定三维参考坐标系下由于绕三轴任意旋转而形成的姿态角,具体描述参看欧拉角说明 图中xyz为固定参考系坐标,XYZ为刚体自有坐标系 (假定右手定则,即大拇指指向某一轴正方向,握拳是其余手指的转动方向为姿态角变化的正方向),则定义 绕固定坐标系的z轴旋转:航向角yaw, 转动 y 角度 绕固定坐标系的y轴旋转:俯仰角pitch,转动p 角度 绕固定坐标系的x轴旋转:横滚角roll, 转动r 角度 可以看出欧拉角或者姿态角是相对各个轴的角度偏移,再假定旋转的顺规为Z

卢平光的博客 1万+

C/C++经典编程源码大全(源码+解析),新手必备!

全国最大的C/C++面试题库,网聚了全球500强IT公司C/C++面试题http://www.cssdn.net/forum-45-1.html搜集了从2000年至今10年内的C/C++网络编程源码http://www.cssdn.net/forum-157-1.html搜集了世界上几乎所有的C/C++数据结构和经典算法源码http://www.cssdn.net/forum-155-1.html

rian_lemon的专栏 6272

【A_Star三维路径规划】基于matlab A_Star算法结合B样条平滑三维地形路径规划(含雷达威胁)【含Matlab源码 3527期】

A_Star算法结合B样条平滑三维地形路径规划(含雷达威胁) 完整代码,包运行;可提供运行操作视频!适合小白!

订阅付费专栏Matlab(奶茶价版),可赠送奶茶价版付费专栏指定代码1份; 741

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

C/C++经典面试题总结

面试题3:sizeof和strlen的区别 sizeof和strlen有以下区别:      sizeof是一个操作符,strlen是库函数。      sizeof的参数可以是数据的类型,也可以是变量,而strlen只能以结尾为‘\0‘的字符串作参数。     编译器在编译时就计算出了sizeof的结果。而strlen函数必须在运行时才能计算出来。并且sizeof计算的是数据类型占内存的大小,而...

Demons的博客 2654

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

腾讯校招历年经典面试汇总:C++研发岗

这个系列离结束差的还特别多,会更新涵盖所有一线大厂的所有岗位,也可以关注一下。

少说,多做。 5647

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

C/C++面试

1、常见问题。

m0_70785396的博客 356

2024年最全C语言【23道】经典面试题【下】(1),2024最新C C++高频精选面试题分享

方括号括住的部份表示已排序完毕,Shaker排序使用了这个概念,如果让左边的元素也具有这样的性质,让左右两边的元素都能先排序完成,如此未排序的元素会集中在中间,由于左右两边同时排序,中间未排序的部份将会很快的减少。更高维度的可以自行依此类推,但通常更高维度的建议使用其它资料结构(例如物件包装)会比较具体,也不易搞错。在快速排序法(一)中,每次将最左边的元素设为轴,而之前曾经说过,快速排序法的加速在于轴的选择,在这个例子中,只将轴设定为中间的元素,依这个元素作基准进行比较,这可以增加快速排序法的效率。

2401_84975459的博客 883

C++经典面试题(六)

最近看一些面试题,觉得如果自己被问到了,并不能很利落的回答出来。 一是从来没有这个意识,二是没有认真的梳理下。 下面对这些题做出分析,哈!个人能力有限,其中难免有疏漏,欢迎大家补充咯! 51.在C++中有没有纯虚构造函数?  【标准答案】构造函数不能是虚的。只能有虚的析构函数。 补充一句,有 纯虚的 析构函数

my_adu的专栏 1756

学C语言和C++它有毛关系吗?

这是最近一周时间几个读者小伙伴所提的问题,我顺手截了两个图。 实不相瞒,这类问题之前也经常看到,但是我忘了截图了。 每次遇到这种问题,看起来很简单,但是打字一时半会还真说不清,想想今天周末了,写一篇文章来统一聊聊吧,如果小伙伴们有不同看法,也欢迎批评指正,评论区见。 本文在开源项目:https://github.com/hansonwang99/JavaCollection 中已收录,里面包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中... ” C语言和C++到底是

CodeSheep 1万+

C++经典面试题(最全,面中率最高)

C++经典面试题   1.new、delete、malloc、free关系 delete会调用对象的析构函数,和new对应free只会释放内存,new调用构造函数。malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同

1028

【C++】7道经典面试题带你玩转vector

7道有关vector对象集合的经典面试题目,包括:只出现一次的数字,杨辉三角,删除有序数组中的重复项,只出现一次的数字 II,只出现一次的数字 III,数组中出现次数超过一半的数字,电话号码的字母组合等题目的详解.

C/C++领域,数据结构,linux,MySQl学习者 1515

猿创征文|C++来时路 _ 重温经典之C++类和对象 | 三大特性之一 - 封装 | 腾讯面试题

💡//类的主体 };👆class:为定义类的关键字className:为类的名字类的主体:可由成员变量➕成员函数组成👉//成员函数: void Init(int inintSize = 4);{size = 0;//增容 //... a [ size ] = x;size ++;} //成员变量:【即 这个 类 的一些属性、特征、数据】 STDataType * a;int size;};...

Dream_Y.ocean的博客 1483

贡献15本经典C、C++、MFC、VC++教程,都是pdf完整版的

(1) 有“新浪微博”的可以直接拷贝资料到你自己的新浪微盘(我的新浪微盘 ); (2) 没有“新浪微博”的,也很方便,也是可以直接下载或者在线浏览,速度也很快。     "C++Primer+中文第四版(pdf高清完整版).pdf" http://t.cn/zWznXX0 "C Primer Plus(第五版)中文完整版.pdf" http://t.cn/zWznfp

budingningmeng的专栏 4251
上一篇: 移动ubuntu 16.04桌面左边的任务栏到屏幕的底部或者右方
下一篇: TCP协议 “3次握手,4次挥手” 详细解释
JIANXIN.Y
博客等级 码龄9年 273粉丝 42原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值