C语言面试常考内容

static

下面是中兴通讯2012校招笔试题的一道问答题:

  1. static全局变量与普通的全局变量有什么区别 ?
      全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。
      全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。
      这两者的区别在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。

static全局变量只初使化一次,防止在其他文件单元中被引用;  

2、static局部变量和普通局部变量有什么区别 ?

  把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。

  static局部变量只被初始化一次,下一次依据上一次结果值;  

3、 static函数与普通函数有什么区别?

   static函数与普通函数作用域不同,仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static修饰的函数),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件.

 static函数与普通函数最主要区别是static函数在内存中只有一份,普通静态函数在每个被调用中维持一份拷贝程序的局部变量存在于(堆栈)中,全局变量存在于(静态区)中,动态申请数据存在于(堆)。
 static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝
C语言中的static:
1、表示变量是静态存储变量,即变量存放在静态存储区.
2,表示该变量是内部连接,(这种情况是指该变量不在任何{}之内,就象全局变量那样,这时候加上static) ,也就是说在其它的.cpp文件中,该变量是不可见的(你不能用).

当static加在函数前面的时候
表示该函数是内部连接,只在本文件中有效,别的文件中不能用该函数.
不加static的函数默认为是全局的。
也就是说在其他的.cpp中只要声明一下这个函数,才可以使用它。
C++中static
文件内的,匿名命名空间内,定义的变量,相当于文件静态变量。作用域为文件,生命周期是程序生命期。
类内部定义的静态变量,作用域为类,生命周期是程序生命期。可以使用的区域是类。
和函数不同,函数内部定义的静态变量;不可用在其他地方,只能通过函数调用访问。类内部定义的静态变量,可以直接在类外部,通过类名引用。
关于static可参阅: https://www.cnblogs.com/felix-kun/p/5404058.html

const:具有常属性

1、const修饰成员变量
(1)关于引用时候的const
const可以修饰const变量、非const变量,而非const变量不能修饰const变量,只能修饰非const变量。

int a=10;//非const变量
const int b=20;//const变量

const int& c=a;//const修饰非const变量
const int& d=b;//const修饰const变量

int& e=a;//非const修饰非const
int& f=b;//非const不能修饰const,这是错误写法

const变量说明变量只读,而非const变量来引用就改变了它的只读属性,显然不可以。
(2)const修饰指针变量

int a=10;//非const data
const int b=a;//const变量

int * a3 = &a1;             //非const data,非const pointer
const int * a4 = &a1;       //const data,non-const pointer
int * const a5 = &a1;       //non-const data,const pointer
int const * const a6 = &a1;   //const data,const pointer
const int * const a7 = &a1;   //const data,const pointer

const修饰指针变量时:
  1)只有一个const,如果const位于*左侧,表示指针所指数据是常量,不能通过解引用修改该数据;指针本身是变量,可以指向其他的内存单元。
  2)只有一个const,如果const位于*右侧,表示指针本身是常量,不能指向其他内存地址;指针所指的数据可以通过解引用修改。
  3)两个const,*左右各一个,表示指针和指针所指数据都不能修改(3)const修饰函数参数:表示被修饰的函数参数在函数体中不能被修改。

2、const修饰函数返回值
const修饰函数返回值相对于值传递没有意义,而对于指针传递来说

const int * mallocA(){  ///const data,non-const pointer
    int *a=new int(2);
    return a;
}

int main()
{
    const int *a = mallocA();
    ///int *b = mallocA();  ///编译错误
    return 0;
}

如果返回const data,non-const pointer,返回值也必须赋给const data,non-const pointer。因为指针指向的数据是常量不能修改。
3、const修饰成员函数
(1)const修饰的成员函数不能修改任何的成员变量(mutable修饰的变量除外)
(2)const修饰的成员函数不能调用非const成员函数,因为非const成员函数可能会修改const修饰的成员函数的成员变量
const还可参阅:https://www.cnblogs.com/xudong-bupt/p/3509567.html

宏定义和宏函数

1、这是一个常见的两个数比较大小的宏函数:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>

