题目来源

公司面试题

简答题

*程序的局部变量存在于___ 中,全局变量存在于____中,动态申请数据存在于___ 中。*

程序的局部变量存在于栈(stack) 中,全局变量存在于静态数据区中,动态申请数据存在于堆(heap)中。

这一题全部是概念的问题,需要牢记!!

*PC为32位机,计算该结构体大小?*

1
2
3
4
5
6
7
struct name1
{
char str;
short x;
int num;
};
sizeof(name1)? (8字节)

这一题考察的是结构体大小的计算,需要记住几个点(来自菜鸟教程)

  • 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
  • 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字(internal adding);
  • 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)

题目中的三个变量大小分别为:1个字节、2个字节、4个字节。

char分配一个字节,short分配2个字节,由于int 为4个字节,此时前面1+2为3,不能满足int4字节的整数倍,因此对于要点2,会自动补上1字节,这样一来字节数变成4字节,加上int的4字节一共为8字节。

下面这个来测试一下

1
2
3
4
5
6
struct name2
{
char str;
int num;
short x;
}; 求sizeof(name2)? (12字节)

*static有什么用途?(请至少说明两种)*

  • 限制变量的作用域(static全局变量);
  • 设置变量的存储域(static局部变量)。

*void* p = malloc(100),请计算sizeof(p)。*

根据不同位数的有不同的答案:32位的4字节、64位的8字节。

*写一个“标准”宏,这个宏输入两个参数并返回较*

#define Min(X, Y) ((X)>(Y)?(Y):(X))

*结构体与联合体的区别。*

结构体中的成员拥有独⽴的空间,共⽤体的成员共享同⼀块空间,但是每个共⽤体成员能访问共⽤区的空间⼤⼩是由成员⾃身的类型决定。
共用体使用覆盖技术,成员变量相互覆盖。

*用变量a 给出下面的定义*

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

*++a和a++的区别是什么?*

++a的是先加后取值,a++的是先取值后增加

*大端和小端*

小端 - 低字节存储在低地址 高字节存储在最高地址

如:Intel、AMD、X86等采用的是这种方式;

大端- 高字节存储在低地址 低字节存储在高地址

应用题

取一个范围为-100~499的随机数

运用到随机数

试卷上的答案

1
2
3
4
5
6
7
8
9
10
#include <stdlib.h>

void get_rand_func()
{
int a, b;

a = -100;
srand(a);
b = rand() % 600 + a;
}

自己写的

1
2
3
4
5
6
7
int main()
{
srand((unsigned int)time(NULL));
int ret = (rand() % 500)-100;//可以生成-100~499的数
printf("%d\n", ret);
return 0;
}

查错题1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//代码是将输入的字符串拼接,"姓名","张三"=>"姓名张三"
#include <stdio.h>
char* func(char* ch1, char* ch2)
{
char ch[256];//
int len;
memcpy(ch, ch1, strlen(ch1));
len = strlen(ch1);
memcpy(ch + len, ch2, strlen(ch2));
len += strlen(ch2);
ch[len] = 0;
return ch;
}
void main()
{
char* ch;
ch = func("姓名", "张三");
printf("合成字符串:%s\n", ch);
system("pause");
}

这一题主要考察的是局部变量跟全局变量的引用,局部变量,在函数运行完之后就会马上释放掉,最后返回ch时,返回的是一个空值,cahr* 返回的是地址,此地址已经被释放掉了,返回出来即为错误的,造成字符串的拼接失败,正确的改法如下代码所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
char ch[256] ;
char* func(char* ch1, char* ch2)
{
int len = 0;
memcpy(ch, ch1, strlen(ch1));
len = strlen(ch1);
memcpy( ch + len , ch2, strlen(ch2));//
len += strlen(ch2);
ch[len ] =0 ;

return ch;
}

void main()
{
char* ch;
ch = func("姓名", "张三");
printf("合成字符串:%s\n", ch);
system("pause");
}

把定义的char ch[256] 换成全局变量,此代码即可运行成功。

查错题2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//代码是把一个字符串倒序, "hello,world"=>"dlrow,olleh"
#include <string.h>
#include <stdio.h>
#include<malloc.h>

int main()
{
char* src = "hello,world";
char* dest = NULL;
char* d = NULL;
char* s = NULL;
int len = 0;

len = strlen(src);
dest = (char*)malloc(len);//

if (dest == NULL)
{
return 0;
}

d = dest;
s = src[len - 1];//

while(len-- != 0)
{
d++ = s--;//
}

*d = 0;
printf("%s\n", dest);
system("pause");

return 0;
}

该题目考察的是指针与数组之间的指向,以及指针查找字符串数据的位置,例如s = src[len - 1];这样的赋值会出现错误,即为把数据赋值在char* s的上,不是用地址来传递,根据调试出现这个错误

image-20230205183337929

正确的做法为:s = src + len -1,调试端如下

image-20230205183629571

更改完这个,一下子就看出来 d++ = s–,这里存在错误,d是指针你要改地址里的内容就是+ 星号,不然就是把指针值改掉了而不是改指针指向的内容,这部分不能这写,一开始我写成了这样*(d++) = s–,这里即出现了大问题,d上的数据为s上的地址,由于,char * 为一个字节,只会保存最后小端的地址最后的两个字节(我电脑是小端的),造成一开始数据不是反写的情况,具体如下调试说明:

image-20230205190025940

赋值给d的地址最开始的最后俩位为7a转化为10进制即为122,对应ASCII即为z,这也是遇到的一个问题,分析出原因,理解了指针字符的作用,也间接的理解了大小端的关系。最后修改即为*(d++) = *(s–),取值的赋值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//代码是把一个字符串倒序, "hello,world"=>"dlrow,olleh"
#include <string.h>
#include <stdio.h>
#include<malloc.h>

