系统性学习C语言-第十六讲-深入理解指针(6)

系统性学习C语言-第十六讲-深入理解指针(6)

  • 1. ` sizeof ` 和 ` strlen ` 的对比
    • 1.1 ` sizeof `
    • 1.2 ` strlen `
    • 1.3 ` sizeof ` 和 ` strlen ` 的对比
  • 2. 数组和指针笔试题解析
    • 2.1 一维数组
    • 2.2 字符数组
    • 2.3 二维数组
  • 3. 指针运算笔试题解析
    • 3.1 题目1:
    • 3.2 题目2
    • 3.3 题目3
    • 3.4 题目4
    • 3.5 题目5
    • 3.6 题目6
    • 3.7 题目7

1. sizeofstrlen 的对比

1.1 sizeof

在学习操作符的时候,我们学习了 sizeofsizeof 计算变量所占内存空间大小的,单位是字节,

如果操作数是类型的话,计算的是使用类型创建的变量所占内存空间的大小。

sizeof 只关注占用内存空间的大小,不在乎内存中存放什么数据。

比如:

#include <stdio.h>
int main()
{int a = 10;printf("%d\n", sizeof(a));printf("%d\n", sizeof a);printf("%d\n", sizeof(int));return 0;
}

在这里插入图片描述

1.2 strlen

strlen 是 C语言 库函数,功能是求字符串长度。函数原型如下:

size_t strlen ( const char * str );

统计的是从 strlen 函数的参数 str 中这个地址开始向后,\0 之前字符串中字符的个数。

strlen 函数会⼀直向后找 \0 字符,直到找到为止,所以可能存在越界查找。

#include <stdio.h>
int main()
{char arr1[3] = {'a', 'b', 'c'};char arr2[] = "abc";printf("%d\n", strlen(arr1));printf("%d\n", strlen(arr2));printf("%d\n", sizeof(arr1));printf("%d\n", sizeof(arr2));return 0;
}

在这里插入图片描述

1.3 sizeofstrlen 的对比

在这里插入图片描述

2. 数组和指针笔试题解析

2.1 一维数组

int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a+0));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof(a[1]));
printf("%d\n",sizeof(&a));
printf("%d\n",sizeof(*&a));
printf("%d\n",sizeof(&a+1));
printf("%d\n",sizeof(&a[0]));
printf("%d\n",sizeof(&a[0]+1));

在对代码进行分析之前我们先回忆几个重要知识点。

  • 数组名通常代表数组首元素的地址,但是有例外。

  • sizeof(数组名) ,此时数组名代表整个数组。

  • &数组名 ,此时取出的地址为整个数组的地址。

在对上面的知识点进行回忆后,我们便可以对代码进行分析。

int a[] = {1,2,3,4};
printf("%d\n",sizeof(a)); //为数组名的特殊用法,代表整个数组,结果为 16
printf("%d\n",sizeof(a+0)); //数组名并未单独放在 sizeof 内部,所以代表数组首元素,结果为 4/8
printf("%d\n",sizeof(*a)); //数组名为首元素地址,对其解引用后代表首元素,首元素类型为整形,结果为 4
printf("%d\n",sizeof(a+1));//数组首元素地址 + 1,仍为地址,结果为 4/8
printf("%d\n",sizeof(a[1]));//数组中第二个元素的字节数大小,结果为 4
printf("%d\n",sizeof(&a));//取出整个数组的地址,但仍为地址,结果为 4/8
printf("%d\n",sizeof(*&a));//先取出整个数组的地址,然后再解引用,仍然相当于直接求整个数组的字节数大小,结果为 16
printf("%d\n",sizeof(&a+1));//取出整个数组的地址然后 + 1,仍为地址,结果为 4/8
printf("%d\n",sizeof(&a[0]));//求数组第一个元素的地址的字节数大小,仍为地址,结果为 4/8 
printf("%d\n",sizeof(&a[0]+1));//相当于求第二个元素的地址字节数大小,仍为地址,结果为 4/8

在这里插入图片描述

这里作者的运行环境为 32位,故地址的字节数为 4。

