assert()宏
断言:assert作为一种软件调试的方法,提供了一种在代码中进行正确性检查的机制。
assert()宏接受一个整形表达式参数。如果表达式的值为假,assert()宏就会调用_assert函数在标准错误流中打印一条错误信息,并调用abort()函数终止程序。
使用assert的好处:
- 能自动标识文件和出问题的行号
- 不需要更改代码就能开启或关闭assert机制(开不开启关系到程序大小的问题)。如果认为已经排除了程序的bug,就可以把 #define NDEBUG 写在assert.h前,并重新编译程序。这样编译器就会禁用工程文件中所有的assert语句。如果程序又出问题,可以移除这条#define指令,然后重新编译程序,这样就可以重新启用assert语句了。
assrt与if比较:
int* p = (int*)malloc(sizeof(int));
assert(p); //错误
assert在调试完毕之后会禁用掉,这样一来代码只剩下
int* p = (int*)malloc(sizeof(int));
assert与if做放错处理两点用法区别:
- assert用在debug版本中进行调试
- if(NULL != P)用在release版本中检查指针有效性。
求字符串长度
strlen
手动实现
int my_strlen(const char* str) //常量字符串
{
int count = 0;
assert(str != NULL); //断言
while(*str) //*str != '\0'
{
count++;
str++;
}
return count;
}
递归写法:不创建临时变量
MSDN
size_t strlen( const char *string );
库函数里的返回类型 size_t = unsigned int
总结:
1、字符串以’\0’作为结束标志,strlen函数返回的是在字符串中’\0’前面出现的字符个数(不包含’\0’)
2、参数指向的字符串必须要以‘\0’结束
3、函数的返回值是size_t,无符号的(易错)
长度不受限制的字符串函数
遇到\0才停止:不安全
strcpy
MSDN
char *strcpy( char *strDestination, const char *strSource );
使用
int main()
{
char arr1[] = "abcdefghij";
char arr2[] = "bit";
strcpy(arr1,arr2);
return 0;
}
Output
bit
手动实现
char* my_strcpy(char* dest,const char*src)
{
assert(dest != NULL);
assert(src != NULL);
char* ret = dest;
//拷贝src指向的字符串到dest指向的空间,包含'\0'
while(*dest++ = *src++)
{
;
}
//返回目的空间的起始地址
return ret;
}
1、源字符串必须以 '\0’结束
2、目标空间足够大,确保能够放源字符串
3、目标空间可变
strcat
Append a string. 追加一个字符串。
MSDN
char *strcat( char *strDestination, const char *strSource );
使用
int main()
{
char arr1[30]="hello";
char arr2[]="world";
strcat(arr1,arr2);
printf("%s\n",arr1);
return 0;
}
Output
helloworld
模拟实现
char* my_strcat(char*dext,const char* src)
{
char*ret = dest;
assert(dest != NULL);
assert(src);
//当assert是NULL时,为假false
//1.找到目的字符串的'\0'
while(*dest != '\0')
dest++:
//2.追加
while(*dest++ = *src++);
return ret;
}
总结:
1、源字符串必须以 ‘\0’ 结束
2、目标空间必须足够大,能容纳下源字符串的内容
3、目标空间可修改
4、自己不可以追加自己
strcmp
compare strings
MSDN
int strcmp( const char *string1, const char *string2 );
对应字符进行比较
模拟实现
大于 返回1
相等 返回0
小于 返回-1
int my_strcmp(const char* str1,const char* str2)
{
assert(str1 && str2);
//比较
while(*str1 == *str2)
{
if(*str1 == '\0')
return 0; //相等
str1++;
str2++;
}
if(*str1 > *str2)
return 1;
else
return -1;
}
大于 返回大于0
小于 返回小于0
int my_strcmp(const char* str1,const char* str2)
{
assert(str1 && str2);
//比较
while(*str1 == *str2)
{
if(*str1 == '\0')
return 0; //相等
str1++;
str2++;
}
return (*str1 - *str2);
}
长度受限制的字符串函数
strncpy
Copy characters of one string to another.
char *strncpy( char *strDest, const char *strSource, size_t count );
count
Number of characters to be copied
你让我拷贝几个我就拷贝几个,不一定带 \0。
总结:
1、拷贝count个字符从源字符串到目标空间。
2、如果源字符串的长度小于count,则拷贝完源字符串之后,在目标的后面追加0,直到count个。
strncat
Append characters of a string.指定长度追加字符串。
MSDN
char *strncat( char *strDest, const char *strSource, size_t count );
abcd\0xxxxxxx
总结:
1、追加结束后还是希望他是字符串,会主动放一个\0在后面。
2、str2的长度小于count,追加完了最后一个放 \0
strncmp
MSDN
int strncmp( const char *string1, const char *string2, size_t count );
strstr
Find a substring. 查找字符串
MSDN
char *strstr( const char *string, const char *strCharSet );
使用
int main()
{
char *p1 = "abcdefghj";
char *p2 = "def";
//strstr(p1, p2);
//若存在,返回d的地址
char* ret = strstr(p1, p2);
if(ret == NULL)
{
printf("子串不存在!");
}
else
{
printf("%s\n",ret);
}
return 0;
}
Output
defghj
模拟实现
可进一步去研究KMP算法
#include <assert.h>
char* my_strstr(const char* p1, const char* p2)
{
assert(p1 != NULL);
assert(p2 != NULL);
char *s1 = NULL;
char *s2 = NULL;
char *cur = (char*)p1;
if(!*p2)
{
return (char*)p1; //p2是空字符串
}
while(*cur)
{
s1 = cur;
s2 = (char* )p2; //强制类型转换
while ((*s1==*s2) && *s2 && *s1)
{
s1++;
s2++;
}
if(!*s2)
{
return cur; //找到子串
}
if(*s1!)
{
return NULL;
}
cur++;
}
return NULL; //找不到子串
}
案例:
1、 abbbcefh 和 bbc
2、abcdbcehj 和 bcd
strtok
Find the next token in a string.
MSDN
char *strtok( char *str, const char *sep );
sep参数是个字符串,定义了用作分隔符的字符集合。
sep去切割str
192.168.31.121
192 168 31 121 - strtok 分隔符 .
zpw@bit.com 分隔符 @ .
把找到的字符用\0代替。
strtok函数的第一个参数为NULL,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
strtok函数的第一个参数不为NULL,函数将找到str中的第一个标记,strtok将保存他在字符串中的位置。(函数内部有一个静态变量)
使用
#include <stdio.h>
#include <string.h>
int main()
{
char arr[] = "192.168.31.121";
char* p = ".";
char buf[1024] = { 0 };
strcpy(buf, arr);
char* ret = NULL;
for (ret = strtok(arr, p); ret != NULL; ret = strtok(NULL, p))
{
printf("%s\n", ret);
}
return 0;
}
先拷贝一份原串,防止对原先字符串进行改变。
strerror
错误报告函数:返回错误码,对应的错误信息(返回地址 char*)
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
//错误码 - 错误信息
//0 - No error
//1 - Operation not permitted
//2 - No such file or directory
//strerror将错误码翻译成错误信息
//char* str = strerror(4);
//errno是全局的错误码变量
//C语言的库函数在执行过程中发生了错误,就会把对应的错误码,赋值到errno中
char* str = strerror(errno);
printf("%s\n", str);
return 0;
}
errno是全局的错误码变量
C语言的库函数在执行过程中发生了错误,就会把对应的错误码,赋值到errno中
使用
打开文件
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
//打开文件
FILE* pf = fopen("test.txt", "r");
if (!pf)
{
printf("%s\n", strerror(errno));
}
else
{
printf("open file success\n");
}
return 0;
}
内存函数
memcpy
内存拷贝
Copies characters between buffers.(缓冲区)
void *memcpy( void *dest, const void *src, size_t count );
count单位是字节
sizeof(数组名):数组的地址 ----->>> 数组的大小
&数组名:数组的地址
模拟实现
#include <assert.h>
void my_memcpy(void* dest, const void* src, size_t num)
{
void* ret = dest;
assert(dest != NULL);
assert(dest != NULL);
while(num--)
{
*(char*)dest = *(char*)src;
++(char*)dest; //先强制类型转换再 ++
++(char*)src;
}
return ret;
}
memmove
void *memmove( void *dest, const void *src, size_t count );
C语言标准说,memcpy 只要处理 不重叠的内存拷贝就可以,当下发现:VS2013环境下的memcpy可以处理重叠拷贝。
memmove 处理重叠内存的拷贝
memcmp
int memcmp( const void *buf1, const void *buf2, size_t count );
< 0 buf1 less than buf2
0 buf1 identical to buf2
> 0 buf1 greater than buf2
memset
内存设置
void *memset( void *dest, int c, size_t count );
使用
#include <stdio.h>
#include <string.h>
int main()
{
char arr[10] = "";
memset(arr, 'c', 10);
return 0;
}
易错点
#include <stdio.h>
#include <string.h>
int main()
{
int arr[10] = { 0 };
//40个字节
// hex
// 01 01 01 01 | 01 01 01 01 | 01 01 00 00 00 ...
memset(arr, 1, 10);
return 0;
}
count的单位是字节,修改的单位是字节。