先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Golang全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip1024b (备注go)
正文
请输入第二个字符串:abc
返回的值:1**🎄第三次的运行结果🎄
请输入第一个字符串:abc
请输入第二个字符串:abcd
返回的值:-1
🎄创建自定义函数实现strcmp()🎄
思路很简单其实 👇
相等为0、大于返回1、小于返回-1,重要的点注释其实都是写了的。
示例代码如下:👇
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
int my_strcmp(const char *str1, const char *str2)
{
assert(str1 && str2 != NULL);
while (*str1 == str2)
{
//判断相等情况,有一个相等就代表str2也是一样的
if (*str1 == ‘\0’)
{
return 0;
}
*str1++;
*str2++;//自增比较
}
if (*str1 > *str2)
return 1;//大于
else
return -1;//小于
//其实还有一种更简单的方法,大于小于。
//return *str1 - *str2;
//这个是指针减指针的概念,前面介绍有说过!
}
int main()
{
char str1[20] = { 0 };
char str2[20] = { 0 };
puts(“请输入第一个字符串:”);
scanf(“%s”, &str1);
puts(“请输入第二个字符串:”);
scanf(“%s”, &str2);
int ret = my_strcmp(str1, str2);
printf(“返回的值:%d\n”,ret);
return 0;
}
🎄第一次的运行结果🎄
请输入第一个字符串:abc
请输入第二个字符串:abc
返回的值:0🎄第二次的运行结果🎄
请输入第一个字符串:abcd
请输入第二个字符串:abc
返回的值:1🎄第三次的运行结果🎄
请输入第一个字符串:abc
请输入第二个字符串:abcd
返回的值:-1
以上四个字符串函数是我们学C语言必须要掌握的,包括模拟字符串函数的实现也是有必要去实现的,这样的好处可以锻炼我们对函数库的了解,增强自己写代码的能力,建议反复观看,牢记掌握这四个字符串函数~
🔧 限制 🔧
上述 strcpy、strcat、strcmp、长度是不受限制的字符串函数
而下面所说的是长度受限制的字符串函数了>>>
起始记住下面这个只需要在上面的基础上加上个str后面+n即可(^∀^●)ノシ
下面所介绍的3种函数相对比上的使用要更加安全,但并不是绝对安全。
🎋strncpy() - 复制字符串(受长度限制)🎋
这个函数实际上和**strcpy()的功能是一模一样的,唯一不同的是在于strncpy()**多了一个参数,那个参数就是受长度所限制的。
strncpy()函数的声明方式如下****👇
char *strncpy(char *dest, const char *src, size_t n)
把 src 所指向的字符串复制到 dest,最多复制 n 个字符。
当 src 的长度小于 n 时,dest 的剩余部分将用空字节填充。
dest → 指向用于存储复制内容的目标数组。
src → 要复制的字符串。
n → 要从源中复制的字符数。
Size_t→是一个无符号整型类型。
该函数最终返回所复制的字符串。
🎋strncpy()函数代码示例🎋
使用**strncpy()**函数代码示例如下 👇
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
int main(void)
{
char str1[20] = “helloC”;
char str2[] = “HELLO”;
printf(“字节=%d\n”, sizeof(str2));
printf(“str = %s\n”,strncpy(str1, str2, sizeof(str2)));
return 0;
}
运行结果如下 👇
字节 = 6
str = HELLO
sizeof(str2) 相当于为 6个字节,因为 char 为一个字节 里面元素有6个包括’\0’
✅来一起看看调试结果说不定会更好点✅
我们再来看下一个例子👇
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
int main(void)
{
char str1[20] = “helloC”;
char str2[] = “HELLO”;
printf(“字节=%d\n”, sizeof(char));
printf(“str = %s\n”, strncpy(str1, str2, sizeof(char)));
return 0;
}
运行结果如下 👇
字节 = 1
str = HELLO
sizeof(char) 结果为一个字节,因为 char 类型大小为一个字节!
✅来一起看看调试结果说不定会更好点✅
🎋strncpy()源程序实现🎋
示例代码如下:👇
char * __cdecl strncpy (
char * dest,
const char * source,
size_t count
)
{
char *start = dest;
while (count && (*dest++ = source++)) / copy string */
count–;
if (count) /* pad out with zeroes */
while (–count)
*dest++ = ‘\0’;
return(start);
}
🎋strncat() - 连接字符串(受长度限制)🎋
strncat()函数的声明方式如下****👇
char *strncat(char *dest, const char *src, size_t n)
把 src 所指向的字符串追加到 dest 所指向的字符串的结尾,直到 n 的长度为止。
dest→指向目标数组,该数组包含了一个 C语言 字符串,且足够容纳追加后的字符串,包括额外的空字符。
src→要追加的字符串。
n→追加的最大字符串。
Size_t→是一个无符号(unisgned int)整型类型。
该函数返回一个指向最终的目标字符串 dest 的指针。
注意:这里的返回值的指针类型可以是void也可以是char *🧨
🎋strncat()函数代码示例🎋
使用**strncpy()**函数代码示例如下 👇
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
int main(void)
{
char str1[20];
char str2[20];
strcpy(str1, “Cyuyan”);
strcpy(str2, “yyds”);
printf(strncat(str1, str2, 5));//追加字符串!
return 0;
}
运行结果如下 👇
Cyuyanyyds
✅来一起看看调试结果说不定会更好点✅
我们把 Size_t 参数改为④来看看
printf(strncat(str1, str2, 4));
运行结果依旧和上述结果是一样的。
🎋strncat()源程序实现🎋
示例代码如下:👇
char * __cdecl strncat (
char * front,
const char * back,
size_t count
)
{
char *start = front;
while (*front++)
;
front–;
while (count–)
if (!(*front++ = *back++))
return(start);
*front = ‘\0’;
return(start);
}
🎋strncmp() - 比较字符串(受长度限制)🎋
strncmp()函数的声明方式如下****👇
int strncmp(const char *str1, const char *str2, size_t n)
str1 → 要进行比较的第一个字符串。
str2 → 要进行比较的第二个字符串。
n → 要比较的最大字符数。
这个函数开始比较每个字符串的第一个字符。如果它们相等,则继续执行下面的对,直到字符不同,直到达到一个结束的空字符,或直到两个字符串中的num字符匹配,以先发生的为准。
如果返回值 < 0,则表示 str1 小于 str2。
如果返回值 > 0,则表示 str2 小于 str1。
如果返回值 = 0,则表示 str1 等于 str2。
🎋strncmp()函数代码示例🎋
使用**strncpy()**函数代码示例如下 👇
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
int main(void)
{
char str1[20];
char str2[20];
strcpy(str1, “Cyuyan”);
strcpy(str2, “Cyuyanyyds”);
printf(“%d”, strncmp(str1, str2, 6));
return 0;
}
运行结果如下 👇
0 → 代表****str1 等于 str2
如果这里没有追加字符串的话结果就会是-1,因为str1<str2。正因为我们追加了字符为6,它才可以是str1 = str2。
🎋strncmp()源程序实现🎋
示例代码如下:👇
int __cdecl strncmp
(
const char *first,
const char *last,
size_t count
)
{
size_t x = 0;
if (!count)
{
return 0;
}
/*
- This explicit guard needed to deal correctly with boundary
- cases: strings shorter than 4 bytes and strings longer than
- UINT_MAX-4 bytes .
/
if( count >= 4 )
{
/ unroll by four */
for (; x < count-4; x+=4)
{
first+=4;
last +=4;
if (*(first-4) == 0 || *(first-4) != (last-4))
{
return((unsigned char *)(first-4) - *(unsigned char *)(last-4));
}
if (*(first-3) == 0 || *(first-3) != (last-3))
{
return((unsigned char *)(first-3) - *(unsigned char *)(last-3));
}
if (*(first-2) == 0 || *(first-2) != (last-2))
{
return((unsigned char *)(first-2) - *(unsigned char *)(last-2));
}
if (*(first-1) == 0 || *(first-1) != (last-1))
{
return((unsigned char *)(first-1) - *(unsigned char *)(last-1));
}
}
}
/* residual loop */
for (; x < count; x++)
{
if (*first == 0 || *first != last)
{
return((unsigned char *)first - *(unsigned char *)last);
}
first+=1;
last+=1;
}
return 0;
}
🎍strstr() - 在一个字符串中查找另外一个字符串🎍
strstr()函数的声明方式如下****👇
char *strstr(const char *haystack, const char *needle)
在字符串 haystack 中查找第一次出现字符串 needle 的位置,不包含终止符 ‘\0’。
haystack→ 要被检索的 C 字符串。
needle→ 在 haystack 字符串内要搜索的小字符串。
该函数返回在 haystack 中第一次出现 needle 字符串的位置,如果未找到则返回 null。
注意:这里的返回值的指针类型可以是void也可以是char *🧨
🎍strstr()函数代码示例🎍
题目:在arr1中查找是否包含arr2中的数组。要求使用 strstr() 库函数。
使用**strncpy()**函数代码示例如下 👇
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
int main(void)
{
char arr1[20] = “abcdef”;
char arr2[20] = “bcd”;
char ret = strstr(arr1, arr2);
if (ret == NULL)
{
printf(“没找到!\n”);
}
else
{
printf(“找到了→%s\n”, ret);
}
return 0;
}
运行结果如下 👇
找到了→bcdef
那没找到的情况下,我们把 arr2 数组修改下。其它值不变🎉
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<assert.h>
int main(void)
{
char arr1[20] = “abcdef”;
char arr2[20] = “bf”;
char *ret = strstr(arr1, arr2);
if (ret == NULL)
{
printf(“没找到%s!\n”,ret);
}
else
{
printf(“找到了→%s\n”, ret);
}
return 0;
}
运行结果如下 👇
找不到(null)!
对比之下,你发现了区别了没👀
🎍创建自定义函数实现strstr()🎍
分析思路🧐
其实很容易,我们只需要拿首字符地址进行比较相等的话返回,不相等的时候进行自增++(注意:这里自增++只需要str1进行++而str2依旧拿首元素地址跟它进行比较),再进行比较直到’\0’,没有就返回空指针NULL。如果str1有元素和str2首元素地址匹配上了的话那么就继续往后走~但是,这个仅仅是假设 abcd 和 bcd 所适用的场景。如果是 bbbc 和 bbc,这种怎么办?是不是发现用这种思路不行,下面就用代码来讲解下怎么实现。
示例代码如下:👇
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<assert.h>
char *my_strstr(const char str1, const char str2)
{
assert(str1 && str2 != NULL);
const char s1 = NULL;
const char s2 = NULL;//在不知道赋值什么的情况下直接赋值空指针。
if (str2 == ‘\0’)
return (char)str1;//假设str2首元素地址为空字符串直接返回str1
while (*str1)
{
s1 = str1;
s2 = str2;
while (*s1 && *s2 != ‘\0’ && (*s1 == *s2))
{
s1++;
s2++;
}
if (s2 == ‘\0’)
{
return (char)str1;//注意返回类型强制转换!
}
str1++;
}
return NULL;
}
int main(void)
{
char arr1[20] = “abbcdef”;
char arr2[20] = “bc”;
char *ret = my_strstr(arr1, arr2);
if (ret == NULL)
{
printf(“没找到%s!\n”,ret);
}
else
{
printf(“找到了→%s\n”, ret);
}
return 0;
}
运行结果如下 👇
bcdef
这个代码的实现相对来说比上面的代码都要比较复杂,希望各位理解了可以自己上手做下,因为自有当你把代码打出来了,那么才是你学到的知识。
🎍strtok() - 切割字符串🎍
这个函数比较难懂,比较奇观,使用场景也不多见。
strtok()函数的声明方式如下****👇
char * strtok ( char * str, const char * delimiters );
对该函数的调用序列将str分割成标记,标记是由连续字符组成的序列,由分隔符的任何字符分隔。
在第一次调用时,函数期望一个C的字符串作为str的参数,它的第一个字符被用作扫描标记的起始位置。在随后的调用中,该函数期望得到一个空指针,并使用最后一个标记结束后的位置作为新的扫描起始位置。
要确定标记的开始和结束,函数首先从开始位置扫描分隔符中不包含的第一个字符(它成为标记的开始)。然后从标记的这个开头开始扫描分隔符中包含的第一个字符,这个字符成为标记的结尾。如果找到终止的空字符,扫描也会停止。
这个标记的末尾会被一个空字符自动替换,而该标记的开头则由函数返回。
一旦在strtok调用中找到str的终止空字符,所有后续对该函数的调用(以空指针作为第一个参数)都返回一个空指针。
找到最后一个令牌的位置由函数保存在内部,以便下次调用时使用(为了避免数据竞争,不需要特定的库实现)。
str→要截断的C字符串。注意,这个字符串被分解成更小的字符串(令牌)。
或者,可以指定一个空指针,在这种情况下,函数继续扫描之前对该函数的成功调用结束的地方。delimiters→包含分隔符字符的C字符串。这些在不同的调用之间可能是不同的。
返回值:如果找到令牌,则指向令牌开头的指针。否则,为空指针。当被扫描的字符串到达字符串的末尾(即一个空字符)时,总是返回一个空指针。
🎍strtok()函数代码示例🎍
题目:把字符串"Hello.Cyuyan.yyds",.之前语句进行分割最后进行打印。用strtok字符串函数实现。
使用**strtok()**函数代码示例如下 👇
#include <stdio.h>
#include <string.h>
int main(void)
{
char str[] = “Hello.Cyuyan.yyds”;
printf(“yiduanhua|%s|dezifu\n”, str);
char * pch=strtok(str, “.”);
while (pch != NULL)
{
printf(“%s\n”, pch);
pch = strtok(NULL, “.”);
}
return 0;
}
运行结果如下 👇
Hello.Cyuyan.yyds
Hello
Cyuyan
yyds
不知道你是否明白了( ゚д゚)つ
在这里思考下,那么它使用什么进行保存的呢?就是这个函数它能够记住地址,其实这个只需要用到一个关键字 static 静态局部变量就可以完美实现这种类似于记忆功能,把生命周期延长,出了函数的范围内不会销毁保留原来的值。
🎍strtok()源程序实现🎍
示例代码如下:👇
/***
*strtok.c - tokenize a string with given delimiters
*
-
Copyright (c) Microsoft Corporation. All rights reserved.
*Purpose:
-
defines strtok() - breaks string into series of token
-
via repeated calls.
*******************************************************************************/
#include <cruntime.h>
#include <string.h>
#ifdef _SECURE_VERSION
#include <internal.h>
#else /* _SECURE_VERSION /
#include <mtdll.h>
#endif / _SECURE_VERSION */
/***
*char *strtok(string, control) - tokenize string with delimiter in control
*
*Purpose:
-
strtok considers the string to consist of a sequence of zero or more
-
text tokens separated by spans of one or more control chars. the first
-
call, with string specified, returns a pointer to the first char of the
-
first token, and will write a null char into string immediately
-
following the returned token. subsequent calls with zero for the first
-
argument (string) will work thru the string until no tokens remain. the
-
control string may be different from call to call. when no tokens remain
-
in string a NULL pointer is returned. remember the control chars with a
-
bit map, one bit per ascii char. the null char is always a control char.
*Entry:
-
char *string - string to tokenize, or NULL to get next token
-
char *control - string of characters to use as delimiters
*Exit:
-
returns pointer to first token in string, or if string
-
was NULL, to next token
-
returns NULL when no more tokens remain.
*Uses:
*
*Exceptions:
*
*******************************************************************************/
#ifdef _SECURE_VERSION
#define _TOKEN context
#else / _SECURE_VERSION /
#define _TOKEN ptd->_token
#endif / _SECURE_VERSION */
#ifdef _SECURE_VERSION
char * __cdecl strtok_s (
char * string,
const char * control,
char ** context
)
#else /* _SECURE_VERSION /
char * __cdecl strtok (
char * string,
const char * control
)
#endif / _SECURE_VERSION */
{
unsigned char *str;
const unsigned char *ctrl = control;
unsigned char map[32];
int count;
#ifdef _SECURE_VERSION
/* validation section */
_VALIDATE_RETURN(context != NULL, EINVAL, NULL);
_VALIDATE_RETURN(string != NULL || *context != NULL, EINVAL, NULL);
_VALIDATE_RETURN(control != NULL, EINVAL, NULL);
/* no static storage is needed for the secure version */
#else /* _SECURE_VERSION */
_ptiddata ptd = _getptd();
#endif /* _SECURE_VERSION */
/* Clear control map */
for (count = 0; count < 32; count++)
map[count] = 0;
/* Set bits in delimiter table */
do {
map[*ctrl >> 3] |= (1 << (*ctrl & 7));
} while (*ctrl++);
/* Initialize str */
/* If string is NULL, set str to the saved
- pointer (i.e., continue breaking tokens out of the string
- from the last strtok call) */
if (string)
str = string;
else
str = _TOKEN;
/* Find beginning of token (skip over leading delimiters). Note that
- there is no token iff this loop sets str to point to the terminal
- null (*str == ‘\0’) */
while ( (map[*str >> 3] & (1 << (*str & 7))) && *str )
str++;
string = str;
/* Find the end of the token. If it is not the end of the string,
- put a null there. */
for ( ; *str ; str++ )
if ( map[*str >> 3] & (1 << (*str & 7)) ) {
*str++ = ‘\0’;
break;
}
/* Update nextoken (or the corresponding field in the per-thread data
- structure */
_TOKEN = str;
/* Determine if a token has been found. */
if ( string == str )
return NULL;
else
return string;
}
🎍strerror() - 返回错误码🎍
该函数的功能是:返回错误码,所对应的错误信息。
strerror() 函数的声明方式如下****👇
char * strerror ( int errnum );
获取指向错误消息字符串的指针。
errnum的值,生成一个字符串,该字符串带有一条描述错误条件的消息,就像库的函数设置为errno一样。头文件是:#include <errno.h>
返回的指针指向一个静态分配的字符串,该字符串不能被程序修改。对该函数的进一步调用可能会覆盖其内容(为了避免数据竞争,不需要特定的库实现)。
由strerror产生的错误字符串可能特定于每个系统和库实现。
返回值:该函数返回一个指向错误字符串的指针,该错误字符串描述了错误 errnum。
🎍strerror()函数代码示例🎍
打开文件函数是:fopen()
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main(void)
{
FILE* Pf = fopen(“test.txt”, “r”);//打开文件如果以读的形式存在那么这个文件就会打开失败!
//一旦打开失败那个得到的就是NULL
if (Pf == NULL)
{
printf(“%s\n”, strerror(errno));
return 1;//返回main函数结束
}
fclose(Pf);
Pf = NULL;
return 0;
}
运行结果如下 👇
Error: No such file or directory
当然如果你在安装路径新建上一个test.txt文件就不会显示如上问题。
🎍strerror()源程序实现🎍
示例代码如下:👇
#include <cruntime.h>
#include <errmsg.h>
#include <stdlib.h>
#include <syserr.h>
#include <string.h>
#include <mtdll.h>
#include <tchar.h>
#include <malloc.h>
#include <stddef.h>
#include <dbgint.h>
#include <internal.h>
/* [NOTE: The error message buffer is shared by both strerror
and _strerror so must be the max length of both. /
/ Max length of message = user_string(94)+system_string+2 */
#define ERRMSGLEN (94+_SYS_MSGMAX+2)
#ifdef _UNICODE
#define _terrmsg _werrmsg
#else /* _UNICODE /
#define _terrmsg _errmsg
#endif / _UNICODE */
/***
*char *strerror(errnum) - Map error number to error message string.
*
*Purpose:
-
The strerror runtime takes an error number for input and
-
returns the corresponding error message string. This routine
-
conforms to the ANSI standard interface.
*Entry:
-
int errnum - Integer error number (corresponding to an errno value).
*Exit:
-
char * - Strerror returns a pointer to the error message string.
-
This string is internal to the strerror routine (i.e., not supplied
-
by the user).
*Exceptions:
-
None.
*******************************************************************************/
#ifdef _UNICODE
wchar_t * cdecl _wcserror(
#else /* _UNICODE /
char * __cdecl strerror (
#endif / _UNICODE */
int errnum
)
{
_TCHAR *errmsg;
_ptiddata ptd = _getptd_noexit();
if (!ptd)
return _T(“Visual C++ CRT: Not enough memory to complete call to strerror.”);
if ( (ptd->_terrmsg == NULL) && ((ptd->_terrmsg =
_calloc_crt(ERRMSGLEN, sizeof(_TCHAR)))
== NULL) )
return _T(“Visual C++ CRT: Not enough memory to complete call to strerror.”);
else
errmsg = ptd->_terrmsg;
#ifdef _UNICODE
_ERRCHECK(mbstowcs_s(NULL, errmsg, ERRMSGLEN, _get_sys_err_msg(errnum), ERRMSGLEN - 1));
#else /* _UNICODE /
_ERRCHECK(strcpy_s(errmsg, ERRMSGLEN, _get_sys_err_msg(errnum)));
#endif / _UNICODE */
return(errmsg);
}
/***
*errno_t strerror_s(buffer, sizeInTChars, errnum) - Map error number to error message string.
*
*Purpose:
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
This string is internal to the strerror routine (i.e., not supplied
-
by the user).
*Exceptions:
-
None.
*******************************************************************************/
#ifdef _UNICODE
wchar_t * cdecl _wcserror(
#else /* _UNICODE /
char * __cdecl strerror (
#endif / _UNICODE */
int errnum
)
{
_TCHAR *errmsg;
_ptiddata ptd = _getptd_noexit();
if (!ptd)
return _T(“Visual C++ CRT: Not enough memory to complete call to strerror.”);
if ( (ptd->_terrmsg == NULL) && ((ptd->_terrmsg =
_calloc_crt(ERRMSGLEN, sizeof(_TCHAR)))
== NULL) )
return _T(“Visual C++ CRT: Not enough memory to complete call to strerror.”);
else
errmsg = ptd->_terrmsg;
#ifdef _UNICODE
_ERRCHECK(mbstowcs_s(NULL, errmsg, ERRMSGLEN, _get_sys_err_msg(errnum), ERRMSGLEN - 1));
#else /* _UNICODE /
_ERRCHECK(strcpy_s(errmsg, ERRMSGLEN, _get_sys_err_msg(errnum)));
#endif / _UNICODE */
return(errmsg);
}
/***
*errno_t strerror_s(buffer, sizeInTChars, errnum) - Map error number to error message string.
*
*Purpose:
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)
[外链图片转存中…(img-buVVzjkf-1713174517830)]
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!