1.分析下面的输出
int main(int argc, char *argv[])
{
int t = 4;
printf("%lu\n", sizeof(t--));
printf("%lu\n", sizeof("ab c\nt\012\xa1*2")); return 0;
}
sizeof计算的是对象占用的字节数, t-- 的类型是int, 所以输出4。字符串中包含转移序列,\n换行, \012为进制,\xa1是16进制, ascll字符128个,但是char是8bit, 所以八进制需要3位, 最大有效\377 表示255, 十六进制位区分八进制,用\x表示, ff表示255, 因此最大\xff。所以字符串中共 ‘a’‘b’‘space’‘c’’\n’‘t’’\012’’\xa1’’*’‘2’ + 字符串结尾’\0’11个
2.分析下面的输出
int main(int argc, char *argv[])
{
int a = 10, b = 20, c = 30;
printf("%d %d\n", b = b*c, c = c*2);
printf("%d\n", printf("%d ", a+b+c));
return 0;
}
第一个输出句输出两个赋值表达式的值, 复制表达式的值就是左侧的值,也就是被修改后的值,所以输出600, 60。 第二个输出句外层输出内层printf的返回值, printf返回值表示格式化输出字符的个数, 既然要知道输出字符的个数, 必须要先做输出字符, 所以会先做内层printf, 输出660, 然后输出字符个数,3+1 = 4个, 此题中的执行顺序我还无法验证。
3.判断代码正误
void get_str(char *ptr)
{
ptr = (char*)malloc(17);
strcpy(ptr, "Xiyou Linux Group");
}
int main(int argc, char *argv[])
{ char *str = NULL;
get_str(str);
printf("%s\n",str);
}
显然, 目的是想给传入函数的指针赋值, 也就是修改传入的指针, 对于函数而要, 想要修改传入的东西, 必须传入指向目标的指针,所以更改如下
void get_str(char **ptr)
{
*ptr = (char*)malloc(17);
strcpy(*ptr, "Xiyou Linux Group");
}
int main(int argc, char *argv[])
{ char *str = NULL;
get_str(&str);
printf("%s\n",str);
}
4.解释输出结果
size_t q(size_t b)
{
return b;
}
size_t (*p(char *str))(size_t a)
{
printf("%s\n", str);
return q;
}
int main(int argc, char *argv[])
{
char str[] = "XiyouLinuxGroup";
printf("%lu\n", p(str)(strlen(str)));
return 0;
}
先读第二个函数,从内向外, (*p(char * str)) 表示p是一个函数名, 该函数需要一个char *参数, 返回一个指针, 指针指向 size_t (size_t a)这个函数。 看到return 的是q, q是函数名, 那么返回的就是函数指针.
接下去分析输出句, p(str)调用p函数传入str, 所以会出输出字符串的动作, 然后返回q的指针,接着做q(strlen(str))
所以会输出strlen(str)的值, 也就是15。
5.static 全局变量与普通的全局变量有什么区别? static 局部变量和普通局部变量有什么区别? static 函数与普通函数有什么区别?
static作为类型限定, 在函数和全局变量前的作用类似private,表示属于文件私有,称作内部链接 修饰局部变量的static不相同,只将存储期变为静态。
6.下列程序分别输出的是数组中的第几个
int main(int argc, char *argv[])
{
int a[][2] = {0, 0, 0, 0, 0, 0, 0, 0};
for(int i = 0; i <= 2; i++){
printf("%d\n", a[i][i]);
}
return 0;
}
输出a[0][0] a[1][1] a[2][2]
a[0][0]鬼也知道是第一个, 上面给的数组列数位2, 所以a[1][1] 就是1 * 2 + 1也就是a[3]即第4个 a[2][2] 是2 * 2 + 2 a[6]即第7个.
7.const 关键字的作用是什么?下面的这几种定义有 区别吗?
const char *p;
char const *p;
char *const p;
const char *const p;
读法 const 和 * 谁在前就先读谁, 一二读作常量指针, 表示指针不能被修改, 三四读作指针常量, 表示指针指向的东西不能被修改。
8.说说 #include<> 和 #include" " 有什么区别?为 什么需要使用 #include ?
区别在预处理时候的搜索路径不同, 我也记不的, 只记得自己写的头文件要""。摘抄如下:
1.<> 先搜索系统头文件所在目录
2.“”先搜索当下目录
9.说明下面程序的运行结果。
int main()
{
int a, b = 2, c = 5;
for (a = 1; a < 4; a++) {
switch (a) {
b = 99;
case 2: printf("c is %d\n", c); break;
default: printf("a is %d\n", a);
case 1: printf("b is %d\n", b); break;
}
}
}
这里让我比较疑惑的是default的位置,其他比较简单,为什么default写在case前面的时候编译器仍然知道defualt后面的分支的存在?
10.说明下面输出的结果
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;
整形提升,在a + b > 0 的式子中出现了int 和 unsigned int, int 会被转化为unsigned int所以值肯定是正数, 执行第一条语句
11.下列程序运行的结果是什么?
11. 以下程序运行结果是什么?
int main(int argc, char *argv[])
{
int nums[5] = {2, 4, 6, 8, 10};
int *ptr = (int *)(&nums+1);
printf("%d, %d\n", *(nums+1), *(ptr-1));
return 0;
}
第一个 (nums + 1)中nums是数组名,降级成指向第一个元素的指针, 由于数组是一位的,所以指向的一个元素是nums[0], 为int, 所以 + 1运算偏移的单位就是int的长度, 所以解引用后是nums[1]
第二个ptr先是指向(&nums + 1), 因为nums是一维数组, 所以&nums指向一维数组, 所以(&nums + 1)是指向一个一维数组, 起始地址为nums[6], 然后做类型转换, 所以(ptr - 1)就是nums[4]
12.对比下面程序在 Linux 和 Windows 上的输出结 果,并思考原因。
#include <stdio.h>
int main(int argc, char *argv[]) {
while(1)
{
fprintf(stdout,"Group");
fprintf(stderr,"Xiyoulinux");
getchar( );
}
return 0;
}
linux下stdout默认行缓冲io, stderr无缓冲io, 所以stdout在遇到\n 或者buffer满 或者遇到另一个输入或强制输出时才会输出buffer中的内容, win下stdout, stderr默认无缓冲, 可以做如下修改
int main(int argc, char *argv[]) {
while(1)
{
fprintf(stdout,"Group\n");
fprintf(stderr,"Xiyoulinux");
getchar( );
}
return 0;
}
//
int main(int argc, char *argv[]) {
while(1)
{
fprintf(stdout,"Group");
fprintf(stdout, "\nhello");
fprintf(stderr,"Xiyoulinux");
getchar( );
}
return 0;
}
//
int main(int argc, char *argv[]) {
while(1)
{
fprintf(stdout,"Group");
fclose(stdout);
fprintf(stderr,"Xiyoulinux");
getchar( );
}
return 0;
}
13.观察程序的运行结果,说明出现这种情况的原因
int main(int argc, char *argv[])
{
char str[512];
int i;
for(i = 0; i < 512; i++)
{
str[i] = -1 - i;
}
printf("%lu", strlen(str));
}
str[i]的对象只有一个Byte, 而-1 - i是int, 所以会做截断, linux下属于小端机器, 低尾端, 先写低字节部分到低地址部分,由与每次的对象只有1字节, 所以会截取int的低8位。 strlen统计到\0,对应的8位无符数是00000000, 所以要找出第一个低8位是全0的数, 根据答案是255,就试试把i = 255带入, 发现-256的补码形式是111…11100000000,正好低八位为0高位全为1, 如果比-256更小那么最小1出现的位置只会在第9位或更高。
所以相当于str[255] = ‘\0’;
因此strlen就是255
14.请修改下面的 swap 函数,使得既可以交换 int 类 型的参数,也可以交换 double 类型的参数。
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
想法是按字节复制,这样的话就需要修改一下形参
首先我们不能声明具体的数字类型,不然肯定报错,
所以该成void *类型,让编译器忽略形参类型 既然如此编译器不知道偏移量, 我们还需要告诉它字节数, 修改如下
void swap(void *a, void *b, unsigned int n)
{
char *p = (char *)a;
char *q = (char *)b;
for(int i = 0; i < n; i++)
{
char temp = *(p + i);
*(p + i) = *(q + i);
*(q + i) = temp;
}
}
15.请说出 a 在不同平台上的值,并思考此种方式的 优点。
#ifdef __linux__
int a = 1;
#elif _WIN32
int a = 2;
#elif __APPLE__
int a = 4;
#else
int a = 3;
#endif
好处很显然, 可移植性强, 不需要重新为不同的平台写不同程序。
16.请设计一个程序,通过命令行参数接收一个文件 名 filename.txt( 纯文本文件 ) 和一个整型数字 n,实 现从 filename.txt 中删除第 n 行数据。
17.解释程序运行结果。
#include <stdio.h>
struct node{
char a;
short b;
int c;
}T;
int main(int argc, char *argv[]) {
T.a = 3;
T.b = 5;
T.c = 7;
struct node *pt = &T;
printf("%d\n", *(int*)pt);
printf("%lld\n", *(long long *)pt);
}
结构体为了读取高效, 存在字节对齐, 所以a 后有一个空字节,
写入没什么好说的, 和前面的题目一样,从低字节写起, 写满为止, 接下来是输出句, pt的地址是结构体的起始地址,也是a的起始地址, 做(int *)表示告诉编译器取四个字节进行解读, 所以读取的数据是将b和a的bit组合起来的一个int类型数据,下句同理。
18.分析下面这段代码,说出最终输出结果。
#define NAME(n) x##n
#define PRINTVAL(y, ...) printf("x"#y":" __VA_ARGS__)
int main(int argc, char *argv[])
{
int NAME(1);
short *NAME(2) = ( short *)&NAME(1);
char *NAME(3) = (char *)&NAME(1);
NAME(1) = 0;
*NAME(2) = -1;
PRINTVAL(1, "%x\n", NAME(1));
PRINTVAL(2, "%x\n", *NAME(2));
PRINTVAL(3, "%x\n", *NAME(3));
}
在一同赋值后, x1四字节的结构应为, 高2个字节全0, 低两个字节全1; 第一个输出, %x是unsigned int, 所以会做类型转化, 第一个输出就是ffff对应16个1的二进制数, 而x2, 是16位全位1的short,在整数上理解为-1, 因此做强制类型转换, 就变成了2^32 - 1, x3是8位全1的char, 整数上理解仍是-1, 做完类型转化, 就变成了2 ^ 32 - 1。
19.C语言从源程序到可执行程序需要经过哪些步骤? 说说这些步骤都具体干了哪些工作
编译预处理->汇编->生成obj文件->link
1.编译预处理, 做一些头文件的照抄啊, 宏定义的替换啊, 不然后面的编译是通不过的。
2.汇编,生成汇编文件, 这个。。我也不太清楚为什么要有这一步, 不过如果读懂汇编, 你可以知道计算机具体的硬件在哪一步做了什么,了无秘密
3.obj文件, 将汇编变成二进制文件,计算机才能执行
4.link,
链接就是将汇编生成的OBJ文件、系统库的OBJ文件、库文件链接起来,最终生成可以在特定平台运行的可执行程序。
不太懂链接, 这里照搬了定义。