C语言面试常见问题汇总

1.sizeof和strlen的区别

sizeof和strlen有以下区别:

sizeof是一个操作符,strlen是库函数。

sizeof的参数可以是数据的类型,也可以是变量,而strlen只能以结尾为‘\0‘的字符串作参数。

编译器在编译时就计算出了sizeof的结果。而strlen函数必须在运行时才能计算出来。

sizeof计算的是数据类型占内存的大小,而strlen计算的是字符串实际的长度。

数组做sizeof的参数不退化,传递给strlen就退化为指针了。

2.说明关键字volatile有什么含意

volatile的作用:

作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。

一个定义为volatile的变量就是说这个变量可能会被意想不到的改变,这样,编译器就不会去随便假设这个变量的值了。

精确的说,优化器在用到这个变量的值的时候,必须每次都小心的重新读取这个变量的值,而不是使用保存在寄存器里面的备份。

下面是volatile变量的几个例子:

1). 并行设备的硬件寄存器(如:状态寄存器)

2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)

3). 多线程应用中被几个任务共享的变量

3.写一个“标准”宏MIN

#define min(a,b) ( (a)<=(b)?(a):(b) )

注意:在调用时一定要注意这个宏定义的副作用,如下调用:

min(++*p,x) 展开为 ((++*p)<=(x)?(++*p):(x)。

p指针就自加了两次,违背了MIN的本意。

4.设置地址为0x67a9的整型变量的值为0xaa66

*(int *)0x67a9 = 0xaa66;

5.用变量a定义

一个整型数 int a;

一个指向整型数的指针 int *a;

一个指向指针的指针,它指向的指针是指向一个整型数 int **a;

一个有10个整型数的数组 int a[10];

一个有10指针的数组,该指针是指向一个整型数 int *a[10];

一个指向有10个整型数数组的指针 int (*a)[10];

一个指向函数的指针,该函数有一个整型数参数并返回一个整型数 int (*a)(int);

一个有10个指针的数组,该指针指向一个函数,该函数有一个整型数参数并返回一个整型 int (*a[10])(int);

6.static的用法(定义和用途)

1)用static修饰局部变量:使其变为静态存储方式(静态数据区),那么这个局部变量在函数执行完成之后不会被释放,而是继续保留在内存中。

2)用static修饰全局变量:使其只在本文件内部有效,而其他文件不可连接或引用该变量。

3)用static修饰函数:对函数的连接方式产生影响,使得函数只在本文件内部有效,对其他文件是不可见的。

这样的函数又叫作静态函数。使用静态函数的好处是,不用担心与其他文件的同名函数产生干扰,另外也是对函数本身的一种保护机制。

7.const的用法(定义和用途)

const主要用来修饰变量、函数形参和类成员函数:

1)用const修饰常量:定义时就初始化,以后不能更改。

2)用const修饰形参:func(const int a){};该形参在函数里不能改变

3)用const修饰类成员函数:该函数对成员变量只能进行只读操作,就是const类成员函数是不能修改成员变量的数值的。

被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。

const int a;

int const a;

const int *a; //等于 int const *a;

int * const a;

int const * const a; //等于 const int* const a;

前两个的作用是一样,a是一个常整型数。

第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。

第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。

最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。

8.内存四区

由C/C++编译的程序占用的内存分为以下几个部分

8.1.代码区:存放函数体内的二进制代码,有操作系统管理。

8.2.全局区:存放全局变量,静态变量以及常量。

这部分可以细分为data区和bss区

(1)data区

data区里主要存放的是已经初始化的全局变量、静态变量和常量

(2)bss区

bss区主要存放的是未初始化的全局变量、静态变量,这些未初始化的数据在程序执行前会自动被系统初始化为0或者NULL

(3)常量区

常量区是全局区中划分的一个小区域,里面存放的是常量,如const修饰的全局变量、字符串常量等

8.3.栈区:由编译器自动分配和释放,存放函数参数,局部变量等

8.4.堆区:由程序员分配释放,若程序员未释放,则程序结束之后,编译器自动释放。

注意:

const修饰的全局变量也储存在常量区;

const修饰的局部变量依然在栈上。

有的分为5个区,即代码区,全局区,常量区,栈区,堆区