2.2 字符数组

练习 1:

char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr+0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr+1));
printf("%d\n", sizeof(&arr[0]+1));

与上面一样的步骤,接下来我们对代码进行解析。

char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr));//计算整个数组的字节数大小,为 6
printf("%d\n", sizeof(arr+0));//计算数组首元素地址的大小,为 4/8
printf("%d\n", sizeof(*arr));//计算首元素的大小,元素为字符类型,为 1
printf("%d\n", sizeof(arr[1]));//计算首元素的大小,元素为字符类型,为 1
printf("%d\n", sizeof(&arr));//取出整个数组的地址,结果仍为地址,字节数为 4/8
printf("%d\n", sizeof(&arr+1));//取出整个数组的地址,然后 + 1,相当于跳过了整个数组后的地址,仍为地址,字节数为 4/8
printf("%d\n", sizeof(&arr[0]+1));//相当于取出的是整个数组第二个元素的地址,仍为地址,字节数为 4/8

在这里插入图片描述

练习 2:

char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));

解析:

char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", strlen(arr));//从首元素地址开始计算,由于 strlen 的特性,数组中并没有存储 \0,最终结果为随机值
printf("%d\n", strlen(arr+0));//从首元素开始计算,与上面的解析同理,最终结果仍为随机值
printf("%d\n", strlen(*arr));//*arr 解析出的结果为 a,a 的 ASCII值为 97,strlen 会将这个值作为地址进行访问,最终结果为非法访问
printf("%d\n", strlen(arr[1]));//arr[1] 解析出的结果为 b,与上面的解析同理,最终结果仍为非法访问
printf("%d\n", strlen(&arr));//从数组的地址开始计算,结果为随机值,与上面的解析同理
printf("%d\n", strlen(&arr+1));//从跳过整个数组的第一个地址开始计算,结果为随机值,与上面解析同理
printf("%d\n", strlen(&arr[0]+1));//从数组的第二个元素地址开始计算,结果为随机值,与上面解析同理

因有非法访问,所以无法插入实机演示结果

练习 3:

char arr[] = "abcdef";
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr+0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr+1));
printf("%d\n", sizeof(&arr[0]+1));

解析:

char arr[] = "abcdef";
printf("%d\n", sizeof(arr));//求整个数组的字节数,结果为 7
printf("%d\n", sizeof(arr+0));//求数组首元素地址的字节数,结果为 4/8
printf("%d\n", sizeof(*arr));//求首元素的字节数,元素类型为字符,结果为 1
printf("%d\n", sizeof(arr[1]));//求数组第二个元素的字节数,元素类型为字符,结果为 1
printf("%d\n", sizeof(&arr));//求整个数组地址的字节数,结果为 4/8
printf("%d\n", sizeof(&arr+1));//求跳过整个数组后第一个地址的字节数,结果为 4/8
printf("%d\n", sizeof(&arr[0]+1));//求数组第二个地址的字节数,结果为 4/8

在这里插入图片描述

练习 4:

char arr[] = "abcdef";
printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));

解析:

char arr[] = "abcdef";
printf("%d\n", strlen(arr));//从首元素地址开始计算,结果为 6
printf("%d\n", strlen(arr+0));//从首元素地址开始计算,结果为 6
printf("%d\n", strlen(*arr));//*arr 地结果为 a ,对应地 ASCII值 为 97,strlen 会将 97 作为地址进行访问,结果为非法访问
printf("%d\n", strlen(arr[1]));//结果为非法访问,arr[1] 结果为 b,其余解析与上面一样
printf("%d\n", strlen(&arr));//从整个数组地地址开始计算,结果为 6
printf("%d\n", strlen(&arr+1));//从跳过整个数组地第一个地址开始计算,结果为随机值
printf("%d\n", strlen(&arr[0]+1));//从数组地第二个元素地址开始计算,结果为 5

练习 5:

