小组15 17 18面试题总结

@[TOC])
新手上路,如果理解有问题,欢迎指出

大小端问题

此处,先了解大小端的概念
大端:高地址存放低字节,低地址存放高字节
小端:低地址存放低字节,高地址存放高字节
假设变量x的类型为int,位于地址0x01234567。地址范围0x100~0x103的字节顺序依赖于机器的类型。
大端法:
在这里插入图片描述
小端法:
在这里插入图片描述
大多数INTEL兼容机都只用小端模式
了解大小端概念之后,我们看一下17年的第17题

struct  node
{
	char s;
	short b;
	int c; 
};
int main(int argc,char *argc[])
{
	struct node s;
	memset(&s,0,sizeof(s));//将一块内存中的内容全部设置为指定的值,这个函数通常为新申请的内存做初始化工作,对较大的结构体或数组进行清零操作的一种最快方式,头文件<string.h>或<memory.h>,此处是将变量s所占内存的值全部置为零。
	s.a=3;
	s.b=5;
	s.c=7;
	struct node *pt=&s;//定义结构体指针,使其指向s
	printf("%d\n",*(int *)pt);//将指针pt转换为int类型指针,并解引用
	printf("%lld\n",*(long long *)pt);//同理
	return 0;
}
			低地址					                  高地址

在这里插入图片描述此处,我是以十六进制存储进去的,因为int占 四个字节,所以,第一次读出的十六进制的005003,将它转换成十进制,那么 ,long long也就很简单了,long long 占 8个字节 ,所读的应该是十六进制的0000007000500030,再将它转换为十进制就行了

宏定义问题

可用#define定义一个标识符来表示一个常量,定义的标识符不占内存,预编译后这个符号就不存在了,(用双引号括起来的宏在预处理的时候是不会被宏替换的,在c中,用双引号括起来来的是字符)
在这里插入图片描述
宏参数展开规则:在展开宏参数时,如果当前宏含有有##或#,则不进行宏参数的展开,否则先展开宏参数,再展开当前宏
然后,我们看一下15年的第八题

#define f(a,b) a##b
#define g(a) #a
#deine h(a) g(a)
int main(int argc,char *argv[])
{
	printf("%s\n",h(f(1,2)));
	printf("%s\n",g(f(1,2)));
}

我们先看第一个printf,h()为当前宏,f()为宏参数,含有宏参数时,先展开宏参数,再展开当前宏,因为当前宏h()里不含有#或##,所以先展开f(1,2),##将1和2连接起来,组成12,然后再展开当前宏h(),将12转换为字符,所以输出12;第二个printf ,g()为当前宏,f()为宏参数,因为当前宏g()里含有#,所以不进行宏参数的展开,输出f(1,2);

数组问题

先看一个题

int main(int argc,char *argv[])
{
	int a[3][4];
	printf("%p%p%p%p%p%p",&a[0][0],a[0],a,a[0]+1,a+1,a[1]);
	return 0;
}

若a[0][0]的地址是0x00000000,求程序输出的结果
这是一个二维数组,但严格来说没有什么二维数组,三维数组,我们可以把它看做是数组的数组或是数组的数组的数组,此时的二维数组就是一个数组的数组(可以看为一个大数组里包含了一个小数组),即对于a来说,它有3个元素,每个元素都是一个具有4个整型元素的一维数组,我们可以把a[0]看做第一个元素的数组名,其他类推
a=&a[0],所以有a+1=&a[0]+1,就是跨过第一行,(&a[0]跨越了整个第一个大数组)那么a[0]+1呢,此刻的a[0]是第一个大数组的数组名,所以a[0]+1就为第一个大数组中第二个元素的地址,即a[0][1]的地址,
此处,区别一下,a+1是第二行第一个元素的地址,&a+1跨越的是整个数组了
所以,这个题的答案为:
0x00000000
0x00000000
0x00000000
0x00000004
0x00000010
0x00000010

const关键字

