1.字符串与指针安全操作
函数
函数 | 功能 | 安全替代 | 功能 |
---|---|---|---|
strcpy |
字符串拷贝 | strncpy |
复制前n个,最多strlen个,超出有效长度,按原样复制 |
strcat |
字符串拼接 | strncat |
dest只连接src的前n个,如果n超过有效长度,按原样链接 |
strcmp |
字符串比较 | strncmp |
比较前n 个字符 |
strlen |
获取有效长度(不含\0 ) |
const关键字与万能指针
const关键字
1.普通变量经过const修饰后,会变为只读变量,无法直接访问,只能通过间接访问修改。
2.指针变量前加const,不是说p不能改,而是*p不能改(p指向的变量),修饰的是*p(无法通过指针运算修改指针变量所指向的变量)
void*
万能指针
- 可接收任意基类型指针,但不能做指针运算,可以降低程序的耦合性。
- 需强制类型转换。
2.数组指针与指针数组
数组指针(指向数组的指针)
- 定义:指向数组的指针,括号不能省略,一维数组使用时,p+1为野指针。二维数组使用p + 1,不是野指针(二维数组作为函数参数传递,形参是指向一维数组的指针)。
int (*p)[4]
(指向含4个整数的数组)
3.函数与指针
指针函数(返回指针的函数)
- 禁止返回局部变量的地址,可以返回局部变量的值。
// 错误示例
int* func() {
int a = 10;
return &a; // a销毁后返回野指针
}
- 安全返回,用static修饰为静态变量,或者直接用全局变量。
int* safe_func() {
static int b = 20; // 静态存储期
return &b; // 合法
}
4.关键技巧总结
- 字符串常量,内容相同时,地址是一样的
void*
使用规范- 用于内存拷贝
memcpy
函数 - 用前必须强制类型转换
- 用于内存拷贝
const
最佳实践- 函数参数加
const
防止意外修改(如strlen(const char*)
)
- 函数参数加
4.代码
1)字符串的操作
void Puts(const char *s)
{
while(*s)
{
putchar(*s++);
}
puts("");
}
int Strlen(const char *s)
{
int counter = 0;
while(*s)
{
++counter;
++s;
}
return counter;
}
void Strcpy(char *dest, const char *src)
{
while(*src)
{
*dest++ = *src++;
}
*dest = 0;
}
void Strcat(char *dest, const char *src)
{
while(*dest)
{
++dest;
}
while(*src)
{
*dest++ = *src++;
}
*dest = 0;
}
int Strcmp(const char *s1,const char *s2)
{
while(*s1 == *s2 && *s1 && *s2)
{
++s1;
++s2;
}
return *s1 - *s2;
}
void Strncpy(char *dest, const char *src, int n)
{
while(n--)
{
*dest++ = *src++;
}
*dest = 0;
}
void Memcpy(void *dest, const void *src, int n)
{
char *p = (char *)src;
char *q = (char *)dest;
while(n--)
{
*q++ = *p++;
}
*q = 0;
}
void Strncat(char *dest, const char *src, int n)
{
while(*dest)
{
++dest;
}
while(n-- && *src)
{
*dest++ = *src++;
}
*dest = 0;
}
int Strncmp(const char *s1, const char *s2, int n)
{
while(--n && *s1 == *s2 && *s1 && *s2)
{
++s1;
++s2;
}
return *s1 - *s2;
}
int main(void)
{
char s1[100] = "Hello";
char s2[100] = "Helloq";
//Puts(s1);
//printf("%d\n", Strlen(s1));
//Strcpy(s2, s1);
//Puts(s2);
//Strcat(s1, s2);
//puts(s1);
//printf("%d\n",Strcmp(s1,s2));
//Strncpy(s2, s1, 5);
//Puts(s2);
//Memcpy(s2, s1, sizeof(s1));
//Puts(s2);
//Strncat(s2, s1, 8);
//Puts(s2);
//printf("%d\n", Strncmp(s1, s2, 5));
return 0;
}
2)二维数组的操作(数组指针)
void printfArray2D(int (*a)[4], int rows)
{
int i, j;
for(i = 0;i < rows;++i)
{
for(j = 0;j < 4;++j)
{
printf("%2d ", *(*(a + i) + j));
}
puts("");
}
}
int sumOfArray2D(int (*a)[4], int rows)
{
int i,j;
int sum = 0;
for(i = 0;i < rows;++i)
{
for(j = 0;j < 4;++j)
{
sum += *(*(a + i) + j);
}
}
return sum;
}
void swap(int *a, int *b)
{
int t = *a;
*a = *b;
*b = t;
}
void reverse(int *begin, int *end)
{
while(begin < end)
{
swap(begin, end);
++begin;
--end;
}
}
void reverse2D(int (*a)[4], int rows)
{
int i;
for(i = 0;i < rows;++i)
{
reverse(*(a + i),*(a + i) + 4 - 1);
}
}
int main(void)
{
int a[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
int rows = sizeof(a) / sizeof(a[0]);
//printfArray2D(a, rows);
//printf("%d\n", sumOfArray2D(a, rows));
//reverse2D(a, rows);
//printfArray2D(a, rows);
return 0;
}
3)指针函数
char *Strcat(char *dest, const char *src)
{
char *ret = dest;
while(*dest)
{
++dest;
}
while(*src)
{
*dest++ = *src++;
}
*dest = 0;
return ret;
}
5.总结
- 字符串操作:
strn
系列函数更安全。 - 嵌入式安全
const
保护只读数据(如固件配置)- 静态变量/全局变量保障指针生命周期
关联知识:结合内存分区理解指针生命周期(栈/静态区/堆)