第一题
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] );
return 0;
}
运行结果
2
3
-9
分析
数组的排列如下
1 2 3
4 5 6
7 8 9
一共有三个数组,一 一来看:
要取出数组元素,就要计算出数组元素的地址。设数组 a[rows][cols](即有rows行,cols列),a[i][j]的地址为 a + i*cols + j , 这里是3行3列,故而
①nums[1][-2]的地址计算如下, ,得nums[0][1] = 2
nums + 1*3 + (-2)==nums + 1==nums + 0*3 +1==nums[0][1]==2
②(-1)[nums][5]
(-1)[nums][5] = nums[-1][5]
nums + (-1)*3 + 5 == nums + 0*3 + 2 == nums[0][2] == 3
③-1[nums][5]
-1[nums][5] == - nums[1][5]
nums + 1*3 +5 == -(nums + 2*3 + 2) == - nums[2][2] == -9
第二题
int main(int argc, char *argv[])
{
int a[3][2] = {
2, 0, 1, 8 };
char *str = (char *)malloc(sizeof(char) * 20);
strcpy(str, "\0101\\xb2");
printf("%zu\n", sizeof(a));
printf("%zu %d\n", sizeof(a[1][1] =0), a[1][1]);
printf("%zu %zu\n", sizeof(str), strlen(str));
return 0;
}
运行结果
24
4 8
8 6
分析
数组排列如下
2 0 1
8 0 0
0 0 0
① a 是一个 int 类型的数组 , 一位四字节,6 位 24 字节。
② 一位四字节 , a[1][1] == 8
③ * 转义字符
第三题
static int a = 2018;
static void func(void)
{
static int b;
printf("a = %d, b = %d\n", a++, ++b);
}
int main(int argc, char *argv[])
{
func( );
func( );
func( );
return 0;
}
分析
static 静态变量
把局部变量改变为静态变量后是改变了它的存储方式,即改变了它的生存期。
1.普通局部变量:存储于栈中,使用完毕会立即释放
被 static 修饰的变量总是存在内存的静态区
2.自动变量会随着函数被调用和退出而存在和消失,而static类局部变量不会,它不管其所在的函数是否被调用,都将一直存在
把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围
1.全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。这两者在存储方式上并无不同
2.非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。
静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。
static静态函数:使函数的作用域仅局限于本文件,即内部函数。
第四题
下面是一个 C 语言程序从源代码到形成可执行文件的过程,请解释图 中的 ABCD 分别表示什么,在每个阶段分别完成了什么工作?
一个程序从源代码到可执行程序的过程
一、预编译:主要处理源代码文件中的以“#”开头的预编译指令。
1.删除所有的#define,展开所有的宏定义。
2.删除所有的注释,“//”和“/**/”。
…
二、编译:进行一系列词法分析语法分析、语义分析及优化后,生成相应的汇编代码文件。
1.词法分析 2. 语法分析 3. 语义分析 4. 优化 5. 目标代码生成 6. 目标代码优
三、汇编:将汇编代码转变成机器可以执行的指令(机器码文件)
四、链接
第五题
根据所给代码,说明 const 关键字的用法,指出标号为 (1)~(4) 的 代码哪些是错误的。
char y[ ] = "XiyouLinuxGroup", x[ ] = "2018";
char *const p1 = y; const char *p2 = y;
/* (1) */ p1 = x ;
/* (2) */ p2 = x ;
/* (3) */ *p1 = 'x';
/* (4) */ *p2 = 'x';
const关键字
1.const int *p :*p指向的内容无法改变
2.int const *p:*p指向的内容不可改变
3.int * const p: *p不能改变指向,值可以变
4.const int *const p:即不改变指向也不改变内容
分析
1,4错误
2,3正确
总结:
如果const在 * 的左侧,则const用来修饰指针所指向的变量,即指针指向位常量;
如果const位于 * 的右侧,则const就是修饰指针本身,即指针本身就是常量。
const与#define区别
1.#define 宏是在预编译阶段进行替换,而 const 修饰的只读变量是在编译的时候确定其值
2…#define 宏没有类型,而 const 修饰的只读变量具有特定的类型
第六题
猜想下面程序的输出,并谈谈自己的理解。
int main(int argc, char *argv[])
{
int a[5];
printf("%p\n", a);
printf("%p\n", a+1);
printf("%p\n", &a);
printf("%p\n", &a+1);
return 0;
}
打印数组地址,每个计算机的结果都有可能不同,这里不打印我的结果
主要讲一下知识点
数组名+1与&数组+1
a即a[0]的地址。 a+1取的是a[1]的地址,即在数组首地址上加上一个int型大小空间。
&a取的也是数组首地址 。 &a+1中的&a代表整个数组, +1后就是跳过这个数组,即加上5个int大小的空间
第七题
谈谈你对 main 函数的理解,可以从参数、返回值等角度分析。
int main(int argc, char *argv[])
{
/*...*/
}
程序由主函数开始运行,由主函数结束,主函数可调用任何函数,任何函数都不可以调用主函数
argc:参数的个数。argv[]:是一个字符指针数组,每一个元素指向一个字符串。argv[0]:指向程序的路径名,argv[1…]:指向第n个参数对应的字符串
第八题
分析以下函数,给出 f(2018) 的值,推测并验证函数的作用。
int f(unsigned int num)
{
for (unsigned int i = 0; num; i++)
{
num &= (num - 1);
}
return i;
}
位操作
&运算符,两个数对应的二进制位都为1结果为1 ,否则 为 0
第一次循环num=2018&2017等价于11111100010&11111100001 结果为11111100000,即 num = 2016
第二次循环num=2016&2015等价于11111100000 & 11111011111 结果为 11111000000,即num=1984
第三次循环num=1984&1983等价于11111000000&11110111111结果为11110000000,即num=1920
第九题
分析以下代码段,解释输出的结果。
int main(int argc, char *argv[])
{
char n[] = {
1, 0, 0, 0 };
printf("%d\n", *(int *)n);
return 0;
}
大小端
分析
强制转换为int*类型,按大小端模式读出,如果是小端就是0000 0000 0000 0000 0000 0000 0000 0001
第十题
分析以下代码段,解释输出的结果。
#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);
printf(LEVELONE(YEAR));
printf(LEVELTWO(YEAR));
}
第十一题
以下代码段输出的两个值相等吗?为什么?
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));
return 0;
}
运行结果
16 24
涉及知识点,结构体对齐
对齐规则
1.第一个成员在与结构体变量偏移量(offset)为0的地址处。
2.其他成员变量要对齐到对齐数的整数倍的地址处。
(1)对齐数 = 对齐系数 与 该成员大小的较小值。
(2)#pragma pack(n);中的n就是对齐系数。
(3)VS中默认的值为8;linux中的默认值为4。
3.结构体总大小为最大对齐数(每个成员变量除了第一个成员都有一个对齐数)的整数倍。
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍
第十二题
在以下代码段执行完毕后,文件 Linux.txt 中的内容是什么?
int main(int argc, char *argv[])
{
FILE *fp = fopen("Linux.txt", "wb");
long long a = 0x78756e694c;
fwrite(&a, sizeof(a), 1, fp);
fclose(fp); return 0;
}
运行结果
Linux
第十三题
判断以下代码是否存在问题,如果有请找出并做适当的修改。
typedef struct a
{
char *name;
int num;
} A;
void func(A *a)
{
a = (A *)malloc(sizeof(A));
strcpy(a->name , "XiyouLinuxGroup");
a->num = 2018;
}
int main(int argc, char *argv[])
{
A *a;
func(a);
printf("%s %d\n", a->name, a->num);
return 0;
}