小组18,19,20面试题总结

1. 数组指针与指针数组
	int *p[5];
	int (*p)[5];  //p先和*结合,说明了p是一个指针变量,指向一个大小为5的数组

int (*p)[5]为一个数组指针

int *p[5]则是一个大小为5且存放整型指针的指针数组

  1. 指针运算
    指针指向的是一个地址,因此数组指针也同样可以进行相关运算;例如指针的加减可以实现指针指向数组上一个或者下一个元素的功能
    在这里插入图片描述定义指针变量的时候需要定义类型,如果指针p指向了一个数组中的一个元素,那么p+1并不是将地址加上1,而是系统判定类型之后加上一个数组元素所占用的字节数(即为p+1*d)。

举例说明:

int main()
{
	int a[4] = { 2, 0, 1, 9 };
	printf("%p, %p\n", a, &a);
	printf("%p, %p\n", a + 1, &a + 1);
	return 0;
}

结果:0x7fffffffe3e0, 0x7fffffffe3e0
	 0x7fffffffe3e4, 0x7fffffffe3f0
	int main(int argc, char *argv[])
	{
		int nums[3][3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
		printf("%d\n", nums[1][-2] );
		printf("%d\n", (-1)[nums][5] );
		printf("%d\n", -1[nums][5] );
	}
	
/*结果详解:nums[1][-2]=nums[0][1]
          (-1)[nums][5]=nums[-1][5]=nums[0][2]
		  -1[nums][5]=-nums[1][5]=nums[2][2]
*/

相关地址的表示方式,便于理解:

表示含义
a二维数组名,指向a[0]
a[0], *(a+0), *a0行0列元素地址
a+1, &a[1]1行首地址
a[1]+2, *(a+1)+2, &a[1][2]a[1][2]的地址
*(a[1]+2), ((a+1)+2), a[1][2]a[1][2]的值
2. 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 = 0
a = 2019,b = 1
a = 2020,b = 2

第一个作用:修饰变量(分为修饰全局变量和修饰局部变量两种情况)

1.修饰全局变量:全局变量的值存放在栈上,其存储类型为静态存储类型
全局变量的作用域为从定义全局变量起始处到文件结尾处
没有加static关键字的全局变量,除了可以在其定义的文件之中被引用外,其他文件也可以通过使用extern声明来引用它。
static关键字的全局变量,其他文件即使使用extern声明也无法引用它

2.修饰局部变量:若局部变量前没有加static关键字,则其值存放在堆上,存储类型为动态存储类型,作用域为从变量定义起始处到函数结束处,可以被初始化任意次,若未指定初始化值,则默认初始化值是一个任意数。
若局部变量前加了static关键字,则其值存放在栈上,存储类型为静态存储类型,作用域为从变量定义起始处到文件结尾处,只能被初始化一次,若未指定初始化值,则默认初始化值为0。

3. C语言程序从源代码到可执行文件过程

在这里插入图片描述1.预编译:主处理源代码文件中的以"#"开头的预编译指令,生成.i 文件
2.编译:把预编译之后生成的xxx.i或xxx.ii文件,进行一系列词法分析、语法分析、语义分析及优化后,生成相应的汇编代码文件。
3.汇编:将汇编代码转换成机器可执行的机器代码,生成.o文件
4.链接:将有关的目标文件彼此相连接

详细C语言程序从源代码到可执行文件过程详解

4. const关键字

它是定义只读变量的关键字,或者说 const 是定义常变量的关键字。

说 const 定义的是变量,但又相当于常量;说它定义的是常量,但又有变量的属性,所以叫常变量。用 const 定义常变量的方法很简单,就在通常定义变量时前面加 const 即可

	int main()
	{
		 char y[] = "xiyoulinuxgroup",x[] = "2018"
		 char *const p1 = y; 
		 const char * p2 = y;
		 
		 return 0;
	}

const后面紧跟的是不能改变
p1(地址)不可变,p1指向的对象(值)可变
p2(地址)可变,p2指向的对象(值)不可变
const关键字详细用法

5. define用法

当宏参数是另一个宏的时候,需要注意的是凡宏定义里有用’#’或’##’的地方宏参数是不会再展开.
即, 只有当前宏生效, 参数里的宏不会生效 !

宏定义参数连接符 ##:##主要用于将宏定义中的两个token链接起来,这里的token可以是宏的变量,也可以是任意参数或者标记。

(嵌套宏中,有##时,先展开函数,再展开参数)

宏定义符号#: #能是将其后面的宏参数进行字符串化操作,简单说就是在对它所引用的宏变量 通过替换后在其左右各加上一个双引号

(嵌套宏中,有#时,不展开参数)

宏定义符号#@:将标记转换为相应的单个字符,注意:仅对单一标记转换有效

	#define YEAR 2018
	#define LEVELONE(x) "xiyoulinux"#x"\n"
	#define LEVELTWO(x) LEVELONE(x)
	#define MULTIPLY(x,y) x*y
	
	int main(int argc, char *argv[])
	{
		int x = MULTIPLY(1+2,3);
		printf("%d\n",x);            // 7   =   1+2*3
		printf(LEVELONE(YEAR));      // xiyoulinuxYEAR
		printf(LEVETWO(YEAR));       // xiyoulinux2018
	}

define中#和##的用法详解
注意:宏定义不是语句,是预处理指令,故结尾不加分号。

6. 结构体字节对齐

32位机字节数
计算结构体大小需要遵循的规则

  1. 第一个成员在与结构体变量偏移量为0的地址处
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处
  3. 结构体的总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍
  4. 如果嵌套了结构体的情况,被嵌套的结构体对齐到其自身对齐数的整数倍处(结构体的对齐数就是其内部成员中最大的对齐数),此时结构体的整体大小就是所有最大对齐数(含被嵌套结构体的对齐数)的整数倍。
	struct icd{
		int a;
		char b;
		double c;
	};
	struct cdi{
		char a;
		double b;
		int c;
	};
	int main(int argc, char *argv[])
	{
		printf("%zu %zu\n", sizeof(struct icd), sizeof(struct cdi));
	}

偏移量:指的是结构体变量中成员的地址和结构体变量地址的差

结构体大小等于最后一个成员的偏移量加上最后一个成员的大小
显然,结构体icd中第一个成员的地址就是结构体变量的首地址。因此,第一个成员a的偏移量为0(大小为4)。第二个成员b的偏移量(4+0)是第一个成员的偏移量加上第一个成员的大小,b的大小为(4+1)。第三个成员c的偏移量(5),大小为(5->8 补齐 ,8+8=16)

7. main函数

在C99标准中定义main函数两种正确的写法

	int main(void);
	int main(int argc, char* argv[]);

参数

  1. argc : main函数参数个数,当参数为void的时,argc=1,默认参数为可执行文件名
  2. argv : 指针数组,分别指向个参数字符串首地址,其中argv[0]指向默认参数

main函数返回值

main函数的返回值用于说明程序的退出状态。如果返回0,则代表程序正常退出。返回其它数字的含义则由系统决定。通常,返回非零代表程序异常退出。

8. 大小端

大端存储就是把一个数的低位字节序的内容存放到高地址处,高位字节序的内容存放在低地址处。
小端存储就是把一个数的低位字节序的内容存放到低地址处,高位字节序的内容存放在高地址处。

	puts((char*)(int const[])
	{
		0X6F796958,0X6E694C75,0X72477875,
		0X3270756F,0X313230,0X00000A
	});
	
	结果:XiyouLinuxGroup2021
  1. 大端模式:

    低地址 -----------------> 高地址

    0x12 | 0x34 | 0x56 | 0x78

  2. 小端模式:

    低地址 ------------------> 高地址

    0x78 | 0x56 | 0x34 | 0x12

9. 逻辑运算
&I^<<>>~
按位与按位与异或按位左移按位右移按位取反
int main(int argc, char * argv[]) 
{
	char ch = 'A';
	int i = 65;
	unsigned int f = 33554433;
	*(int *)&f >>= 24;
	*(int *)&f = *(int *)&f + '?';
	printf("ch = %c i = %c f = %c\n", ch, i, *(int *)&f);
	return 0;
}

结果:ch = A i = A f = A

左移与右移比较类似,是将目标二进制数字向左/右移动相应的位数。

     左移补0:1111 1111 <<1 == 1111 1110,换算十进制的话是原来数值的2倍。

     右移看情况:负数补1,正数补0.需要看符号位。同样,换算为十进制数值变为原来的1/2.

     总结:左乘右除。
int main(int argc, char *argv[])
{
	char x = -2, y = 3;
	char t = (++x) | (y++);
	printf("x = %d, y = %d, t = %d\n", x, y, t);
	t = (++x) || (y++);
	printf("x = %d, y = %d, t = %d\n", x, y, t);
	return 0;
}
结果:x = -1, y = 4, t = -1
	 x = 0, y = 5, t = 1
9. printf返回值

举例:

	# include <stdio.h>
	int main()
	{
		int A=43;
		printf("%d\n",printf("%d",printf("%d",A)));
	}
	结果:4321

一,执行第三个printf,输出A的值“43”
二,执行第二个printf,输出"printf("%d",A)“的值,在结果中对应的是"2”
三,执行第一个printf,输出"printf("%d",printf("%d",A))“的值,为"1”
printf的返回值就是输出的字符数量
第三个printf输出"43"字符数量为2,于是返回值为2,第二个printf就输出"2”
第二个printf输出"2"字符数量为1,于是返回值为1,第一个printf就输出"1"

	int main(int argc, char *argv[])
	{
		printf("%d\n", printf("Xiyou Linux Group2%d", printf("")));
		return 0;
	}
	结果:Xiyou Linux Group2019

先打印了空字符,%d打印出的为0
最后一次printf函数打印出19(前面的字符数)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值