#define Max(a,b) (((a)>(b))?(a):(b))
using namespace std;

int main()
{
    int x = 3;
    int y = 7;
    cout << "Max" << Max(x, y) << endl;

    system("pause");
    return 0;
}

2、在宏定义中
使用#表示,把一个宏参数替换成对应的字符串。
例如上式中的Max(a,b)被替换成了(((a)>(b))?(a):(b)),从而实现了两个数的比较。
使用##,起到将符号左右两部分连接的作用。

#include<iostream>
#define Test(Test1,Test2)\
do{\
    std::cout << #Test1"and " ##Test2<< std::endl;\
} while (0);
int main()
{
    Test("I am the head", "I am the tail");
    system("pause");
    return 0;
}

结果如下:

"I am the head"and I am the tail

关于宏定义中的#和##还可参阅:
http://www.cnblogs.com/welkinwalker/archive/2012/03/30/2424844.html/
3、宏的优点:

a、使用宏一般要求代码短小,宏替换发生在预处理阶段,
而函数存在栈帧压栈等操作,因此宏比函数高效(时间方面)。
b、宏是无类型的,可以适用于任意类型。

宏的缺点:

a、宏要求代码短小,否则每次调用宏都会展开,会造成空间冗余。
b、宏是没法调试的。
c、宏由于类型无关,也就不够严谨。
d、宏可能会带来运算符优先级的问题,导致程容易出现错。

这里写图片描述
4、带副作用的宏函数参数

x++;//带副作用
x+1;//不带副作用
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>

#define Max(a,b) (((a)>(b))?(a):(b))
using namespace std;

int main()
{
    int x = 3;
    int y = 7;
    int z = Max(x++, y++);
    cout << "x="<<x << ' '<<"y=" << y <<' '\
    <<"z="<< z << endl;

    system("pause");
    return 0;
}
x=4 y=9 z=8

经过宏替换——>(((x++)>(y++))?(x++):(y++))
(4>8)?显然不成立,取(y++),先将y赋值给z,然后再y=y+1
5、宏可以在命令行定义

#include <stdio.h>
int main()
{
int array [ARRAY_SIZE];
int i = 0;
for(i = 0; i< ARRAY_SIZE; i ++)
{
array[i] = i;
}
for(i = 0; i< ARRAY_SIZE; i ++)
{
printf("%d " ,array[i]);
}
printf("\n" );
return 0;
}
//编译指令:
gcc -DARRAY_SIZE=10 programe.

可以实现一个根据实际需要去设定数组容量的数组。
6条件编译

内存对齐

https://blog.csdn.net/Vickers_xiaowei/article/details/79491528

数组和指针

https://blog.csdn.net/Vickers_xiaowei/article/details/79558158

栈帧

https://blog.csdn.net/Vickers_xiaowei/article/details/79512762

函数参数传什么??

1、传值
当调用该函数时候,该函数自己形成栈帧,形参数值是拷贝过来的新变量,在函数中对形参变量操作,自然不会改变实参的值。在该函数执行结束,该函数的栈帧销毁,拷贝数据(形参)自然也就丢失了。
2、传地址
函数形成栈帧,形参就是实参地址,可以改变实参的数值。
3、传引用
引用定义出来的形参变量相当于实参变量的别名,也就是说形参等同于实参,它们虽是两个不同的变量名,指向的是同一块内存地址,对形参操作实参也会改变。

#include<iostream>

void func_val(int a)
{
    a = 1;
}
void func_addr(int*a)
{
    *a = 2;
}
void func_quote(int&a)
{
    a=3;
}
int main()
{
    int a = 0;
    std::cout << "初值:" << a << std::endl;
    func_val(a);
    std::cout << "传值:" << a << std::endl;
    func_addr(&a);
    std::cout << "传地址:" << a << std::endl;
    func_quote(a);
    std::cout << "传引用" << a << std::endl;
    system("pause");
}

这里写图片描述

猜你喜欢

转载自blog.csdn.net/Vickers_xiaowei/article/details/82623415