char *p = "abcdef";
printf("%d\n", sizeof(p));
printf("%d\n", sizeof(p+1));
printf("%d\n", sizeof(*p));
printf("%d\n", sizeof(p[0]));
printf("%d\n", sizeof(&p));
printf("%d\n", sizeof(&p+1));
printf("%d\n", sizeof(&p[0]+1));

解析:

char *p = "abcdef";
printf("%d\n", sizeof(p));//计算指针 p 的大小,结果为 4/8
printf("%d\n", sizeof(p+1));//计算第二字符 b 的地址大小,结果为 4/8
printf("%d\n", sizeof(*p));//计算第一元素 a 的字节大小,结果为 1
printf("%d\n", sizeof(p[0]));//计算第一元素 a 的字节大小,结果为 1
printf("%d\n", sizeof(&p));//计算指针 p 的地址大小,结果为 4/8
printf("%d\n", sizeof(&p+1));//计算跳过指针 p 地址后的第一个地址大小,结果为 4/8
printf("%d\n", sizeof(&p[0]+1));//计算 b 的地址大小,结果为 4/8

在这里插入图片描述

练习 6:

char *p = "abcdef";
printf("%d\n", strlen(p));
printf("%d\n", strlen(p+1));
printf("%d\n", strlen(*p));
printf("%d\n", strlen(p[0]));
printf("%d\n", strlen(&p));
printf("%d\n", strlen(&p+1));
printf("%d\n", strlen(&p[0]+1));

解析:

char *p = "abcdef";
printf("%d\n", strlen(p));//从 p 开始计算,结果为 6
printf("%d\n", strlen(p+1));//从数组的第二个元素开始计算,结果为 5
printf("%d\n", strlen(*p));//*p 解析为 a ,ASCII值 为 97,strlen 会将 97 当作地址进行访问,结果为非法访问
printf("%d\n", strlen(p[0]));//p[0] 解析为 a,结果为非法访问
printf("%d\n", strlen(&p));//从 p 的地址开始计算,结果为随机值
printf("%d\n", strlen(&p+1));//从跳过 p 的地址后的第一个地址进行计算,结果为随机值
printf("%d\n", strlen(&p[0]+1));//从数组的第二个元素的地址开始计算,结尾为 5

2.3 二维数组

int a[3][4] = {0};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a[0][0]));
printf("%d\n",sizeof(a[0]));
printf("%d\n",sizeof(a[0]+1));
printf("%d\n",sizeof(*(a[0]+1)));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof(*(a+1)));
printf("%d\n",sizeof(&a[0]+1));
printf("%d\n",sizeof(*(&a[0]+1)));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a[3]));

解析:

int a[3][4] = {0};
printf("%d\n",sizeof(a));//计算整个 a 数组的字节数大小,结果为 48
printf("%d\n",sizeof(a[0][0]));//计算第一行第一个元素的大小,元素为整形类型,结果为 4
printf("%d\n",sizeof(a[0]));//a[0] 解析为 a数组 第一行的数组名,计算整个第一行的字节数大小,结果为 16
printf("%d\n",sizeof(a[0]+1));//a[0]+1 解析为 &a[0][1] ,计算第一行第二个元素地址的大小,结果为 4/8
printf("%d\n",sizeof(*(a[0]+1)));//*(a[0]+1) 解析为 a[0][1],计算第一行第二个元素的大小,结果为 4
printf("%d\n",sizeof(a+1));//计算 a数组 第二行地址的大小,结果为 4/8
printf("%d\n",sizeof(*(a+1)));//计算 a数组 第二行的大小,结果为 16
printf("%d\n",sizeof(&a[0]+1));//取出跳过第一行后的第一个地址,也就是第二行的地址,结果为 4/8
printf("%d\n",sizeof(*(&a[0]+1)));//取出第二行的地址然后解引用,求第二行的大小,结果为 16
printf("%d\n",sizeof(*a));// a 为首元素 a[0] 的地址,对其进行解引用表示第一行,求第一行的大小,结果为16 
printf("%d\n",sizeof(a[3]));//a 数组并没有第四行,最终结果为报错

