3. 写代码

1. 不使用临时临边交换数据

void swap(int &a,int &b)
{
  a = a^b;
  b = a^b;
  a = a^b;
}
//一个数和0进行异或,仍然是自身。

2. 将特定位置1,将特定位置0

#define bit_set(n, value)     value | (1<
  • 特定位,置1功能——将对应的位置1,并进行 或 操作
  • 特定位,清零功能——将对应的位置0,然后取反,并与待操作数进行 与 操作

3. 实现字符串的拷贝strcpy

char* strcpy(char* dest,const char* src)
{
    assert(src != NULL && dest != NULL);
    char* p1 = dest;
    char* p2 = src;
  
    #if 0
    while(p2){
        *p1++ = *p2++;
    }
    p1[strlen[p2]+1] = '\0';
    return p1;
    #else
    while((*p1++ = *p2++) != '\0');  //这种写法更好,效率更高
    return p1;
    #endif

}

注意事项

  1. const修饰源字符串
  2. 检查指针的有效
    • 最好不要使用assert(!p1 && !p2)
    • 也尽量不要使用assert(p1 != 0 && p2!= 0)
  3. 注意字符串结尾的 ‘\0’问题

4. 实现strlen 功能

注意的问题

  • 在c语言中,局部变量如果未初始化,其值是随机的。
    定义局部变量其实就是在栈中通过移动栈指针给程序提供一个内存空间和这个局部变量名绑定。因为栈内存是反复使用的,且栈内存比较脏,上次使用完没有清零。所以说使用栈来实现的局部变量定义时如果不显式初始化,值就是脏的。
  • 在c语言中,全局变量和静态变量都会自动初始化为0,堆和栈中的局部变量如果不初始化会有不可预测的值。
  • 在C++中,会执行 默认初始化,那什么是默认初始化?
    • 栈中的变量和堆中的变量会保有不确定的值。
    • 全局变量和静态变量会初始化为0。
      所以函数体中的变量定义是这样的规则:
  int i;             // 不确定值
  int i = int();      // 0
  int* p = new int;   // 不确定值
  int* p = new int(); // 0

#include 
#include 
size_t strlen(const char* str)
{
    int n =0;
    char* p1 = str;
    assert(p1 != NULL);  
    while(p1 != '\0')
    {
      n++;
    }
    return n;
}

strlen 与 sizeof的区别

  • sizeof是运算符,strlen是库函数
  • sizeof可以用类型、变量做参数,strlen 只能用char* 做参数,且必须以 \0结尾。
  • 数组做sizeof的参数不退化,传递给strlen就退化为指针了
  • sizeof是在编译的时候计算所占内存大小,strlen是在运行的时候计算出来。

5. 实现strcat功能

实现原理

  • 首先将目的字符串移动到最后一位,及‘\0’的位置
  • 然后进行拷贝,需要注意末尾字符‘\0’的问题。
#include 
#include 
char* strcat(char* str1,const char* str2)
{
    char* p1 = str1;
    char* p2 = str2;
    assert((p1 != NULL) && (p2 !=NULL)); 
    #if 0 // self
    int n = strlen(p1);
    while( *p1[n+1]++ = *p2++ ) != '\0');
    return p1;
    #else
    while(*p1++ != '\0');
    while((*p1++ = *p2++) != '\0');
    return p1;
    #endif
}

6. 字符串比较函数

规则

  • 若s1 == s2,返回零
  • 若s1 > s2,返回正数
  • 若s1 < s2,返回负数
int strcmp(const char* s1,const char* s2)
{
    char* p1 = s1;
    char* p2 = s2;
    assert( (p1 != NULL)&&(p2 != NULL)  );
    while(*p1 == *p2)
    {
        if(*p1 == '\0' )
          return 0;
        ++p1;
        ++p2;
    }
    return *p1 - *p2;
}

注意:关于assert的说明
assert是宏,不是函数,原型定义在头文件 assert.h中:

  void assert(int expression);

宏assert经常用于在函数开始处检验传入参数的合法性,可以将其看做异常处理的一种高级形式。
assert的作用是先计算表达式expression,然后判断

  • 如果表达式值为假,那么先向stderr打印错误信息,然后通过调用abort来终止程序运行
  • 如果表达式值为真,继续运行后面的程序。

7. 实现memmove

void* memmove(void* dst, const void* src,size_t n)
{
    char* p1 = (char*)dst;
    char* p2 = (char*)src;
    assert( (p1 != NULL)&&(P2 != NULL));
    
    #if 0  //self
    // 需要考虑内存重叠问题
    p2+=n-1;
    p1+=n-1;
    while(n--)
    {
        *p1-- = *p2--
    }
    return p1;
    #else
    if((&p1 -&p2)

8.实现strtok功能

char* strtok(char* str, const char* delim ){
    assert( delim != NULL);

    const char*  left = NULL
    if(str == NULL)
      str = left;
    if(str == NULL)
        return NULL;
    
    const char* p;
    bool flag;
    while(*str != 0){
        p = delim;
        flag = false;
        while(*p != 0){
            if(*p++ == *str)
            {
                flag = ture;
                break;
            }
        }
        if(!flag) // 这里不能理解
            break;
        ++str;
    }

    char* s = str;
    while(*s != 0)
    {
        p = delim;
        flag = false;
        while( *p != 0)
        {
            if(*p++ = *s)
            {
                flag = true;
                *s = 0;
                break;
            }
        }
        ++s;
        if(flag )
        {
            left = s;
            return str;
        }
    }
    left = NULL;
    return left;
}

9.

你可能感兴趣的:(3. 写代码)