- 下面介绍一些常用的标准库库函数,因为经常用到,所以搞懂内部源代码很重要,也是进一步学习C语言指针很有用。慢慢更新,很重要的过程。
- 为了防止和库函数名重复,例如strncpy则表示为mystrncpy测试,但是mystrncpy里面的具体实现和标准库strncpy实现一样。
string.h
strlen
size_t mystrlen(const char *s)//s指向的内容只读
{
const char *sc;//sc变量可以修改,但是sc指向的内容不允许修改
for(sc = s ; *sc != '\0' ; ++sc)//直接对sc执行内容判断,依据指针++sc相当于执行下一个元素
;//最后肯定sc指向'\0',循环结束.
return(sc - s);//这就是元素个数
}
//++sc更加符合人的思维就是需要它改变。
strncmp
int mystrncmp(const char *s1 , const char *s2 , size_t n)
{
for( ; 0 < n ; ++s1 , ++s2, --n)//for后面做多件事情,分别指向下一个元素,以及n减一
if(*s1 != *s2)
//条件运算符优先级最低.解址符优先与关系运算符
return ( *(unsigned char *)s1 < *(unsigned char *)s2 ? -1 : 1 );//条件运算符进行比较
else if(*s1 == '\0')//碰到终止字符结束.
return (0);
return (0);//字典比较每一个字符相等或者碰到终止,则返回0
}
strncpy
/*
从s2执行的数据复制最多n个字符到s1指向数组中.
如果s2比n短,则s1执行数据后面添加空字符,直到写入n个字符.
直接上代码.
*/
char *mystrncpy(char *s1 , const char *s2 , size_t n)
{
char *s;//为了不影响s1,利用s = s1,通过s指针向s1指向区域填充数值.
for(s = s1 ; 0 < n&& *s2 != '\0' ; --n)//复制n个或者遇到s2为空,那么结束复制
*s++ = *s2++;//s当前地址字符=s2当前地址字符,然后s和s2指向下一个字符.
for(; 0<n ; --n)
*s++ = '\0';//长度不够,直接用空字符填充.
return(s1);
}
int main(void)
{
char des[12];
char *sou = "mycomputer";
mystrncpy(des , sou , 12);
exit(0);
}
strrchr
查找一个字符c在另一个字符串str中末次出现的位置(也就是从str的右侧开始查找字符c首次出现的位置)
查找字符c在str末次出现的次数。如果str中没有c,返回NULL。如”/bin/more”,那么返回指向第二/。
char *mystrrchr(const char *str, char c){
const char ch = c;
const char *sc;//和传入参数类型一样const否则报错. const为了警示在程序处理过程中不会
//改变传入参数
for(sc = NULL; ; ++str){//这里利用for,循环,更加灵活,通过条件返回,和while一样.
if(*str == ch)//sc记录s最后ch的位置
sc = str;
if(*str == '\0')//字符串结尾,那么返回
return ((char *)sc);//必须和返回类型一样,否则报错.
}
}
int main(int argc , char *argv[])
{
int n;
int fd[2];
char line[MAXLINE];
char *argv0 , *page;
pid_t pid;
FILE *fp;
printf("%s \n" , mystrrchr("/bin/more" , '/'));
}
输出:/more
strcat
/*
将s2[]拷贝到s1[]的后面
*/
char *mystrcat(char *s1 , const char *s2)
{
char *s;//暂存进行拷贝作用
for(s = s1;*s != '\0';++s)
;//s指向s1的结尾
for( ; (*s = *s2) != '\0' ; ++s , ++s2)
;//将s2拷贝到s1末尾,直到遇到s2的结束符
return(s1);//可以不测试返回值
}
int main(void)
{
//下面三者有重要的区别,一定要注意注意
char *s1 = "mynameis";//定义一个const指针,指向一个字符串常量
//之后可以修改其指向其他地址,但是如果试图修改内容,结果没有定义.
//也就是说不能当做strcat的s1
char s[] = "mynameis";//定义一个数组,大小没有写,这时编译器根据后面内容
//自动确定大小.这种用法很重要.Linux内核多处用到这种用法,因为灵活。
//此处大小是7,最后是字符结束符'\0'
char s2[20] = "mynameis";//定义一个字符串数组,大小20,后面没有初始化的自动为'\0'
//可以当做strcat的s1.
mystrcat(s,"wangjun");//因为大小是7,就存一个w
mystrcat(s2,"wangjun");//大小20,足够存wangjun,后面都是空格
//mystrcat(s1,"wangjun");//结构没有定义,在嵌入式设备上面跑必然产生硬件错误
exit(0);
}
memset
今天,将利用这个程序,彻底解决小端模式,大端模式,以及内存对齐,memset如何设置数据大小。。。。。。
#include <memary.h>
void *mymemset(void *s , int c , size_t n)
{
const unsigned char uc = c;
unsigned char *sc;
for(sc = (unsigned char *)s ; n > 0; ++sc , --n)//将指定区域全部赋值0,字节大小是n,sizeof正好计算存储需要的字节
*sc = uc; //所以数组,结构体,字符串数组都可以通过这个清0
return s; //sc指针类型强制转换成了char,那么就是一字节,然后可以每一个字节都给0
}
int main(void)
{
struct test{
int a;
int b;
char c;
}foo;
char char_test[4];
int int_test[4];
int cnt;
foo.a = 20;
foo.b = 30;
foo.c = 50;
cnt = sizeof(foo);//12
cnt = sizeof(char_test);//4
cnt = sizeof(int_test);//16
memset(&foo , 0 , sizeof(foo));//0 0 0
memset(&foo , 1 , sizeof(foo));//0x1010101 0x1010101 0x1
memset(char_test , 0 , sizeof(char_test));//0 0 0 0
memset(char_test , 1 , sizeof(char_test));//1 1 1 1
memset( int_test , 0 , sizeof(int_test));//0 0 0 0
memset( int_test , 1 , sizeof(int_test));//0x1010101 0x1010101 0x1010101 0x1010101
exit(0);
}//这个例子出现这种问题,都是因为多字节变量涉及大小端以及内存对齐问题。
- 对于int_test数组内存初始分配:
memset( int_test , 1 , sizeof(int_test));将int_test连续的内存区域(16字节)中每一个内存单元(1字节)都赋值1.
。
主机是小端系统。数组每个成员占用4字节,所以成员数值最终会是0x1010101。 - 对于struct test内存初始分配:
这里有内存对齐问题:留给后续解释。 - 对于char_test因为都是以最小内存单位进行操作的,所以可以使用memset进行相应的处理。
memchr
stdlib.h
一个c标准程序数据对象占据以下3种类型存储空间:
1、分配的静态存储空间并进行初始化,如没初始化,则自动初始化为0
2、每一个块,分配在栈中,假如没有初始化,则其初始化内容不确定。块结束,占用空间释放。这是动态存储区(先进后出)
3、只有当调用malloc,calloc,realloc分配才可控,那就是堆,这些对象一直存在指定free或者程序结束。
http://www.cnblogs.com/zfyouxi/p/4011020.html
atoi
int atoi(const char *s)
{
return ((int)_Stoul(s , NULL , 10));
}
itoa
/*
* 函数名:itoa
* 描述 :将整形数据转换成字符串
* 输入 :-radix =10 表示10进制,其他结果为0
* -value 要转换的整形数
* -buf 转换后的字符串
* -radix = 10
* 输出 :无
* 返回 :无
* 调用 :被USART1_printf()调用
*/
static char *itoa(int value, char *string, int radix)
{
int i, d;
int flag = 0;
char *ptr = string;
/* This implementation only works for decimal numbers. */
if (radix != 10)
{
*ptr = 0;
return string;
}
if (!value)
{
*ptr++ = 0x30;
*ptr = 0;
return string;
}
/* if this is a negative value insert the minus sign. */
if (value < 0)
{
*ptr++ = '-';
/* Make the value positive. */
value *= -1;
}
for (i = 10000; i > 0; i /= 10)
{
d = value / i;
if (d || flag)
{
*ptr++ = (char)(d + 0x30);
value -= (d * i);
flag = 1;
}
}
/* Null terminate the string. */
*ptr = 0;
return string;
}
assert.h
stdio.h
pritnf
这里设计串口重定向的问题,嵌入式里面常常使用printf进行串口重定向功能。
Mark一个 有时间好好记录这个 搞懂搞清楚。
http://blog.csdn.net/yannanxiu/article/details/52438351
sprintf and snprintf
/*
字符串格式化命令,要格式化的数据写入某个字符串中。这里没有指定缓冲区长度所以可能会导致缓冲区溢出的问题,破坏了调用函数的内存区域。
*/
int sprintf (char *__restrict __s,const char *__restrict __format, ...);
/*
sprintf的安全版本。
最多从源串中拷贝maxlen-1个字符到s中,然后在s末尾后面加一个'\0'表示字符串。因为指定了缓冲区的长度,所以缓冲区拷贝不会溢出,这点很重要。注意此函数返回是源串的长度。
*/
int snprintf (char *__restrict __s, size_t __maxlen,
const char *__restrict __format, ...);
https://baike.baidu.com/item/sizeof/6349467?fr=aladdin
http://blog.csdn.net/cosmoslife/article/details/7769921
内存对齐和sizeof的实现。