int main()
{
char* src = "hello,world";
char* dest = NULL;
char* d = NULL;
char* s = NULL;
int len = 0;
int i = 0;

len = strlen(src);
dest = (char*)malloc(sizeof(char)*len + 1);//分配空间一开始写准确点
printf("%d\r\n",sizeof(char)*len);
printf("%x\r\n",dest);
//printf("%d\r\n",len);
if (dest == NULL)
{
return 0;
}
d = dest; //指向刚刚分配的空间首地址
s = src + len - 1 ;//这样指向尾部数据


while(len-- != 0)
{
*(d++) = *(s--);//指向这个数据,把数据取出来,单纯的s--是数据的地址,会出现错误*(d++) = s--,因为由于char类型,只取地址的最低字节,出来应该即位ASCII码;
//printf("s = %c\r\n",s);
}


*d = 0;
printf("%s\n", dest);
system("pause");

return 0;
}

查错题3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//代码是将学生名记录在结构体表中,并统计学生个数
#include <string.h>
#include <stdio.h>
#include <malloc.h>

typedef struct
{
char *student;
int count;
} INFO_TB;

int main()
{
INFO_TB* pInfoTb;
char* ch1 = "张三";
char* ch2 = "李四";
int len;

pInfoTb = (INFO_TB*)malloc(sizeof(INFO_TB));

if (pInfoTb == NULL)
{
return 0;
}

//
memcpy(pInfoTb->student, ch1, strlen(ch1));
len = strlen(ch1);
pInfoTb->count++;

memcpy(pInfoTb->student + len, ch2, strlen(ch2));
len += strlen(ch2);
pInfoTb->count++;

//
free(pInfoTb);
system("pause");
}

这到题考验的是结构体分配内存,主要卡点为,结构体中存在指针时,需要另外给结构体中的指针分配空间。

pInfoTb =(INFO_TB*)malloc(sizeof(INFO_TB));分配的是常量,例如int 、char之类的数据,没有给结构体中指向的数据分配空间,造成memcpy错误, memcpy(pInfoTb->student, ch1, strlen(ch1));这样写以及是地址指向了。另外,分配完空间需要及时的释放,不让会造成内存泄漏。pInfoTb->count++,看起来是没有问题的,但是在调试的时候出现如下问题:

image-20230205191905047

这是没有初始化造成的,跟编译器有关,有的编译器会自动初始化,我使用的是VC++ 6.0版本比较老,没有初始化,在这里画了一些时间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//代码是将学生名记录在结构体表中,并统计学生个数
#include <string.h>
#include <stdio.h>
#include <malloc.h>

typedef struct
{
char *student;
int count;
} INFO_TB;
//结构体中,有指针存在,需要单独给指针分配空间。
int main()
{
INFO_TB* pInfoTb;
char* ch1 = "张三";
char* ch2 = "李四";
int len;
pInfoTb->count = 0;//初始化需要加上。
pInfoTb = (INFO_TB*)malloc(sizeof(INFO_TB));

if (pInfoTb == NULL)
{
return 0;
}
//给指针的内容分配空间。
pInfoTb->student = (char*)malloc(strlen(ch1)+strlen(ch2)+1);
if (pInfoTb->student == NULL)
{
return 0;
}
memcpy(pInfoTb->student, ch1, strlen(ch1));
len = strlen(ch1);
pInfoTb->count++;
memcpy(pInfoTb->student + len, ch2, strlen(ch2));
len += strlen(ch2);
pInfoTb->count++;
//
free(pInfoTb->student);
free(pInfoTb);


system("pause");
}

最终的代码如上,这里说一下我的疑惑:

1
2
free(pInfoTb);
free(pInfoTb->student);

能否这样写呢?或者只写一个free(pInfoTb);——不能,因为free(pInfoTb);知识释放这个结构体里面的空间而不会释放结构体指针里面指向的数据的空间,因此不能只写一个,反过来写,也不行,不然结构体以及释放掉了,那一个空的结构体去指向一个数据,这样是释放不掉的。

请写出一个程序得出当前使用的是大端还是小端存储。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdio.h>
#include <stdlib.h>

typedef union
{
int i;
char c;
}UN_DAT;

int check_sys()
{
UN_DAT *p, dat;
dat.i = 1;
p = &dat;
return dat.c;
}

int main()
{
int ret = check_sys();
if (ret == 1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
system("pause");
return 0;
}

试卷上的参考答案是用指针,我来换一种方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>
#include <math.h>
#include <stdlib.h>

/*****************************************************/
int checkCPU()
{
union w
{
int a;
char b;
}c;
c.a = 1;
if(c.b == 1)
printf("小端\r\n");
else
printf("大1\r\n");
return 0;
}

//判断大小端的代码
int main()
{
checkCPU();
return 0;
}

达到的效果一致。大小端的区分要知道数据的在内存中的存放方式:

1
2
3
4
5
6
int main () 
{
int a=2;
return 0;
}
(未存储时)a=2转化为16进制时为:(00就是高序字节)0x00 00 00 02(02就是低序字节)(引用CSDNʕ•̀ o •́ʔ!文章)

image-20230205200403491

image-20230205200517901

1675596569575

即可看出来,代码的部分是怎么描述的,我们假设其中一个字节的数据,就能知道另外三个字节的数据了,从而验证猜想。也就是char *p=(char *)&a这句。以下是小端(引用CSDN不悔哥的例子)

0000 0000 内存高地址
0000 0000
0000 0000
0000 0001 内存低地址<——

大端如下

0000 0001 内存高地址
0000 0000
0000 0000
0000 0000 内存低地址<——

总结

这次的联系弥补了自己的各个细节部分的缺少的点,更加完善了自己的知识体系。