记录了一些原理简单、但是具体实现方式要想一想的问题。

手搓函数系列

不是所有设备都有C库函数,会手搓一部分C库是基本素养。

strlen:字符串长度

1
2
3
4
5
6
7
8
int strlen(char *str){
int len = 0;
while((*str)!='\0'){ //或(*str++)!='\0'
len++;
str++;
}
return len;
}

strcpy:字符串复制函数

注意细节问题:

  • 需要使用staddr保存起始地址,返回的也是该起始地址;

  • 为什么要返回起始地址:其一是因为上述原因,需要起始地址,防止调用时没有保存起始地址;其二可以和其他函数嵌套使用,例如计算长度strlen(strcpy(dest,src));其三也方便进行错误检查。

1
2
3
4
5
6
7
char* strcpy(char*dest,char*src){
if(src==NULL||dest==NULL)
return NULL;
char* staddr = dest;
while((*dest++ = *src++)!='\0'); //注意是先赋值再判断,因此\0也会被复制过去
return staddr;
}

strcat:字符串拼接函数

strcat(char*dest, const char*src)用于将src追加到dest字符串后,实现拼接功能。

注意以下代码调用时,必须提前保证dest的空间是足够dest和src的!即调用只能是:char dest[20] = xx;strcat(dest,src),如果dest是char*,则调用失败。

1
2
3
4
5
6
7
8
9
10
11
12
13
char* strcat(char*dest,const char*src){
if(dest==NULL)
return NULL;
char* staddr = dest;
if(src==NULL)
return staddr;
while((*dest++)!='\0');

dest--; //回到\0前
while((*dest++ = *src++) != '\0');

return staddr;
}

strcmp:字符串比较函数

使用限制:必须保证str1长度长于str2;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int strcmp(const char*str1,const char*str2){
int ret = 0;
//str1到末尾或者str1与str2的ASCII码分出
while(*str1 && !(ret = (*(unsigned char*)str1-*(unsigned char*)str2))){
str1++;
str2++;
}
if(ret<0)
return -1;
else if(ret>0)
return 1;
else
return 0;
}

memset:内存填充函数

1
2
3
4
5
6
7
8
9
void *memset(void*dest,int c,size_t size){  //内存默认是void*类型
if(dest==NULL)
return NULL;
char*staddr = (char*)dest;
while(size--){
*staddr++ = c; //char*类型去++
}
return dest;
}

memcpy:内存复制函数

1
2
3
4
5
6
7
8
9
10
void* memcpy(void*dest,const void*src,size_t size){
if(dest==NULL||src==NULL)
return NULL;
char *stdes = (char*)dest;
char *stsrc = (char*)src;
while(size--){
*stdes++ = *stsrc++;
}
return dest;
}

memcmp:内存比较函数

buf1字符大于buf2,输出正数,相等输出0,否则输出负数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int memcmp(const void*buf1,const void*buf2,size_t size){  //比较内存的size个字符
assert(buf1&&buf2); // #include <cassert>
const unsigned char* p1 = (const unsigned char*)buf1;
const unsigned char* p2 = (const unsigned char*)buf2;
int ret=0;
while(size--){
ret = *p1 - *p2;
if(ret!=0)
break;
p1++;
p2++;
}
return ret;
}

memmove:内存拷贝函数

比起memcpy,只需要考虑多一种情况,即内存的覆盖。如果目的地址在源地址区域的size空间内,memcpy不能正常完成内存复制,因为未来得及负值,源地址空间就被新数据破坏了。在memmove中,如果遇到这种情况,就进行逆序复制,先复制重叠的空间,再拷贝起始空间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void *memmove(void *dest,const void*src,size_t size){
assert(dest&&src);
unsigned char* d = (unsigned char*)dest;
const unsigned char* s = (const unsigned char*)src;
if(d==s||size==0)
return dest;
if(d<s||d>s+size){ //只需要避免一种情况:即目的地址d在src的中间(s<d<s+size)
for(size_t i=0; i<size; i++){ //顺序复制
d[i] = s[i];
}
}
else{ //目的地址在源地址的size空间内,逆序复制
for(size_t i=size-1; i>=0; i--){
d[i] = s[i];
}
}
return dest;
}

atoi:字符串转int函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int myAtoi(char* str) {
long result = 0;
int sign = 1; //符号位
int i = 0;
while(str[i]==' ') i++; //忽略空格
if(str[i]=='-'){ //符号位
sign = -1;
i++;
}
else if(str[i]=='+') i++;
if(str[i]-'0'<0||str[i]-'0'>9) //符号位后出现奇怪字符,错误
return 0;
while(str[i]-'0'>=0&&str[i]-'0'<=9){ //整数字符
result = 10*result + sign*(str[i]-'0'); //计算
if(result>INT_MAX) return INT_MAX; //越界转换
if(result<INT_MIN) return INT_MIN;
i++;
}
return result;
}

C语言特性

判断字节序大小端

1
2
3
4
5
6
7
bool isLittle(){  //判断是否小端序
int a=0x1234;
char t = *((char*)&a); //取最低地址
if(t==0x34) //低地址为低字节,小端
return true;
return false;
}

算法思考

提取整数的质数因子

功能:输入一个正整数,按照从小到大的顺序输出它的所有质因子(重复的也要列举)(如180的质因子为2 2 3 3 5 )

输入描述:

输入一个整数

输出描述:

按照从小到大的顺序输出它的所有质数的因子,以空格隔开。

example:输入:180
输出: 2 2 3 3 5

注意:程序输入elem!=1,或者增加判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
using namespace std;

int main() {
int elem;
cin>>elem;

for(int i=2; i*i<=elem; i++){ //i从2开始,i<=根号elem
if(elem%i==0){
elem /= i; //能被i整除
cout<<i<<' ';
i = 1; //elem/i后可能仍然被i整除,需要全部排除再遍历下一个i,会++再执行下一次循环,因此为1
}
}
cout<<elem<<endl;

return 0;
}