到此我们再对数组名的意义进行总结:

  1. sizeof(数组名) ,这里的数组名表示整个数组,计算的是整个数组的大小。

  2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。

  3. 除此之外所有的数组名都表示首元素的地址

3. 指针运算笔试题解析

3.1 题目1:

#include <stdio.h>
int main()
{int a[5] = { 1, 2, 3, 4, 5 };int *ptr = (int *)(&a + 1);printf( "%d,%d", *(a + 1), *(ptr - 1));return 0;
}

解析:

int *ptr = (int *)(&a + 1);

对于指针 ptr 所指向的地址 &a + 1 代表这跳过整个 a 数组地址后的第一个地址。

所以 *(ptr - 1) 就是对指针 ptr 的前一个地址进行解引用,也就是数组中最后一个元素的地址进行解引用。

对于 *(a + 1)a + 1 表示数组中第二个元素的地址,所以 *(a + 1) 表示对第二个元素的地址进行解引用。

最终的结果为 2,5

在这里插入图片描述

3.2 题目2

//在X86环境下 
//假设结构体的⼤⼩是20个字节 
//程序输出的结果是啥? 
struct Test
{int Num;char *pcName;short sDate;char cha[2];short sBa[4];
}*p = (struct Test*)0x100000;
int main()
{printf("%p\n", p + 0x1);printf("%p\n", (unsigned long)p + 0x1);printf("%p\n", (unsigned int*)p + 0x1);return 0;
}

解析:

要解出正确答案,我们就要清楚 指针 +1 ,会产生怎样的操作,指针 + 1 会根据指针不同的步长,从而跳过不同的字节数,

所以对于 p + 0x1 跳过的就是结构体的字节数,结构体 Test 的字节数大小为 20 ,所以 p + 0x1 跳过的字节数大小为 20,

20 转换为 16进制 为 0x14,所以结果为 0x100014,同样的对于 (unsigned int*)p + 0x1 我们跳过则为 unsigned int 类型的字节数,

为 4 字节,所以最终结果为 0x100004,但是在(unsigned long)p + 0x1 p 被转换成了无符号长整形类型,不再为指针,

所以这时的 +1 就不能再遵循指针的规则,而是遵循整形的算术规则,正常 + 1,结果为 0x100001,

对于 X86 环境下的地址显示,32位系统在显示地址时最多能显示 8 位,所以我们要在结果前补上两个 0,

最终结果为:

在这里插入图片描述

3.3 题目3

#include <stdio.h>
int main()
{int a[3][2] = { (0, 1), (2, 3), (4, 5) };int *p;p = a[0];printf( "%d", p[0]);return 0;
}

解析:

对于这道题,我们一定要辨析好二维数组的初始化方式,题目中二维数组的初始化分组部分使用的是 () ,而并非 {}

而小括号内部使用了逗号表达式,逗号表达式返回的是表达式中最后一个值,

所以实际二维数组的初始化其实应该为这样 int a[3][2] = { 1, 3, 5 };

所以指针 pp = a[0]; 取到的是数组第一行地址,第一行包含的元素为 1,3,所以 p[0] 取到的第一个元素为 1,

最终结果为 1 。

在这里插入图片描述

3.4 题目4

//假设环境是x86环境,程序输出的结果是啥? 
#include <stdio.h>
int main()
{int a[5][5];int(*p)[4];p = a;printf( "%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);return 0;
}

解析:

首先我们对于 p[4][2] 进行分析, p[4][2] 相当于 *(p + 4)[2],这里就和指针步长扯上关系了,指针 p 的类型为 int [4]

所以每次 + 1 时,跳过 4 个整形类型的字节,因为 p = a ,所以指针 p 与 数组 a 的首元素地址时一样的,

所以 p[4][2] 实际上跳过了 a 数组的 18 个元素,而 a[4][2] 跳过了数组的 22 个元素,指针 - 指针计算出的是指针之间的元素个数,

所以结果为 -4,但对于第一个 -4 我们要化成十六进制地址的形式,我们先写出 -4 的源码,然后求出补码。

源码:10000000 00000000 00000000 00000100
反码:11111111 11111111 11111111 11111011
补码:11111111 11111111 11111111 11111100
补码的十六进制:FFFFFFFC

所以最终的结果为:
在这里插入图片描述

3.5 题目5

#include <stdio.h>
int main()
{int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };int *ptr1 = (int *)(&aa + 1);int *ptr2 = (int *)(*(aa + 1));printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1));return 0;
}

