1.sizeof
关于sizeof我们是经常使用的,所以使用方法就不需要提及了,这里我们需要注意的是,sizeof 后面如果是表达式可以不用括号,并且sizeof实际上不参与运算,返回的是内容的类型大小(size_t类型)------sizeof是在编译时就确定结果的
2.scanf/fscanf/sscanf 与 printf/fprintf/sprintf
对于每一个学习C、C++的同学来说,区分上面的内容非常重要!!!
当然实际上还有snprintf这样可以读入读出n个字节的输入输出函数!!!
可以适当留意下输出的对齐符(+-)左右对齐
3.指针(重点)
指针可以说是我们C、C++程序员最重要的知识了,所以我们必须要熟悉其全部内容
首先,指针变量的大小与指针指向的类型无关,只与编译器环境有关(x86/x64)
区分指针数组/数组指针:
指针数组:表示的是一个数组,但是数组的元素都是指针
数组指针:表示一个指针,但是指向的是一个数组
联系知识:
sizeof(arr)/&arr(arr是一个数组):表示的是对数组的整体操作,而非对数组的某个元素操作!!!
函数指针:
规则:指向的函数返回类型 (*函数名)(指向的函数的参数类型与个数)
例如:
int Add(int x,int y) { return x+y; } //需要注意的是:函数名可以表示函数地址 int main() { int (*pf) (int,int) =Add;//定义一个函数指针指向Add函数 //进行相关的操作 return 0; }
4.库函数实现
strlen实现:
//参数是const,注意统计的是'\0'之前的字符数量 size_t strlen(const char* str) { size_t ans = 0; while (*str) { ans++; str++; } return ans; }
现在我们增加难度,如果我要求不能使用计数器来实现strlen,请写出代码:
//参数是const,注意统计的是'\0'之前的字符数量 size_t strlen(const char* str) { size_t ans = 0; while (*str) { ans++; str++; } return ans; }
假如我是面试官,请联系指针特性再写一种方法实现strlen:
size_t strlen(const char* str) { const char* p=str; while(*p) { p++; } return p-str;//指针-指针 }
下面是三个不考的库函数实现:
//1.strcpy实现 char* strcpy(char* dest,const char* src) { //检查: assert(dest!=null); assert(src!=null); //首先用一个新指针记录dest指针起始位置,便于后面dest后移可以找到头部 char* p=dest; while((*dest++=*src++)) { } return p; } //2.strcat实现 char* strcat(char* dest,const char* src) { //检查: assert(dest!=null); assert(src!=null); //同理记录dest起始位置 char* p=dest; //先将dest走空 while(*dest!='\0') { dest++; } //再拷贝 while((*dest++=*src++)) { } return p; } //3.strcmp实现 int strcmp(const char* str1,const char* str2) { //判断: assert(str1!=null); assert(str2!=null); while(*str1==*str2) { //检查:是否已经出现str1为'\0'情况 if(*str1=='\0') return 0; str1++; str2++; } return *str1-*str2; }
对于上面上个库函数的衍生:strncat/strncmp/strncpy
我们是需要保证在n个字符的情况下实现即可!!!
大家可以去参考我之前写的博客:字符串函数详解_tolower 头文件-CSDN博客
下面是几个面试常考的库函数实现:
1.strstr实现:
char* strstr(const char* str1,const char* str2) { //最简单实现:O(N^2) char* p=(char*) str1;//注意强转 //检查str2 if(!*str2) return p; while(*p) { char* s1=p; char* s2=(char*) str2; while(*s1!='\0'&&*s2!='\0'&&(*s1==*s2)) { s1++;s2++; } if(*s2=='\0') return p; p++; } return null; }
5.内存函数
内存函数:memecpy/memset/memmove/memcmp,其中重点考察的是memmove和memcpy,我们这里只讲解重点内容的实现,其他部分可以参考下面链接:
memcpy实现:
//注意点:我们的返回值和参数都是void*,因为我们也不确定要拷贝的类型,num为字节数 void* memcpy(void* dest,const void* src,size_t num) { void* p=dest; //检查: assert(dest); assert(src); while(num--) { *(char*)dest=*(char*)src; dest=(char*)dest+1; src=(char*)src+1; } return p; }
memmove实现:
//注意:返回值和参数都是void* void* memmove(void* dest,const void* src,size_t num) { //检查: assert(dest!=null); assert(src!=null); void* p=dest;//方便返回 //判断是否出现内存重叠 if(dest<=src||(char*)dest>=(char*)src+num) { //无内存重叠 while(num--) { *(char*)dest=*(char*)src; dest=(char*)dest+1; src=(char*)src+1; } } else { //有内存重叠 //此时我们可以从后向前拷贝 dest=(char*)dest+num-1; src=(char*)src+num-1; while(num--) { //拷贝 *(char*)dest=*(char*)src; dest=(char*)dest-1; src=(char*)src-1; } } return p; }
memcpy与memmove区别:
如果memmove函数处理的源内存块和目标内存块是可以重叠的,而memcpy是不处理重叠部分的,对于重叠的是未定义的
6.数据储存
整数的储存:
正整数原反补相同,负数各不相同
原码:将整数按照数值位转变成二进制即可,最高位负数为1,正数为0
反码:符号位(最高位)不变,其余为按位取反(负数)
补码:在反码的基础上+1
注意点:在计算机中整数全部按照补码存取
大小端:
存在原因:对于数据超过一个字节,而内存中一个地址对应一个字节,所以必然产生如何存储顺序的问题。
大端:将数据的低位字节存储在内存地址的高字节中
小端:将数据的高字节存储在在内存地址的低字节中
那么我们如何判断大小端呢?
//通常有以下两种方法 //法一:指针转换法 void Test1() { int i=1;//0x00000001 int ret=*(char*)&i;//将四字节转换为一字节,判断低位是0x00还是0x01 if(ret==1) { //如果是1,说明数据的小端存储在内存的小端 std::cout<<"小端”<<std::endl; } else { //否则说明数据的大端储存在内存的大端 std::cout<<"大端"<<std::endl; } } //法二:联合体判断法 Union { int i; char ch; }UU; void Test2() { UU.i=1; //通过判断ch的值就可以判断大小端,与上面道理相似 if(UU.ch==1) { std::cout<<"小端”<<std::endl; } else { std::cout<<"大端"<<std::endl; } }
下面我们来补充一个知识:大小端转换
这里我们就直接实现:
//现在假设我们要将0x12345678转换为0x78563412 template<size_t T> inline void convert(char* val) { std::swap(*val,*(val+T-1)); convert<T-2>(val+1); } template<class T> inline Apply(T* val) { convert(sizeof(T)>((char*)val); } int main() { int i=0x12345678; Apply<int>(&i); std::cout<<i<<std::endl; return 0; }
最后,感谢大家的支持,祝大家国庆节快乐!!!