#include<stdio.h>

#include<malloc.h>

int a = 0; //全局初始化区 .data段

char *p1; //全局未初始化区 .bss段

void main()

{

    int b;// 栈

    char s[] = "abc"; //s在栈上,大小4字节。 "abc\0"为s的初始值,可以不用纠结其存在哪里

    char *p2; //栈

    char *p3 = "123456"; //"123456\0"在常量区,p3在栈上。

    static int c =0; //全局(静态)初始化区 .data段

    p1 = (char *)malloc(10);

    p2 = (char *)malloc(20);

    //分配得来的10和20字节的区域就在堆区。

    strcpy(p1, "123456"); //"123456\0"放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。

}

9.大小端问题

大端:一个多字节整数,数字的高位部分存放在内存的低地址单元。低地址存高字节

小端:一个多字节整数,数字的低位部分存放在内存的低地址单元。低地址存低字节

大小端格式问题查看:

方法一:

 void checkCpuMode(void)

 {

  int i = 0x12345678;

  char *cp = (char *)&i;

  if(*cp == 0x78)

   printf("little endian");

  else

   printf("big endian\n");

 }

方法二:

 void checkCpuMode(void)

 {

  int a = 0x12345678;

  if((char)a == 0x12)

   printf("big endian\n");

  else

   printf("little endian\n");

 }

方法三:

void checkCpuMode(void)

{

  union

  {

   short s;

   char c[sizeof(short)];

  }un;

  un.s=0x0102;

  if(un.[0]==1&&un.c[1]==2)

   printf("big endian\n");

  else

   printf("little endian\n");

 }

10.静态局部变量区别

(1)

① static局部变量在编译阶段就已经分配内存空间了,也就是函数没有调用前它就已经存在了。

② 普通局部变量只有运行到定义该变量的时候才会分配内存空间。

(2)

① 当执行完定义该static局部变量的函数体后,该static变量的内存空间不会被释放,只有程序结束时static变量才会自动释放。

② 当执行完定义该普通局部变量的函数体后,该普通局部变量的内存空间就会被释放。

(3)

① 如果static局部变量不初始化,那么它默认为0。

② 如果普通局部变量不初始化,那么它的值为随机数。

(4)

① static局部变量的初始化语句只会执行一次,下次再运行该初始化语句就不会被初始化,但是它可以被多次赋值。

② 每次运行该普通局部变量的定义时该变量都会被初始化。

11.#define宏定义与typedef的区别

#define宏定义是字符替换,typedef是定义类型,是声明一种新的类型,等同自带的基本类型。

#define是宏,处理的时候位于编译前阶段,宏处理器基本上对你的C/C++程序不会有任何的感知。

typedef是编译阶段的一部分。

12.sizeof与strlen计算

(1)sizeof值计算

char str1[] = {'a', 'b', 'c', 'd', 'e'};

char str2[] = "abcde";

char *ptr = "abcde";

char book[][80]={"计算机应用基础","C语言","C++程序设计","数据结构"};

sizeof(str1)=?

sizeof(str2)=?

sizeof(ptr)=?

sizeof(book)=?

sizeof(book[0])=?

分析:

sizeof(str1)=5,就是5*sizeof(char)=5;

sizeof(str2)=6,字符串都是以’0’结尾,所以所占字节数为6;

sizeof(ptr)=4,ptr是一个指针,在32位平台上大小为4字节;

sizeof(book)=320,book是一个二维数组,4*80*1

sizeof(book[0])=80,book[0]是第一维数组,因为此80*1

根据sizeof求数组元素的个数也很简单,拿第一个来说,就是sizeof(str1)/sizeof(char)。

(2)计算strlen的值

char  arryA[] = {'a','b','c',0,'d','e'};

char  arryB[] = {'a','b','c','d','e'};

char  arryC[6] = {'a','b','c','d','e'};

char *str = "abcde";

分析:

strlen(arryA) = 3,strlen遇到’\0’就会返回,无论后面有多少个字符;

strlen(arryB)长度无法确定,没有人为写入‘\0’,strlen会继续计算直到找到结束符,结果未知;