解析:

我们先对指针ptr1 进行解析,int *ptr1 = (int *)(&aa + 1); 其中 &aa + 1 是跳过整个 aa 数组后的第一个整形指针地址

*ptr2 = (int *)(*(aa + 1)); 其中 *(aa + 1)aa 表示数组首元素地址,为 &aa[0] ,+ 1 后指向 &aa[1]

所以指针 ptr2 实际代表数组 aa 的第二行。因为 ptr1,ptr2 指针的类型都为整形指针类型,所以 +1,-1 都只会跳过一个整形的地址

所以 *(ptr1 - 1) 是对 aa 数组的最后一个元素地址进行解引用,*(ptr2 - 1) 是对数组第一行最后一个元素地址进行解引用

所以最终的结果为:

在这里插入图片描述

3.6 题目6

#include <stdio.h>
int main()
{char *a[] = {"work","at","alibaba"};char**pa = a;pa++;printf("%s\n", *pa);return 0;
}

解析:

我们先来分析字符指针数组 aa 的成员有三个,依次分别为 workatalibaba

因为 char**pa = a; ,所以指针 pa 指向数组 a 的首元素地址,pa++; 相当于 a[0] + 1 ,数组 a 的第一个元素为 work ,+1后

指向第二个元素 at ,所以最终的打印结果为 at

在这里插入图片描述

3.7 题目7

#include <stdio.h>
int main()
{char *c[] = {"ENTER","NEW","POINT","FIRST"};char**cp[] = {c+3,c+2,c+1,c};char***cpp = cp;printf("%s\n", **++cpp);printf("%s\n", *--*++cpp+3);printf("%s\n", *cpp[-2]+3);printf("%s\n", cpp[-1][-1]+1);return 0;
}

解析:

这里我们先将所有结构以图标的形式呈现出来,以便我们更好地进行观察。
在这里插入图片描述

 	printf("%s\n", **++cpp);

现在我们再对代码进行分析,**++cpp ,其中 cpp 指针指向 cp 的首元素,在 ++ 后指向了 cp 的第二个元素 c + 2

所以结果为 POINT

 	printf("%s\n", *--*++cpp+3);

*--*++cpp+3++cpp 此时指向 cp 的第三个元素 c + 1 ,解引用符号的结合顺序更高,先结合

再与 -- 符号进行结合,此时就变成了 *--(c + 1) + 3 ,结合后变成 *c + 3 ,此时就是对 c 数组的首元素,

ENTER 的第四个元素开始输出,最终结果为 ER

 	printf("%s\n", *cpp[-2]+3);

此时 cpp 指针在与两个自增符号进行结合后,已经指向了 cp 数组的第三个元素,所以 cpp[-2] 指向了 cp 数组的第一个元素,

就表示为 *cp+3,也就是从 FIRST 的第四个字符开始输出,最终结果为 ST

 	printf("%s\n", cpp[-1][-1]+1);

此时的 cpp[-1] 代表 cp 的第二个元素,简化为 cp[1][-1]+1 再次简化为 *((c + 2) - 1) + 1 也就是 *(c + 1) + 1

NEW 的第二个字符开始输出,结果为 EW

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.tpcf.cn/news/914468.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

8:从USB摄像头把声音拿出来--ALSA大佬登场!

前言前面的章节我们从认识摄像头开始&#xff0c;逐渐认识的YCbCr&#xff0c;并对其进行了H264的编码以及MP4封装。整个过程中&#xff0c;我们大致使用了V4L2和FFmpeg这两个重量级工具&#xff0c;就像我们前面章节所讲&#xff0c;V4L2只是给图像做服务的&#xff0c;并不参…

Linux 命令:useradd

