vscode配置C语言环境
MinGW-W64 GCC下载链接:
基础语法
字符串
string库
strlen函数测量字符串长度:strlen(s)传入头指针。
指针
指针基础语法
来自CSDN-唐大麦
1 2 3 4 5 6 7 8 9
| int p; int *p; int p[3]; int *p[3]; int (*p)[3]; int **p; int p(int); int (*p)(int); int *(*p(int))[3];
|
上面就已经把指针的语法说得相当明白了,无非就是各种符号的优先级问题,优先级 () > [] > *
,哪一个符号先与变量结合,然后是哪一个,这样子再复杂的指针复合体也能迎刃而解。
指针结合地址
指针,当然会指向一个地方。
指针具体指向一个地址
,这个地址我们就以stm32单片机为例,stm32内核是32位的系统,所以地址就是32位的,比如0x12345678
,就是一个32位的地址,也就是4个字节。所以32位系统的指针都是4个字节的。
32位系统中char占1个字节。
32位系统中short占2个字节。
32位系统中int占4个字节。
指针的算术运算
来自CSDN-唐大麦
1 2 3
| char a[20]; int *p=(int *)a; p++;
|
p是一个int类型的指针。
a是字符数组的头指针,a的类型是char *
,强制转换为int *
并赋值给p。
重点来了:p++之后,指针p指向哪里?
这里并不是执行p+1,而编译器执行的真正操作是p+sizeof(int)
。
因为p是int型的指针,int 占4 个字节。由于地址是用字节做单位的,故p所指向的地址向高地址方向增加了4 个字节。
由于char 类型的长度是一个字节,所以,原来p是指向数组a[0],p++之后指向的是a[4]。
指针运算符&和*
来自自己
在我看指针运算符时一直是用的下面的方式:
1 2 3 4 5
| int a = 4, b; int *p;
p = &a; b = *p;
|
数组和指针的关系
1 2 3 4
| int array[10]={0,1,2,3,4,5,6,7,8,9},value; value=array[0]; value=array[3]; value=array[4];
|
这个比较简单了,主要是记住数组名就代表数组头指针就行了。
二级指针
来自CSDN-唐大麦
1 2 3 4 5 6 7 8
| int a, b; int array[10]; int *pa; int **ptr;
pa = &a; ptr = &pa; *ptr = &b;
|
数组的二级指针例子:
1 2
| char *arr[20]; char **parr = arr;
|
字符串的指针表示(未来可能移动到字符串节)
来自CSDN-唐大麦
1 2 3 4 5 6 7 8 9
| char *str[3]={ "Hello,thisisasample!", "Hi,goodmorning.", "Helloworld" }; char s[80]; strcpy(s,str[0]); strcpy(s,str[1]); strcpy(s,str[2]);
|
记录一下字符串的表示方法,这种表示方法之前我见到过,但是后面忘记了。
字符串与结构体
重点!
来自CSDN-唐大麦
1 2 3 4 5 6 7 8 9 10 11
| struct MyStruct { int a; int b; int c; }; struct MyStruct ss={20,30,40};
struct MyStruct *ptr=&ss;
|
通过指针访问结构体成员变量:
函数和指针的关系
来自自己
1 2 3 4
| int fun(char *,int); int (*pfun)(char *,int); pfun=fun; int a=(*pfun)("abcdefg",7);
|
指针类型转换
来自自己
举例,int类型的指针变成char类型指针:
1 2 3 4
| unsigned int a; a=0x12345678; char *ptr; ptr=(char*)a;
|
举例:指针具体数值的提取:
1 2 3 4 5
| int a=123,b; int *ptr=&a; char *str; b=(int)ptr; str=(char*)b;
|
指针安全
来自CSDN-唐大麦
1 2 3 4
| char s='a'; int *ptr; ptr=(int *)&s; *ptr=1298;
|
指针原先是char型,只选定了一个字节,这样强制转换成int型导致指针选定了这个字节连带后面三个字节,总共四个字节。
假如后三个字节里原本存储着重要内容,那这一操作就有一定的危险。
指针实用操作
函数传入数组与返回数组
一个数组要想传入函数内,或者被函数return出来,那么只能是利用函数的头指针。
例如力扣第一题两数之和:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| int* twoSum(int* nums, int numsSize, int target, int* returnSize) { for(int i=0; i<numsSize; i++) { for(int j=i+1; j<numsSize; j++) { if(nums[i] + nums[j] == target) { int *ans = malloc(sizeof(int)*2); ans[0] = i; ans[1] = j; *returnSize = 2; return ans; } } } *returnSize = 0; return NULL;
|
导入的数组是nums,导出的数组是ans。
为什么不能int ans[2];呢?而是要用malloc申请内存?
因为函数的返回值是数组,也就是需要返回一个指针
。
而局部变量的指针是无法被函数返回的。
,所以要么把ans数组拿出去,变成全局变量,要么就申请内存。
数据结构
链表
内存就像黑暗森林
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include<stdio.h>
struct ListNode { int val; struct ListNode *next; };
void main() { struct ListNode* l1 = malloc(sizeof(struct ListNode)); struct ListNode* l2 = malloc(sizeof(struct ListNode)); l1->val = 2; l2->val = 6; l1->next = l2; printf("%d\r\n",l1->val); printf("%d\r\n",l1->next->val); printf("%d\r\n",(int)l1->next->next); printf("%d\r\n",(int)NULL); if (l1->next->next==NULL) { printf("111"); } }
|
输出的结果是:
所以初始化节点一定要做到每个节点的每个成员都初始化,NULL也是人为赋予的,不是系统自动的。
来自我的力扣
第二题的两个倒序链表相加:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
|
struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2) { struct ListNode* head=NULL; struct ListNode* node=NULL; int carry = 0; while(l1 || l2) { int c1 = l1? l1->val : 0; int c2 = l2? l2->val : 0; if(head==NULL) { head = node = malloc(sizeof(struct ListNode)); node->val = (c1+c2+carry)%10; node->next = NULL; } else { node->next = malloc(sizeof(struct ListNode)); node = node->next; node->val = (c1+c2+carry)%10; node->next = NULL; } if(l1)l1 = l1->next; if(l2)l2 = l2->next;
carry = (c1+c2+carry)/10; } if(carry!=0) { node->next = malloc(sizeof(struct ListNode)); node = node->next; node->val = carry; node->next = NULL; } return head; }
|
其实稍微复习一下,语法就能记起来了。
1
| head = node = malloc(sizeof(struct ListNode));
|
上面这句我甚至还想了一段时间。head是个指针,指向的就是node这个链表头节点的地址
。
初始化节点
1 2 3 4
| node->next = malloc(sizeof(struct ListNode)); node = node->next; node->val = carry; node->next = NULL;
|
这几句是一成不变的,必须连在一起,
三步初始化节点:
给节点指针申请内存
节点连接
赋值
然后写算法一定要打草稿。
算法
滑动窗口算法
该算法的适用关键词是:子串
。
各种找怎么怎么样的子串都可以用这个算法。
子串问题套用这个算法就可以简化为:滑动窗口的左端和右端位置锁定的问题
。
以下是精炼的算法核心:
- 如果当前子串不符合要求:
扩展右端
。
- 如果当前子串 符合要求:
收缩左端
。
比如力扣第三题:无重复字符的最长子串。
来自我的力扣
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #define max(a,b) a>b? a : b
int lengthOfLongestSubstring(char* s) {
int left = 0,length = 0,max_l = 0; for(int right=0; s[right];right++) { for(int i=left ; i<right ; i++) { if(s[i]==s[right]) { left=i+1; }
} length = right+1 - left; max_l = max(length,max_l); } return max_l; }
|
遍历
比如找最长的回文子串这种,必须要把所有可能的子串全部遍历出来。
1 2 3 4 5 6 7 8 9
| for(int i=0; i<n; i++) { for(int j=0; j<i; j++) { int L = j; int R = i; } }
|
第一个循环遍历右边界,第二个循环遍历左边界。