结构的一些基本概念:
结构不能比较,因为在内存中他们的成员不一定是连续放置。
结构需按顺序初始化。
结构作为参数时一般是按值传递,也可以作指针或引用传递。
位运算
& 按位与
| 按位或
^ 按位异或
<< 左移位
>> 右移位
~ 求反
按位与通常与一个屏蔽字(mark)一起使用,屏蔽字就是某一位为1的数(其他位为0)。一个整数与这个屏蔽字则除了为1的那位外,其他位都被“屏蔽”掉了。专门测试一个数的某一位是否为1。
利用这点可以写出一种显示整数的二进制数值的方法:
void displayBits(unsigned x)
{
unsigned m,i=1<<15;
for(m=1; m<=16; m++)
{
cout<<(x & i ? '1':'0');
x<<=1;
if(m%8==0)
cout<<' ';
}
cout<<endl;
}
位段(位域)
对于结构或类中的unsigned类型和int类型的成员,C语言允许指定存储他们的位数,指定了存储位数的这些结构的成员或就是位域(位段)。
struct BitCard
{
unsigned face:4; 指定了4位,可表示范围0000~1111 2^4=16
unsigned suit:2; 指定了2位,范围 00~11 2^2=4
unsigned color:1 指定了1位,范围 0~1
};
这整个结构其实只是一个unsigned字节(在32位机上是4个字节)
有个问题,超过了指定位,怎么办?程序会有什么行为?验证之
struct exam
{
unsigned a : 2;
unsigned b : 6;
unsigned c : 4;
};
int main()
{
struct exam rt;
memset(&rt,0,sizeof(rt));
rt.a=31;
cout<<"size="<<sizeof(rt)<<'\n';
cout<<rt.a<<' ,'<<rt.b;
return 0;
}
结果显示:
size=4
3 ,0
其实a存储了31(11111)的最后两位。并且,多出的位数并未占b变量,而是给截断了。各位域间显示出单独性。
位域不能去地址 &.
尽管使用位域能够节省内存,但是会使编译器产生执行速度变慢的机器代码。
字符处理函数
int isdigit(int c) 如果c是一个数字,返回true,否则返回false
int isalpha(int c) 如果c是一个字母,返回true,否则返回false
int isalnum(int c) 如果c是一个字母或数字,返回true,否则返回false
int isxdigit(int c) 如果c是一个十六进制字符,返回true,否则返回false
int islower(int c) 如果c是一个小写字母,返回true,否则返回false
int isupper(int c) 如果c是一个大写字母,返回true,否则返回false
int tolower(int c) 返回其小写字母
int toupper(int c) 返回其大写字母
int isspace(int c) 如果c是一个空白符,返回true,否则返回false。空白符包括:’\n’, 空格,’\t’ , ‘\r’ ,进纸符(’\f’),垂直制表符(‘\v’)
int iscntrl(int c) 如果c是一个控制符,返回true,否则返回false
int ispunct(int c) 如果c是一个除空格、数字和字母外的可打印字符,返回true,否则返回false
int isprint(int c) 如果c是一个可打印符(包括空格),返回true,否则返回false
int isgraph(int c) 如果c是除空格之外的可打印字符,返回true,否则返回false
字符串转换函数
double atof(const char *nPtr) 把字符串nPtr转换为double类型
int atoi(const char* nPtr) 把字符串nPtr转换为int类型
long atol(const char *nPtr) 把字符串nPtr转换为long
double strtod(const char* nPtr,char **endPtr)
把字符串nPtr转换为double类型,endPtr接收nPtr中非数字的部分
long strtol(const char *nPtr,char** endPtr, int base)
把字符串nPtr转换为long类型,endPtr接收nPtr中遇到的第一个非数字到结尾的部分,base是待转换字符串中数值的进制类型,0表示可以是(8、10、16)。也可是2~36中任何值。
unsigned long strtoul(const char* nPtr,char **endPtr, int base)
把字符串nPtr转换为unsigned long类型,endPtr接收nPtr中遇到的第一个非数字到结尾的部分,base是待转换字符串中数值的进制类型,0表示可以是(8、10、16)。也可是2~36中任何值。
字符串处理中的查找函数
char *strchr(const char *s,int c)
查找c在s中首次出现的位置,成功将返回该位置的指针,否则返回NULL
size_t strcspn(const char *s1,const char *s2)
计算并返回s1中不包含s2中任何字符的起始段的长度。即在s1中查找是否有s2的字符,若碰到有该字符则返回从开始(数数1开始)到该字符之前的字符串长度。
size_t strspn(const char *s1,const char *s2)
计算并返回s1中只包含s2中字符的起始段长度。即当在s1中没遇到在s2中的字符时,返回从开始到该字符之前的字符串的长度。
char *strpbrk(const char *s1,const char *s2)
定位字符串s1首次出现在字符串s2中字符的位置。若找到了字符串s2中的字符,返回一个指向字符串s1中该字符的指针,否则返回NULL
char *strrchr(const char *s,int c)
返回c在s中最后一次出现的位置指针,否则返回NULL
char *strstr(const char *s1, const char *s2)
返回s2在s1中首次出现(整个字符串匹配)的位置指针,否则返回NULL。
字符串处理库中的内存函数
void *memcpy(void *s1,const void *s2, size_t n)
把s2所指的对象中的n个字符复制到s1所指的对象中。返回s1结果指针。
void *memmove(void *s1,const void *s2,size_t n)
同memcpy,并且多考虑了重叠情况(Overlapping Buffers)
int memcmp(const void *s1,const void *s2,size_t n)
比较s1和s2指向对象的前n个字符。如果s1所指向对象的字符等于、小于或大于s2所指向对象中的字符,返回值分别等于0、<0 、>0
void *memchr(const char *s,int c,size_t n)
定位s的前n个字符首次出现c的位置。找到就返回指向它的指针,否则返回0
void *memset(void *s, int c,size_t n)
把c复制到s所指的对象的前n个字符中。返回指向结果指针。
这里memcpy和memmove的区分有点特别。从源码看,显然,memmove比memcpy多了处理重叠的情况。但当我从标准库直接调用memcpy的时候,发现它也如同memmove一样处理了重叠的情况。
#include <stdio.h>
#include <string.h>
int main()
{
char x[]="Home Sweet Home";
if(x<&x[5])
memmove(&x[5],x,10);//此处换成memcpy结果一样
printf("%s",x);
return 0;
}
结果:
Home Home Sweet
无论是用vc 还是 gcc 结果都是这样。
但是,如看memcpy的源码,结果应该是Home Home Home 才对。于是直接把源码copy过来用。
#include <stdio.h>
#include <string.h>
void * __cdecl Memcpy (
void * dst,
const void * src,
size_t count
)
{
void * ret = dst;
#if defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC)
{
extern void RtlMoveMemory( void *, const void *, size_t count );
RtlMoveMemory( dst, src, count );
}
#else /* defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC) */
/*
* copy from lower addresses to higher addresses
*/
while (count--) {
*(char *)dst = *(char *)src;
dst = (char *)dst + 1;
src = (char *)src + 1;
}
#endif /* defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC) */
return(ret);
}
int main()
{
char x[]="Home Sweet Home";
if(x<&x[5])
Memcpy(&x[5],x,10);
return 0;
}
结果:Home Home Home
看来,标准库里直接把memcpy作为memmove了。也难怪,其实两个功能几乎一样,后者还考虑了内存重叠情况,使用可能更方便。
其他函数
char *strerror(int errornum)
建立与errornum匹配的字符串(与系统有关),返回指向该字符串的指针。
char *_strerror(char *errMsag)
在输出错误信息前先打印出errMsag信息,也可以为NULL
void perror(const char *msg)
把相关错误信息写到stderr上,在此之前先打印msg信息,如果为NULL则直接打印错误信息。最后自动换行
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
FILE *fp;
if((fp=fopen("tstc","r")) == NULL)
{
perror("open file failed");
printf(strerror(errno));
printf("\n");
printf(_strerror(“open failed”));
}
return 0;
}
结果:
open file failed: No such file or directory
No such file or directory
open failed: No such file or directory
程序操作出现错误时通常会写errno,errno与错误信息表sys_errlist联系,而sys_errlist是以errno为序的一个错误信息列表。