Linux useradd 命令详细教程 useradd 是 Linux 系统中用于创建新用户账户的基础命令&#xff0c;它通过配置文件&#xff08;如 /etc/passwd、/etc/shadow&#xff09;和默认设置自动完成用户创建流程。本文将详细介绍其用法、参数及相关配置。资料已经分类整理好&#xff1a;h…

Pytest之收集用例规则与运行指定用例

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 小伙伴们大家好呀&#xff0c;今天笔者会给大家讲解一下pytest是如何收集我们写好的用例&#xff1f;我们又有哪些方式来运行单个用例或者批量运行用例呢&#xff…

qt 使用memcpy进行内存拷贝时注意的问题

int offset sizeof(st_target_data);// 预先分配足够空间this->featureData.resize(offsetsize);// 再执行拷贝memcpy(this->featureData.data()offset, dataa, size);注意 一定要在mencpy之前 使用resize分配足够的空间&#xff0c;否则在方法退出时候会闪退&#xff…

微调性能赶不上提示工程怎么办?Can Gradient Descent Simulate Prompting?——论文阅读笔记

今天速读一篇文章 Can Gradient Descent Simulate Prompting? 一句话总结 针对【新知识应用的场景里&#xff0c;FT效果往往追不上ICL】这个情况&#xff0c;作者引入MAML的思想↓ 内圈让模型学习新知识形成知识FT模型&#xff1b; 外圈通过最小化ICL和知识FT模型的KL散度&…

从“直觉抢答”到“深度思考”:大模型的“慢思考”革命,思维链、树、图如何让AI越来越像人?

注&#xff1a;此文章内容均节选自充电了么创始人&#xff0c;CEO兼CTO陈敬雷老师的新书《GPT多模态大模型与AI Agent智能体》&#xff08;跟我一起学人工智能&#xff09;【陈敬雷编著】【清华大学出版社】 GPT多模态大模型与AI Agent智能体书籍本章配套视频课程【陈敬雷】 文…

Android系统的问题分析笔记 - Android上的调试方式 debuggerd

debuggerd 是 Android 系统中的一个重要调试工具&#xff0c;主要用于生成进程崩溃时的核心转储&#xff08;core dump&#xff09;和调试信息&#xff08;如堆栈跟踪&#xff09;。以下是关于 debuggerd 的详细说明&#xff1a; 1. 基本功能 崩溃分析&#xff1a;当 Native 进…

python 双下划线开头函数

在 Python 里&#xff0c;双下划线开头的函数&#xff08;准确地说是方法&#xff09;有着特殊的用途和意义。下面为你详细介绍相关内容&#xff1a; 1. 类的特殊方法&#xff08;魔术方法&#xff09; 以双下划线开头和结尾的方法&#xff0c;被称为特殊方法或者魔术方法&…

VyOS起步指南:用Docker快速搭建网络实验环境

文章目录1. VyOS是什么&#xff1f;为什么选择它&#xff1f;2. 五分钟快速部署&#xff1a;Docker方案3. 进入容器&#xff1a;初探VyOS世界4. 核心操作&#xff1a;像开发者一样思考5. 踩坑提醒&#xff1a;新手常见问题6. 结语&#xff1a;网络即代码的未来1. VyOS是什么&am…

动态规划理论基础,LeetCode 509. 斐波那契数 LeetCode 70. 爬楼梯 LeetCode 746. 使用最小花费爬楼梯

动态规划理论基础动态规划&#xff0c;英文&#xff1a;Dynamic Programming&#xff0c;简称DP&#xff0c;如果某一问题有很多重叠子问题&#xff0c;使用动态规划是最有效的。所以动态规划中每一个状态一定是由上一个状态推导出来的&#xff0c;这一点就区分于贪心&#xff…

暑期自学嵌入式——Day02(C语言阶段)

点关注不迷路哟。你的点赞、收藏&#xff0c;一键三连&#xff0c;是我持续更新的动力哟&#xff01;&#xff01;&#xff01; 主页&#xff1a; 一位搞嵌入式的 genius-CSDN博客https://blog.csdn.net/m0_73589512?spm1000.2115.3001.5343 目录 Day02→数据类型&#xf…