定义const常变量,具有不可变性
它的作用有:
在这里插入图片描述
写题的时候,关键看const修饰谁,由于没有const的运算,所以若出现const的形式,则const实际上是修饰前面的
然后看一下17年的第七题

const  char *p;
char const *p;
char *const p;
const char *const p;

前边已经说过,没有const这种形式,所以const修饰的是char,则char const p,const保护p,所以就是p不可以改,p可以改;那么 char const p,const修饰的是p,就和前者一样,*p不可以改,p可以改,然后就是char const p,此时const修饰指针为常量(有了常量的特性),即p可以改,p不能改 ,最后一种情况,const char const p,第一个const后面紧跟的是char,所以char类型的字符p不可改
然后再看一个题,

int main(int argc,char *argv[])
{
	const int b=10;
	//自行添加语句,改变变量的值
	return 0;
}

前边说过,const修饰的变量具有不可变性,那么此时如何修改b的值呢?
我们可以通过指针来修改变量b的值,相信你一定写的出来

一个c语言程序从源代码到形成可执行文件的过程

在这里插入图片描述1.预编译:主要处理源代码文件中的以"#"开头的预编译指令,生成.i 文件
2.编译:将预编译之后生成的xxx.i文件,转换为汇编代码,生成.s文件
3.汇编:将汇编代码转换成机器可执行的机器代码,生成.o文件
4.链接:将有关的目标文件彼此相连接

关键字static

当自动变量被定义时,系统不会对其初始化,必须自行对其初始化,而定义static变量时,系统会自动对其初始化(数值型赋0,字符型赋‘、0’)

static全局变量与全局变量的区别

静态全局变量:全局变量的说明之前加上static就构成了静态的全局变量
我们先看一下非静态全局变量和静态全局变量的区别:
两者在存储方式上并无不同,都是静态存储方式
作用域有区别,非静态全局变量的作用域是整个源程序,当一个源程序是多个源文件组成时,非静态全局变量在各个源文件中都是有效的,而静态全局变量只在当前文件中起作用
static全局变量只初始化一次,下一次被调用时保存的是上次调用结束的值

static局部变量与局部变量的区别

首先,两者的存储方式就不相同,static局部变量 static全局变量 和普通全局变量都为静态存储,而普通局部变量为动态存储在这里插入图片描述在这里插入图片描述

static函数与普通函数的区别

在这里插入图片描述然后我们看一个题

static int a=2018;
static void func(void)
{
	static int b;
	printf("a=%d,b=%d\n",a++,++b); 
}
int main()
{
	func();
	func();
	func();
	return 0;
}

猜一下它的输出结果(应该是a=2018,b=1,a=2019,b=2,a=2020,b=3)

标准输出与标准错误

先看一个题

int main()
{
	while(1)
	{
		fprintf(stdout,"group");
		fprintf(stderr,"xiyoulinux");
		getchar();
	}
	return 0;
}

