今日学习内容
字符串操作进阶
查找与统计长字符串中的短字符串,使用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
函数参数传递机制与指针数组内存布局