如何单独安装设置包域名

前言 在 npm 中&#xff0c;直接通过 package-lock.json 无法单独设置包的安装地址&#xff0c;因为该文件是自动生成的依赖关系锁定文件。但你可以通过以下方法间接实现&#xff1a; 一、在 package.json 中指定包来源&#xff08;推荐&#xff09; 在 package.json 的 depend…

存储过程探秘:数据库编程的艺术

文章目录存储过程语法格式BEGIN...END语句块DECLARE&#xff08;声明局部变量&#xff09;流控制语句if函数批处理操作测试2测试3存储过程与函数的关系存储过程 MYSQL的存储过程是一组预处理的SQL语句&#xff0c;可以像函数一样在数据库中进行存储和调用。 它们允许在数据库…

非阻塞写入核心:asyncio.StreamWriter 的流量控制与数据推送之道

在 asyncio 的异步编程框架中&#xff0c;如果说 asyncio.StreamReader 是你异步应用的数据输入管道&#xff0c;那么 asyncio.StreamWriter 就是你异步应用的数据输出管道。它是一个至关重要的组件&#xff0c;让你能够方便、高效且非阻塞地向连接的另一端&#xff08;如 TCP …

控制台打开mysql服务报错解决办法

控制台打开mysql服务报错解决办法这个MySQL错误表示访问被拒绝&#xff0c;通常是因为没有提供正确的用户名和密码。以下是几种解决方法&#xff1a; 方法1&#xff1a;指定用户名和密码连接 mysql -u root -p然后输入root用户的密码。 方法2&#xff1a;如果忘记了root密码&am…

Unsloth 实战:DeepSeek-R1 模型高效微调指南(下篇)

食用指南 本系列因篇幅原因拆分为上下两篇&#xff1a; 上篇以基础环境搭建为主&#xff0c;介绍了 Unsloth 框架、基座模型下载、导入基座模型、数据集下载/加载/清洗、SwanLab 平台账号注册。 下篇&#xff08;本文&#xff09;以实战微调为主&#xff0c;介绍预训练、全量…

Ubuntu安装Jenkins

Ubuntu安装Jenkins方法1&#xff1a;使用官方的Jenkins仓库1. 添加Jenkins仓库2. 更新软件包列表3. 安装Jenkins4. 启动Jenkins服务5. 设置Jenkins开机启动6. 查找初始管理员密码7. 访问Jenkins方法2&#xff1a;使用Snap包&#xff08;适用于较新的Ubuntu版本&#xff09;1. 安…

ubuntu22.04下配置qt5.15.17开发环境

自从qt5.15版本开始&#xff0c;不再提供免费的离线安装包&#xff0c;只能通过源码自行编译。刚好最近需要在ubuntu22.04下配置qt开发环境&#xff0c;于是写篇文章记录配置的过程。 其实一开始是想配置qt5.15.2的&#xff0c;但是在编译配置参数这一步骤中出现如下报错 em…

S7-1200 与 S7-300 CPS7-400 CP UDP 通信 Step7 项目编程

S7-1200 CPU 与S7-300 CP STEP7 UDP通信S7-1200 与 S7-300 CP 之间的以太网通信可以通过 UDP 协议来实现&#xff0c;使用的通信指令是在S7-1200 CPU 侧调用通信-开放式用户通信TSEND_C&#xff0c;TRCV_C指令或TCON&#xff0c;TDISCON&#xff0c;TUSEND&#xff0c;TURCV 指…

基于YOLOv11的无人机目标检测实战(Windows环境)

1. 环境搭建 1.1 硬件与操作系统 操作系统&#xff1a;Windows 11 CPU&#xff1a;Intel i7-9700 GPU&#xff1a;NVIDIA RTX 2080&#xff08;8GB显存&#xff09; 1.2 安装CUDA和cuDNN 由于YOLOv11依赖PyTorch的GPU加速&#xff0c;需要安装CUDA和cuDNN&#xff1a; 安…