0. 鼠鼠我啊,要被祸害了
有1000瓶水,其中有一瓶有毒,小白鼠只要尝一点带毒的水,24小时后就会准时死亡。至少要多少只小白鼠才能在24小时内鉴别出哪瓶水有毒?
我们可以使用二进制编码的方法来解决这个问题。给每瓶水分配一个从0到999的数字(总共1000种,刚好是10位二进制数可以表示的范围)。例如,可以这样分配:
水瓶1:0000000000 水瓶2:0000000001 水瓶3:0000000010 …
水瓶1000:1111101000
1位进位代表2个可能,现在有1000个瓶子,则至少需要10个进位,因为2的10次方为1024。一个进位的两个可能需要1个小鼠,小鼠活着代表0,小鼠死了代表1.现在有10个进位,则需要10个小鼠。
1. 先预测一下~
按照函数要求输入自己的姓名试试~
char *welcome() {
// 请你返回自己的姓名
}
int main(void) {
char *a = welcome();
printf("Hi, 我相信 %s 可以面试成功!\n", a);
return 0;
}
运行结果:
我相信***可以面试成功
解释:
通过定义函数指针来接收名字,并将名字的地址用指针传给a,最后输出。
2. 欢迎来到Linux兴趣小组
有趣的输出,为什么会这样子呢~
int main(void) {
char *ptr0 = "Welcome to Xiyou Linux!";
char ptr1[] = "Welcome to Xiyou Linux!";
if (*ptr0 == *ptr1) {
printf("%d\n", printf("Hello, Linux Group - 2%d", printf("")));
}
int diff = ptr0 - ptr1;
printf("Pointer Difference: %d\n", diff);
}
运行结果:
Hello, Linux Group - 2023
Pointer Difference:
解释:
1.ptr0为指针定义的字符串,ptr1为数组定义的字符串。 两个字符串内容相同。
2.相关知识解释:
在C语言中,printf 函数用于向标准输出(通常是屏幕)打印格式化的字符串。尽管 printf的主要目的是输出,但它实际上也返回一个值,这个值是一个整数,表示成功打印的字符数(不包括终止的空字符 \0)。
然而,需要注意的是,这个返回值并不包括由于格式说明符(如 %s、%d 等)而插入到输出中的字符数,包括转义序列(如 \n、\t)和任何硬编码的文本。
3.题目中的printf
题目中为一个print的嵌套,printf函数从右向左看
最右边为“ ”,占一个字符,返回值为0,0输出到中间printf中%d的位置,Hello, Linux Group -
20,而这个的返回值为23,所以输出Hello, Linux Group - 20234.最后两个指针(地址)进行减法,不可以这样操作,因为ptr0为指针定义的字符串,储存在静态区,ptr1为数组定义的字符串,储存在栈区。二者相减结果为随机值(每次程序运行结果不同)。不是0
3. 一切都翻倍了吗
请尝试解释一下程序的输出。
请谈谈对sizeof()和strlen()的理解吧。
什么是sprintf(),它的参数以及返回值又是什么呢?
int main(void) {
char arr[] = {'L', 'i', 'n', 'u', 'x', '\0', '!'}, str[20];
short num = 520;
int num2 = 1314;
printf("%zu\t%zu\t%zu\n", sizeof(*&arr), sizeof(arr + 0),
sizeof(num = num2 + 4));
printf("%d\n", sprintf(str, "0x%x", num) == num);
printf("%zu\t%zu\n", strlen(&str[0] + 1), strlen(arr + 0));
}
运行结果:
7 8 2
0
4 5
解释:
1.数组和变量的定义: char arr[] = {‘L’, ‘i’, ‘n’, ‘u’, ‘x’, ‘\0’, ‘!’}; 定义了一个字符数组,但通常字符串以空字符\0结尾,所以’!'后面的字符(如果有的话)将被忽略,因为字符串在这里已经正确终止。
char str[20]; 定义了一个足够大的字符数组来存储格式化后的字符串。
short num = 520; 和 int num2 = 1314; 分别定义了短整型和整型变量。2.printf和sizeof的使用: sizeof(*&arr) ,实际上&arr传入的是整个数组的地址,*为解这个地址,arr里有7个字符,所以为7。
sizeof(arr + 0) 等同于 sizeof(&arr[0]),这里是一个指向char的指针的大小(32位为4或64位为8字节)。
sizeof(num = num2 + 4) 是一个复杂表达式。首先,num2 +4计算得到一个整数值,然后赋值给num,但sizeof运算符不会评估其操作数的值,而是评估其类型。因此,这个表达式的结果是short的大小2个字节。3.sprintf的使用: sprintf(str, “0x%x”, num) 将num的值格式化为十六进制字符串,并存储在str中。 sprintf的返回值是写入的字符数(不包括终止的空字符\0)。
sprintf(str, “0x%x”, num) == num这个比较是不合理的,因为sprintf的返回值(写入的字符数)几乎不可能等于num的值(520)。这会导致条件始终为假,因此printf(“%d\n”,…)将打印0。4.strlen的使用: strlen(&str[0] + 1) 计算从str[1]开始的字符串长度,即跳过了str的第一个字符,为4. strlen(arr + 0) 等同于 strlen(arr),计算从arr[0]开始的字符串长度,为5.
5./t为制表符,占8个字节。
4. 奇怪的输出
程序的输出结果是什么?解释一下为什么出现该结果吧~
int main(void) {
char a = 64 & 127;
char b = 64 ^ 127;
char c = -64 >> 6;
char ch = a + b - c;
printf("a = %d b = %d c = %d\n", a, b, c);
printf("ch = %d\n", ch);
}
运行结果:
a=64 b=63 c=-1
ch=-128
解释:
1.位运算
2.64换算为二进制为01000000,127换算为二进制为01111111。
a=64&127为01000000,换算为十进制为64。
b=64^127为00111111,换算为十进制为63。
3. 正数的补码是它本身,负数的补码是符号位不变,其余按位取反,最后再加1得到的。
所以-64的补码为11000000,右移6位,符号位不变,所以变为10000001,转换为十进制为-1.即c=-1.
4.ch=63+64-(-1)=128,因为char类型的取值范围为-128–127,占一个字节(8位),127为01111111,加1,则为10000000,表示-128.
5. 乍一看就不想看的函数
“人们常说互联网凛冬已至,要提高自己的竞争力,可我怎么卷都卷不过别人,只好用一些奇技淫巧让我的代码变得高深莫测。”
这个func()函数的功能是什么?是如何实现的?
int func(int a, int b) {
if (!a) return b;
return func((a & b) << 1, a ^ b);
}
int main(void) {
int a = 4, b = 9, c = -7;
printf("%d\n", func(a, func(b, c)));
}
运行结果: 6
解释:
1.该 func函数为递归函数,if (!a) return b;为终止条件
2.将十进制转换为二进制进行运算,最后在变为十进制。
3.位运算
6. 自定义过滤
请实现filter()函数:过滤满足条件的数组元素。
提示:使用函数指针作为函数参数并且你需要为新数组分配空间。
typedef int (*Predicate)(int);
int *filter(int *array, int length, Predicate predicate,
int *resultLength); /*补全函数*/
int isPositive(int num) { return num > 0; }
int main(void) {
int array[] = {-3, -2, -1, 0, 1, 2, 3, 4, 5, 6};
int length = sizeof(array) / sizeof(array[0]);
int resultLength;
int *filteredNumbers = filter(array, length, isPositive,
&resultLength);
for (int i = 0; i < resultLength; i++) {
printf("%d ", filteredNumbers[i]);
}
printf("\n");
free(filteredNumbers);
return 0;
}
解释:
int *filter(int *array, int length, Predicate predicate,
int *resultLength){
int *arr =(int*)malloc(length*sizeof(int));
int len=0;
for(int i =0;i<length;i++){
if(predicate(array[i])){
arr[len++]=array[i];
}
}
*resultLength=len;
return arr;
}
7. 静…态…
如何理解关键字static?
static与变量结合后有什么作用?
static与函数结合后有什么作用?
static与指针结合后有什么作用?
static如何影响内存分配?
解释:
1.2.3.static解释
4.static修饰全局指针,该指针只能在该文件中使用,不能用extern在其他文件中使用
static修饰局部指针,该指针可以在整个文件中使用
static修饰函数指针,该函数指针只能在该文件中使用,不能用extern在其他文件中使用5.静态存储区:使用static声明的变量(包括指针本身和它们指向的静态变量)都存储在静态存 储区。这意味着这些变量在程序的整个生命周期内都存在,并且在程序的启动时就分配好了内存。
堆区:如果静态指针指向动态分配的内存(例如使用malloc分配的内存),则这块内存属于堆区,其生命周期和释放时间由程序员控制。 代码段:如果静态指针指向的是函数指针或字符串常量等,这些对象可能存储在代码段。存储区。这意味着这些变量在程序的整个生命周期内都存在,并且在程序的启动时就分配好了内存。
8. 救命!指针!
数组指针是什么?指针数组是什么?函数指针呢?用自己的话说出来更好哦,下面数据类型的含义都是什么呢?
int (*p)[10];
const int* p[10];
int (*f1(int))(int*, int);
解释:
1.第一行为数组指针
2.第二行为常量指针数组,指针所指内容不能改变
3.这个声明较为复杂,表示f1是一个函数,它接受一个int类型的参数,并返回一个指向函数的指针。这个返回的函数指针指向的函数接受两个参数:第一个参数是int*(指向整数的指针),第二个参数是int,并返回一个int类型的值。简单来说,f1是一个高阶函数,它返回一个函数,这个返回的函数可以对整数和指向整数的指针进行操作,并返回一个整数结果。
为了更好地理解第三个声明,我们可以将其分解为两部分:
int (* )(int*, int):这是一个函数指针的类型,指向一个接受int*和int参数,返回int的函数。
int(*f1(int))(…):这表明f1是一个函数,它接受一个int参数,并返回一个上述类型的函数指针。
9. 咋不循环了
程序直接运行,输出的内容是什么意思?
int main(int argc, char* argv[]) {
printf("[%d]\n", argc);
while (argc) {
++argc;
}
int i = -1, j = argc, k = 1;
i++ && j++ || k++;
printf("i = %d, j = %d, k = %d\n", i, j, k);
return EXIT_SUCCESS;
}
运行结果:
i=0,j=1,k=2
解释:
1.argc为命令行参数的个数,默认为1,argv为每个命令行参数的内容
2.第一次打印是argc的值为1,所以打印出[1]3.argc的类型为int,范围是-2^31~ 2^31-1,初始argc满足while循环的条件,执行循环内的指令,当argc自增到2 ^ 31 -1时,再次自增,argc的值会变为最小值-2 ^ 31,仍然满足循环条件,循环继续执行,直到argc自增为0,跳出循环。
4.i++为先用后加,先用为-1,为真,并且i的值自增为0,接着判断j++,同样先用后加,先用为0,为假,并且自增为1,逻辑与的结果为假,继续执行k++,先用后加,k的值自增为2。
10. 到底是不是TWO
#define CAL(a) a * a * a
#define MAGIC_CAL(a, b) CAL(a) + CAL(b)
int main(void) {
int nums = 1;
if(16 / CAL(2) == 2) {
printf("I'm TWO(ノ>ω<)ノ\n");
} else {
int nums = MAGIC_CAL(++nums, 2);
}
printf("%d\n", nums);
}
运行结果:
1
解释:
1.if判断中为16/222,注意没加括号,所以结果为32,!=2
2.所以执行else语句,在else语句中重新创建了变量nums并赋值,但因为此变量是在else语句中创建的,所以为局部变量,跳出else语句后会被销毁,所以printf中nums的值仍为1,打印的的结果为1。
11. 克隆困境
试着运行一下程序,为什么会出现这样的结果?
直接将s2赋值给s1会出现哪些问题,应该如何解决?请写出相应代码。
struct Student {
char *name;
int age;
};
void initializeStudent(struct Student *student, const char *name,
int age) {
student->name = (char *)malloc(strlen(name) + 1);
strcpy(student->name, name);
student->age = age;
}
int main(void) {
struct Student s1, s2;
initializeStudent(&s1, "Tom", 18);
initializeStudent(&s2, "Jerry", 28);
s1 = s2;
printf("s1的姓名: %s 年龄: %d\n", s1.name, s1.age);
printf("s2的姓名: %s 年龄: %d\n", s2.name, s2.age);
free(s1.name);
free(s2.name);
return 0;
}
解释:
1.主函数以上部分
这段代码的目的是初始化一个struct Student类型的结构体实例。struct Student是一个结构体,它包含两个成员:一个指向字符的指针name,用于存储学生的名字;和一个整型age,用于存储学生的年龄。
函数initializeStudent接收三个参数:
struct Student *student:一个指向struct Student类型实例的指针,这个实例将被初始化。
const char *name:一个指向字符的指针,指向要赋给学生的名字。
int age:一个整数,表示要赋给学生的年龄。
函数内部执行以下操作:使用malloc函数为student->name分配足够的内存来存储name字符串及其结尾的空字符(‘\0’)。strlen(name) +1计算了包括结尾空字符在内的总字符数。 使用strcpy函数将name字符串复制到student->name指向的内存中。将age的值赋给student->age。
2.主函数部分首先对结构体进行初始化
其次让二者相等,但注意,前面malloc将二者的名字分配了两个内存,不能用s1=s2,来让二者名字相同,应写为strcpy(s1.name,s2.name); s1.age = s2.age;
最后,free释放内存也只用释放一次,因为使用strcpy将二者内存地址变为相同
12. 你好,我是内存
作为一名合格的C-Coder,一定对内存很敏感吧~来尝试理解这个程序吧!
struct structure {
int foo;
union {
int integer;
char string[11];
void *pointer;
} node;
short bar;
long long baz;
int array[7];
};
int main(void) {
int arr[] = {0x590ff23c, 0x2fbc5a4d, 0x636c6557, 0x20656d6f,
0x58206f74, 0x20545055, 0x6577202c, 0x6d6f636c,
0x6f742065, 0x79695820, 0x4c20756f, 0x78756e69,
0x6f724720, 0x5b207075, 0x33323032, 0x7825005d,
0x636c6557, 0x64fd6d1d};
printf("%s\n", ((struct structure *)arr)->node.string);
}
运行结果:
”Welcome to XUPT , welcome to Xiyou Linux Group [2023]“
解释:
arr[]数组中存储了结构体对应的内存地址,按顺序node.string应为第九到第十九字节,但因为没有遇到\n,所以后面一直存储到string字符串中,即从0x636c6557开始储存,向string中存入63,6c,…,54,50,(本应结束,但继续),直到结束。ASCII表中对应过来就是”Welcome to XUPT , welcome to Xiyou Linux Group [2023]“
13. GNU/Linux (选做)
注:嘿!你或许对Linux命令不是很熟悉,甚至你没听说过Linux。但别担心,这是选做题,了解Linux是加分项,但不了解也不扣分哦!
你知道cd命令的用法与 / . ~ 这些符号的含义吗?
请问你还懂得哪些与 GNU/Linux 相关的知识呢~
cd命令的用法
cd 是 “change directory” 的缩写,意为 “切换目录”。这个命令用于在 Linux
系统中更改当前工作目录。基本用法:
cd [目录]:切换到指定的目录。如果目录是绝对路径(以 / 开头),则直接切换到该路径;如果是相对路径,则相对于当前工作目录进行切换。
cd …:切换到当前目录的上一级目录。
cd .:实际上并不会改变当前工作目录,因为 . 表示当前目录。
cd~:切换到当前用户的家目录。对于普通用户,家目录通常位于 /home/用户名;对于 root 用户,家目录是 /root。
cd-:切换到上一次所在的目录。
符号含义
/:根目录的符号。在 Linux 系统中,所有文件和目录都位于根目录之下。根目录是文件系统的起点,用 / 表示。
.:当前目录的符号。在路径中使用 . 可以表示当前目录。
~:当前用户的家目录的符号。使用 ~ 可以快速切换到用户的家目录。
2896

被折叠的 条评论
为什么被折叠?