对比上面程序在linux和windows上面的输出结果,并思考原因
linux上应该是:xiyouLinuxgroup
windows上应该是:groupxiyoulinux
这是因为在linux下** 将group放在了缓冲区内,直到程序结束,才将缓冲区中的数据导出**
而windows下没有
在默认情况下,stdout是行缓冲的,它的输出会放在一个buffer内,只有到换行的时候才会输出到屏幕,而stderr是无缓冲的,会直接输出
stdout:标准输出,输出方式是行缓冲,输出的字符放在缓冲区内,等按下回车键才进行实际的操作,stdout输出的第二种情况是** 即遇到换行符时输出,执行清理缓存**
stderr:标准错误,是不带缓冲的,错误信息可以直接显示出结果
可以在了解一下其他的内容:(图画的很丑~假装没看到
在这里插入图片描述

指针问题

再看一个题

void get(char *ptr)
{
	ptr=(char *)malloc(17);
	strcpy(ptr,"xiyou linux group");
}
int main()
{
	char *str=NULL;
	get(str);
	printf("%s\n",str);
}

这个程序在电脑上运行会出错
main函数里的指针str指向空,get函数中的malloc函数为指针pt分配空间,将str指针传进get函数,但要搞清楚,str指针依旧指向空,所以在strcpy函数中,字符串根本写不进去,这就解释了为什么电脑会报错
我们可以将ptr的地址返回回来
代码修改如下:

char* get(char *ptr)
{
	ptr=(char *)malloc(17);
	strcpy(ptr,"xiyou linux group");
	return ptr;
}
int main()
{
	char *str;
	str=get(str);
	printf("%s\n",str);
}

或者可以使用二级指针

 char* get(char **ptr)
    {
     *ptr=(char **)malloc(17);
      strcpy(*ptr,"xiyou linux group");                                            
    }
  int main()
  {
      char *str;
      get(&str);
      printf("%s\n",str);
  }

这两种方法都可以使源代码正确,但是,这个程序中有一个很隐秘的问题,xiyou linux group加上后面的结束符‘0’,一共18个字符,但是仅仅为它分配了17个字节的空间,程序不会报错,但不安全,因为你占用了后面的内存,可能会引发一些未知的问题

隐式转换问题

先看代码

int main()
{
	unsigned int a=10;
	int b=-20;
	if(a+b>0)
	{
		printf("a+b=%d\n",a+b);
	}
	else
	{
		printf("a=%d,b=%d\n",a,b);
	}
	return 0;
}

数字在计算机内部是以二进制补码进行运算的,所以先将-20转为为二进制,在求它的补码而无符号数的补码最高位不表示符号,所以一个负数表示为无符号数的时候他就会变成一个很大的正数,所以上题中的a在和b相加的时候b会变成一个很大的正数所以a+b就大于0了

sizeof问题

int main()
{
	int t=4;
	printf("%lu",sizeof(t--));
	printf("%liiigviiilu",sizeof("ab c\nt\012\xa1*2"));
	return 0;
}

sizeof()的值是在编译时确定的,它的作用是获得保证能容纳实现所建立的最大对象的字节大小
第一个pirntf语句里面即使t–,t也是个int型变量,输出的肯定是int类型所占的字节数;
第二个printf语句里面a b c 空格 \n t \012 \xa1 * 2 '\0’一共11个字符,所占字节为11个,此处,需要注意的就是\012是一个八进制数,\xa1是一个十六进制数, ddd表示八进制数所代表的字符,可以不用0开头,也可以用0开头,\xhh表示十六进制数所代表的字符

字节截断问题

我们看这个题

intmain()
{
	char str[512];
	int i;
	for(i=0;i<512;i++)
	{
		str[i]=-1-i;
	}
	printf("%lu\n",strlrn(str));
}

将str[i]=-1,char类型占一个字节,int类型占四个字节,所以会存在字节截断的问题,如下图所示
在这里插入图片描述然后根据代码,
在这里插入图片描述所以当srt[255]=-256时,转换为十进制为0,那么根ASCII表,0所对应的为null,所以strlen读到null时就停止了,所以这个题的答案为255

关键字extern

#void func()
{
	printf("a=%d\n",a);\\此时此刻func函数里找不到a变量
}
int a=10;
int main()
{
	func();
	return 0;
}

要求不改变以下代码的运行顺序,修改程序使其能通过编译器运行
此时,就可以用extern关键字了,要注意,*** 当一个变量是全局变量时,才可以引用extern关键字 ***,在func函数里添加一句extern int a,告诉编译器a这个变量是存在的,但不是在这声明的,你到别的地方去找找
extern其他的作用是:
引用不在同一个文件下的变量

字节对齐问题

字节对齐有助于加快cpu的读取速度

struct icd
{
	int a;
	char b;
	double c;
};
struct cdi
{
	char a;
	double b;
	int c;
};

sizeof(icd)=16;
sizeof(cdi)=24;
此处就涉及到了字节对齐问题,

在这里插入图片描述在这里插入图片描述

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值