《嵌入式C语言笔记(十六):字符串搜索、动态内存与函数指针精要》
今日学习内容
字符串操作进阶
查找与统计长字符串中的短字符串,使用strncmp
遍历长字符串每个起始位置,比较从该位置开始的子串与短字符串。
int subString(const char *s, const char *sub)
{int i;int sLen = strlen(s);int subLen = strlen(sub);int k = sLen - subLen;for(i = 0;i <= k;++i){if(strncmp(s + i, sub, subLen) == 0){return i;}}return -1;
}int main(void)
{printf("%d\n", subString("Hello He is handsome He He She", "He"));return 0;
}
计数,匹配成功时计数增加。需处理重叠匹配情况。
int counterString(const char *s, const char *sub)
{int counter = 0;int t = 0;int ret;ret = subString(s, sub);while(ret >= 0){++counter;t = t + ret + strlen(sub);ret = subString(s + t, sub);}return counter;
}int main(void)
{printf("%d\n", counterString("Hello He is handsome He He She", "He"));return 0;
}
strcpy
自复制优化
标准库函数strcpy(s, s)
能安全处理自复制情况。
char *Strcpy(char *dest, const char *src)
{if(dest == src){return dest;}char *ret = dest;while(*src){*dest++ = *src++;}*dest = 0;return ret;
}
strcat
自连接陷阱
strcat(s, s)
会覆盖结束符导致无限循环。动态内存分配是可靠解决方案:
char *Strcat(char *dest, const char *src)
{char s[100];char *p = s;if(dest == src){strcpy(s, src);}char *ret = dest;while(*dest){++dest;}while(*p){*dest++ = *p++;}*dest = 0;return ret;
}int main(void)
{char s[100] = "Hello";Strcat(s, s);puts(s);return 0;
}
动态内存管理
核心函数
函数 | 原型 | 功能 |
---|---|---|
malloc | void* malloc(size_t size) | 分配未初始化内存 |
calloc | void* calloc(size_t num, size_t size) | 分配归零内存 |
realloc | void* realloc(void *ptr, size_t new_size) | 调整内存块大小 |
free | void free(void *ptr) | 释放内存 |
使用规范
使用完内存后,要用free释放内存
malloc
char *p = malloc(strlen(s) + 1);
realloc:重新分配,并且复制原来的内容,归还之前的内存
p = realloc(p, strlen(p) + strlen(t) + 1);
calloc:分配为零的内存
int *p = calloc(n, sizeof(int));
free:归还内存,不归还会导致内存泄漏。
free(p);p = NULL;
函数指针与多级指针
函数指针声明
语法:返回类型 (*指针名)(参数列表)
。
能指向函数入口地址的指针
回调函数
降低耦合性
int add(int a, int b)
{return a + b;
}int sub(int a, int b)
{return a - b;
}int main(void)
{int (*p)(int , int ) = NULL;p = add;printf("%d\n", p(10, 20));return 0;
}
排序(qsort)
传参方式,例子在下下一个。
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
int pow2(int n)
{return n * n;
}int fn(int n)
{return n;
}void sort(int *a, int len, int (*pfn)(int))
{int i, j;for(i = 0;i < len - 1;++i){for(j = i + 1;j < len;++j){if(pfn(a[i]) > pfn(a[j])){int t = a[i];a[i] = a[j];a[j] = t;}}}
}void printArray(int *a, int len)
{int i;for(i = 0;i < len;++i){printf("%d ", a[i]);}puts("");
}int main(void)
{int a[] = {-1,2,-3,4,-5,6,-7,8,-9,0};int len = sizeof(a) / sizeof(*a);sort(a, len, pow2);printArray(a, len);return 0;
}
降低耦合性
int intcmp(const void *p1, const void *p2)
{int *q1 = (int *)p1;int *q2 = (int *)p2;if(*q1 > *q2){return 1;}else if(*q1 == *q2){return 0;}else{return -1;}}int main(void)
{double a[] = {1,2,3,4,5,6,7,8,9,0};qsort(a, sizeof(a) / sizeof(*a), sizeof(*a), intcmp);int i ;for(i = 0;i < sizeof(a) / sizeof(*a);++i){printf("%d\n", a[i]);}return 0;
}
指针的指针(二级指针)应用,修改一级指针的内容
void printStrings(char **p, int len)
{int i;for(i = 0;i < len;++i){puts(p[i]);}
}void reverseStrings(char **p, int len)
{int i;for(i = 0;i < len / 2;++i){
// char t[100];
// strcpy(t, p[i]);
// strcpy(p[i], p[len - i - 1]);
// strcpy(p[len - i - 1], t);char *t;t = p[i];p[i] = p[len - i - 1];p[len - i - 1] = t;}
}char *maxStrings(char **p, int len)
{char *max;max = p[0];int i;for(i = 1;i < len;++i){if(strcmp(max, p[i]) < 0){max = p[i];}}return max;
}void swap(char **a, char **b)
{char *t;t = *a;*a = *b;*b = t;
}void sortStrings(char **p, int len)
{int i, j;for(i = 0;i < len - 1;++i){for(j = i + 1;j < len;++j){if(strcmp(p[i], p[j]) > 0){swap(p + i, p + j);// char *t;// t = p[i];// p[i] = p[j];// p[j] = t;}}}
}
指针数组参数
指针数组作为函数的参数,形参是指向指针的指针(下面的代码是调用上面的函数)
int main(void)
{char *s[] = {"Hello", "World!", "China"};int len = sizeof(s) / sizeof(*s);const char *n = "China";
// puts(maxStrings(s, len));
// reverseStrings(s, len);sortStrings(s, len);printStrings(s, len);return 0;
}
关键技巧
- 字符串安全:动态内存解决自操作问题,始终验证缓冲区容量
- 堆内存铁律:
malloc
/calloc
必须配对free
,realloc
用临时变量接收 - 函数指针:实现策略模式,减少模块耦合
- 多级指针:二级指针通过
pp
→p
→value
间接访问数据
常见问题
内存泄漏
char *p = malloc(100);
// 忘记free导致泄漏
解决方案:
free(p);
p = NULL;
野指针访问
int *p = malloc(sizeof(int));
free(p);
printf("%d", *p); // 危险!
解决方案:
free(p);
p = NULL; // 立即置空
核心要点
- 字符串操作:动态内存是解决自连接问题的根本方案
- 堆内存管理:
realloc
优先尝试原地扩展,需手动控制生命周期 - 函数指针:本质是代码入口地址,实现"相同接口不同行为"
- 多级指针:理解
main
函数参数传递机制与指针数组内存布局