C标准库常用函数实现

  • 下面介绍一些常用的标准库库函数,因为经常用到,所以搞懂内部源代码很重要,也是进一步学习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的实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

有时需要偏执狂

请我喝咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值