strlen(arryC)=5,指定了数组大小,编译器会自动在空余地方添加’\0’,这其实跟char arryC[6] = {‘a’,‘b’,‘c’,‘d’,‘e’,‘\0’};等价。

strlen(str) = 5,不包括结尾的’\0’。

13.char str[]和char *str

1)下面的操作合法么?出错的话,会是在那个阶段?编译时期还是运行时期?

char str[] = "hello";

str[0] = 's';     //合法么

char *str = "hello";

str[0] = 's';      //合法么

分析:

这两个都可以成功编译,只是第二个会在运行时期出现段错误。下面来分析一下:

首先"hello"是一个字符串常量,存储在静态数据区域(data段),这是在编译时期就确定的。第一个是将字符串常量赋值给了一个变量(全局变量在数据段,局部变量在栈区),

实际上是将字符串常量拷贝到了变量内存中,因此修改的只是str[]这个变量的值。

第二个是将字符串常量的首地址赋值给指针str,对指针str操作就是对字符串常量进行修改!因此出现了段错误。

(2)理解了上面的知识,判断一下下面的true or false?

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,他们是什么?他们是数组名,也就是数组首元素的地址!”str1 == str2“本质就是比较两个数组的地址是不是相同。

上面我们说过,编译器给他们分配了新的存储空间来对字符串"abc"进行拷贝,这些变量在内存里是相互独立的,因此他们的地址肯定不同!

再理解str5,str6,str7,str8,他们是什么?他们是指针,他们的值就是字符串常量的地址!它们都指向“abc"所在的静态数据区,所以他们都相等。

(3)数组作为函数参数传递

看看下面的函数有啥问题:

int func(int a[])

{  

    int n = sizeof(a)/sizeof(int);  

    for(int i=0;i<n;i++)

{  

        printf("%d ",a[i]);  

        a[i]++;  

    }  

}  

sizeof(a)/sizeof(int)一般是计算数组的元素个数,结果却发现n的值总是1,为什么会这样呢?这是因为在C中,将数组传递给一个函数时,无法按值传递,而是会自动退化为指针。

下面的三种写法其实是等价的:

“int func(int a[20]);” 等价于 “int func(int a[]);” 等价于 “int func(int *a);”。

14.数组指针和指针数组的问题

(1)说出下面表达式的含义?

int *p1[10];

int (*p2)[10];

第一个是指针数组,首先他是一个数组,数组的元素都是指针。

第二个是数组指针,首先他是一个指针,它指向一个数组。

(2)写出下面程序运行的结果

int a[5] = { 1, 2, 3, 4, 5 };  

int *ptr = (int *)(&a + 1);  

printf("%d,%d", *(a + 1), *(ptr - 1));

分析:

答案是2,5。本题的关键是理解指针运算,”+1“就是偏移量的问题:一个类型为T的指针移动,是以sizeof(T)为单位移动的。

a+1:在数组首元素地址的基础上,偏移一个sizeof(a[0])单位。因此a+1就代表数组第1个元素,为2;

&a+1:在数组首元素的基础上,偏移一个sizeof(a)单位,&a其实就是一个数组指针,类型为int()[5]。因此&a+1实际上是偏移了5个元素的长度,也就是a+5;再看ptr是int类型,因此"ptr-1"就是减去sizeof(int*),即为a[4]=5;

a是数组首地址,也就是a[0]的地址,a+1是数组下一个元素的地址,即a[1]; &a是对象的首地址,&a+1是下一个对象的地址,即a[5]。

15.c语言char和float相互转换方法

作用:可以灵活调整float类型的字节序

union CharFloat{

float numeric;

unsigned char ascii[4];

};

float charTofloat(unsigned char *str) {

CharFloat value;

value.ascii[0] = str[0];

value.ascii[1] = str[1];

value.ascii[2] = str[2];

value.ascii[3] = str[3];

return value.numeric;

}

void floatochar(char* returnData, float numeric) {

CharFloat value;

int i = 0;

value.numeric = numeric;

returnData[i++] = value.ascii[3];

returnData[i++] = value.ascii[2];

returnData[i++] = value.ascii[1];

returnData[i++] = value.ascii[0];

for(int j=0;j<4;j++)

printf("%2x,",value.ascii[j]);

}

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AI+程序员在路上

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值