strlen的作用是获取字符串的长度。该函数返回类型为size_t,参数不需要修改,所以形参加了const修饰。
- size_t my_strlen(const char* p)
- {
- assert(p);
- size_t count = 0;///计数器
- while (*p != '\0')
- {
- count++;
- p++;
- }
- return count;
- }
先在函数内部对传入的指针断言,防止传入空指针。
用count变量进行计数,每次*p不等于'\0',count++。
- size_t my_strlen(const char* p)
- {
- assert(p);
- if (*p != '\0')
- {
- return 1+my_strlen(p + 1);
- }
- return 0;
- }
递归思想是大事化小,小事化了。有一个判断条件,每次进入条件调用自身时,参数范围越缩越小,达到递归的目的。
- size_t my_strlen(char* p)
- {
- assert(p);
- char* q = p;
- while (*q)
- {
- q++;
- }
- return q - p;
- }
不过用这种方法,形参如果用const修饰会报警告,因为执行char* q = p时,相当于一次权限放大。
将sour指向的 C 字符串复制到dest所指向的数组中,包括终止空字符(并在该点停止)。
如果故意让arr2没有'\0',那么使用strcpy函数时会引发异常。
可以发现,当dest空间不足以拷贝sour的数据时,strcpy函数仍然会将所有数据越界拷贝至dest中,并在dest栈帧销毁时引发异常。所以这也是这类函数不安全的原因。
拷贝出现内存重叠时,会引发异常。
- char* my_strcpy(char* dest, const char* sour)
- {
- assert(dest && sour);
- char* p = dest;
- while (*dest++ = *sour++)
- {
- ;
- }
- return p;
- }
实现的功能是把sour的值复制到dest里,注意'\0'也要拷贝过去。
和strcpy一样:
1、sou指向的字符串必须以'\0'结尾,如果没有'\0',vs会报异常。
2、目标空间必须足够大
3、dest与sour指向的内存禁止重叠(例如自己追加自己)
- char* my_strcat(char* dest, const char* sour)
- {
- assert(dest && sour);
- char* p = dest;
- while (*dest)
- {
- dest++;
- }
- while (*dest++ = *sour++)
- {
- ;
- }
- return p;
- }
第一个字符串大于第二个字符串,返回值>0
第一个字符串等于第二个字符串,返回值==0
第一个字符串小于第二个字符串,返回值小于0
- int my_strcmp(const char* str1, const char* str2)
- {
- assert(str1 && str2);
- while (*str1 == *str2)//循环先把相等的找完
- {
- if (*str1 == '\0')//需要在++前判断*str是不是为\0
- return 0;
- str1++;
- str2++;
- }
- return *str1 - *str2;
- }
注意dest指向的空间要大于num
如果sour指向的字符串的长度比num大,从sour中拷贝num个字符到dest。
如果sour指向的字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加'\0',直到num个。
- char* my_strncpy(char* dest, char* sour, size_t num)
- {
- assert(dest && sour);
- char* p = dest;
- while (num&&*sour)//num==0或*sour=='\0'则跳出循环
- {
- *dest++ = *sour++;
- num--;
- }
- while (num)//把剩余的num替换为'\0'
- {
- *dest++ = '\0';
- num--;
- }
- return p;
- }
注意dest指向的空间要足够
字符串追加完毕后,在末尾添加'\0'
如果sour中字符串的长度小于 num,则仅复制终止空字符之前的内容(不会像strncpy一样追加'\0');
- char* my_strncat(char* dest, const char* sour, size_t num)
- {
- assert(dest && sour);
- char* p = dest;
- while (*dest)//先找到dest的'\0'
- {
- dest++;
- }
- while (num&&*sour)//判断num==0和*sour=='\0'
- {
- *dest++ = *sour++;
- num--;
- }
- *dest = '\0';
- return p;
- }
和strcmp类似,跳过。
若str2是str1的子串,则返回str2在str1的首次出现的地址;如果str2不是str1的子串,则返回NULL。
- char* my_strstr(const char* str1, const char* str2)
- {
- assert(str1 && str2);
- const char* p = str1;//p的作用是记录初始位置
- const char* s1 = str1;
- const char* s2 = str2;
- while (*p != '\0')
- {
- while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)//*s1==*s2,进行逐个比较
- {
- s1++;
- s2++;
- }
- if (*s2 == '\0')//如果发现*s2=='\0',那么str2被找到了,返回指针p
- {
- return (char*)p;
- }
- p++;//反之p的地址加1,继续寻找下一个位置
- s1 = p;//s1和s2都要回来
- s2 = str2;
- }
- return NULL;//当*p=='\0',说明str1找完了(找不到),返回空指针
- }
第一个参数是被切割的字符串,第二个参数是分隔符的字符集合。
在第一次调用时,该函数需要一个 C 字符串作为str的参数,其第一个字符用作扫描标记的起始位置。在随后的调用中,该函数需要一个空指针,并使用最后一个标记结束后的位置作为新的扫描起始位置。
为了确定记号的开始和结束,该函数首先从起始位置扫描不包含在分隔符中的第一个字符(它成为记号的开始)。然后从标记的开头开始扫描分隔符中包含的第一个字符,该字符成为标记的结尾。如果找到终止空字符,扫描也会停止。
strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:
strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容
并且可修改。)
找到最后一个标记的点由函数内部保存,以便在下次调用时使用(不需要特定的库实现来避免数据竞争)
第一次使用strtok传入被切割的字符串地址,后续传入空指针即可,strtok函数会记住被切割的位置。
当字符串中没有更多的标记时,返回空指针。
注意:找到分隔符的位置将被替换为'\0',使用strtok会破坏原有字符串。使用时建议创建一个临时变量。
参数errnum是错误码
- #include
- #include
- #include
- int main ()
- {
- FILE * pFile;
- pFile = fopen ("unexist.ent","r");
- if (pFile == NULL)
- printf ("Error opening file unexist.ent: %s\n",strerror(errno));
- return 0;//errno是C语言设置的一个全局的错误码存放的变量
- }
num是拷贝的字节数。
将 num 字节值从sour指向的位置直接复制到的dest指向的内存块。
源指针和目标指针所指向的对象的基础类型与此函数无关;结果是数据的二进制副本。
函数不检查sour中是否有任何终止空字符 - 它始终精确地复制数字字节。
为避免溢出,dest参数和sour所指向的数组的大小应至少为 num 个字节,并且不应重叠(对于重叠的内存块,memmove是一种更安全的函数)。虽然vs中memcpy和memmove都能拷贝重叠内存,虽然都可以使用,但是要知道文档中规定memcpy不应处理重叠内存块。
- void* my_memcpy(void* dest, const void* sour, size_t num)//注意返回类型是void*,链式访问时需要强转
- {
- assert(dest && sour);
- void* p = dest;
- while (num)
- {
- num--;
- *(char*)dest= *(char*) sour;
- dest = (char*)dest + 1;
- sour = (char*)sour + 1;
- }
- return p;
- }
memmove和memcpy的特性一样,唯一区别是memmove可以拷贝内存重叠的区域。
- void* my_memmove(void* dest, const void* sour, size_t num)
- {
- assert(dest && sour);
- void* p = dest;
- //dest<=sour,数据从前向后拷贝
- if (dest <= sour)
- {
- while (num)
- {
- num--;
- *(char*)dest = *(char*)sour;
- dest = (char*)dest + 1;
- sour = (char*)sour + 1;
- }
- }
- //dest>sour,数据从后向前拷贝
- else
- {
- while (num)
- {
- num--;
- *((char*)dest + num) = *((char*)sour + num);
- }
- }
- return p;
- }
memcmp与strcmp不同的是,该函数在找到'\0'不会停止比较。如图所示,使用时num的参数不要大于数组长度。
memset是按字节修改内存