函数原型 memcpy
函数原型
1. memcpy
void *memcpy(void *dest, const void *src, int n);
****功能
从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中
所需头文件
#include
****返回值
函数返回一个指向dest的指针。
****说明
1.source和destin所指内存区域不能重叠,函数返回指向destin的指针。
2.与strcpy相比,memcpy并不是遇到'\0'就结束,而是一定会拷贝完n个字节。
3.如果目标数组destin本身已有数据,执行memcpy()后,将覆盖原有数据(最多覆盖n)。如果要追加数据,则每次执行memcpy后,要将目标数组地址增加到你要追加数据的地址。
//注意,source和destin都不一定是数组,任意的可读写的空间均可。
作用:将s中的字符串复制到字符数组d中。
// memcpy.c
#include
#include
int main()
{
char *s="Golden Global View";
char d[20];
clrscr();
memcpy(d,s,strlen(s));
d[strlen(s)]='\0'; //因为从d[0]开始复制,总长度为strlen(s),d[strlen(s)]置为结束符printf("%s",d);
getchar();
return 0;
}
输出结果:Golden Global View
example2
作用:将s中第14个字符开始的4个连续字符复制到d中。(从0开始) #include
int main()
{
char *s="Golden Global View";
char d[20];
memcpy(d,s+14,4); //从第14个字符(V)开始复制,连续复制4个字符(View) //memcpy(d,s+14*sizeof(char),4*sizeof(char));也可
d[4]='\0';
printf("%s",d);
getchar();
return 0;
}
输出结果:View
example3
作用:复制后覆盖原有部分数据
#include
#include
int main(void)
{
char src[] = "******************************";
char dest[] = "abcdefghijlkmnopqrstuvwxyz0123as6";
printf("destination before memcpy: %s\n", dest);
memcpy(dest, src, strlen(src));
printf("destination after memcpy: %s\n", dest);
return 0;
}
输出结果:
destination before memcpy:abcdefghijlkmnopqrstuvwxyz0123as6
destination after memcpy: ******************************as6
2. malloc函数
本词条主要介绍malloc 函数
Malloc 向系统申请分配指定size个字节的内存空间。返回类型是void* 类型。void* 表示未确定类型的指针。C,C++规定,void* 类型可以强制转换为任何其它类型的指针。
◆目录
◆函数简介
◆函数声明
◆函数的工作机制
◆举例说明
****函数简介
原型:extern void *malloc(unsigned int num_bytes);
头文件:在TC2.0中可以用malloc.h或alloc.h (注意:alloc.h 与malloc.h 的内容是完全一致的),而在Visual C++6.0中可以用malloc.h或者stdlib.h。
功能:分配长度为num_bytes字节的内存块
返回值:如果分配成功则返回指向被分配内存的指针(此存储区中的初始值不确定),否则返回空指针NULL。当内存不再使用时,应使用free()函数将内存块释放。函数返回的指针一定要适当对齐,使其可以用于任何数据对象。
说明:关于该函数的原型,在旧的版本中malloc返回的是char型指针,新的ANSIC标准规定,该函数返回为void型指针,因此必要时要进行类型转换。
名称解释:malloc的全称是memory allocation,中文叫动态内存分配,当无法知道内存具体位置的时候,想要绑定真正的内存空间,就需要用到动态的分配内存。
相关函数:calloc、realloc、free、_alloca
****函数声明
void *malloc(size_t size);
说明:malloc 向系统申请分配指定size个字节的内存空间。返回类型是void* 类型。void* 表示未确定类型的指针。C,C++规定,void* 类型可以强制转换为任何其它类型的指针。
备注:void* 表示未确定类型的指针,更明确的说是指申请内存空间时还不知道用户是用这段空间来存储什么类型的数据(比如是char还是int或者...)从函数声明上可以看出。malloc 和new 至少有两个不同: new 返回指定类
型的指针,并且可以自动计算所需要大小。比如:
int *p;
p = new int; //返回类型为int* 类型(整数型指针),分配大小为sizeof(int);
或:
int* parr;
parr = new int [100]; //返回类型为int* 类型(整数型指针),分配大小为sizeof(int) * 100;
而malloc 则必须要由我们计算字节数,并且在返回后强行转换为实际类型的指针。
int* p;
p = (int *) malloc (sizeof(int)*128);//分配128个(可根据实际需要替换该数值)整型存储单元,并将这128个连续的整型存储单元的首地址存储到指针变量p中
double *pd=(double *) malloc (sizeof(double)*12);//分配12个double型存储单元,并将首地址存储到指针变量pd中
第一、malloc 函数返回的是void * 类型。对于C++,如果你写成:p = malloc (sizeof(int)); 则程序无法通过编译,报错:“不能将void* 赋值给int * 类型变量”。所以必须通过(int *) 来将强制转换。而对于C,没有这个要求,但为了使C程序更方便的移植到C++中来,建议养成强制转换的习惯。
第二、函数的实参为sizeof(int) ,用于指明一个整型数据需要的大小。如果你写成:
int* p = (int *) malloc (1);
代码也能通过编译,但事实上只分配了1个字节大小的内存空间,当你往里头存入一个整数,就会有3个字节无家可归,而直接“住进邻居家”!造成的结果是后面的内存中原有数据内容被改写。
malloc 也可以达到new [] 的效果,申请出一段连续的内存,方法无非是指定你所需要内存大小。
比如想分配100个int类型的空间:
int* p = (int *) malloc ( sizeof(int) * 100 ); //分配可以放得下100个整数的内存空间。
另外有一点不能直接看出的区别是,malloc 只管分配内存,并不能对所得的内存进行初始化,所以得到的一片新内存中,其值将是随机的。
除了分配及最后释放的方法不一样以外,通过malloc或new得到指针,在其它操作上保持一致。
对其做一个特例补充
char *ptr;
if ((ptr = (char *)malloc(0)) == NULL)
puts("Got a null pointer");
else
puts("Got a valid pointer");
此时得到的是Got a valid pointer。把0赋给malloc能得到一个合法的指针。****函数的工作机制
malloc函数的实质体现在,它有一个将可用的内存块连接为一个长长的列表的所谓空闲链表。调用malloc函数时,它沿连接表寻找一个大到足以满足用户请求所需要的内存块。然后,将该内存块一分为二(一块的大小与用户请求的大小相等,另一块的大小就是剩下的字节)。接下来,将分配给用户的那块内存传给用户,并将剩下的那块(如果有的话)返回到连接表上。调用free函数时,它将用户释放的内存块连接到空闲链上。到最后,空闲链会被切成很多的小内存片段,如果这时用户申请一个大的内存片段,那么空闲链上可能没有可以满足用户要求的片段了。于是,malloc函数请求延时,并开始在空闲链上翻箱倒柜地检查各内存片段,对它们进行整理,将相邻的小空闲块合并成较大的内存块。如果无法获得符合要求的内存块,malloc函数会返回NULL指针,因此在调用malloc动态申请内存块时,一定要进行返回值的判断。
Linux Libc6采用的机制是在free的时候试图整合相邻的碎片,使其合并称为一个较大的free空间。
****举例说明
正常片段:
typedef struct data_type{
int age;
char name[20];
} data;
data *bob;
bob = (data *) malloc( sizeof(data) );
if( bob != NULL ) {
bob->age = 22;
strcpy( bob->name, "Robert" );
printf( "%s is %d years old\n", bob->name, bob->age );
}else{
printf("malloc error!\n");
exit(1);
}
free( bob );
内存泄漏实例:?>????????????????????????
#include
#include
#define MAX 100000000
int main(void) {
int *a[MAX];
int i;
for( i=0; i {a[i] = (int *)malloc( MAX ); } return 0; } 3. 函数sprintf 字串格式化命令,主要功能是把格式化的数据写入某个字符串中。sprintf 是个变参函数,使用时经常出问题,而且只要出问题通常就是能导致程序崩溃的内存访问错误,但好在由sprintf 误用导致的问题虽然严重,却很容易找出,无非就是那么几种情况,通常用眼睛再把出错的代码多看几眼就看出来了。 ◆目录 ◆函数简介 ◆参数说明及应用举例 ◆转换字符 ◆格式化数字字符串 ◆字符/Ascii 码对照 ◆使用sprintf 的常见问题 ◆函数简介 ◆参数说明及应用举例 ◆转换字符 ◆格式化数字字符串 ◆字符/Ascii 码对照 ◆使用sprintf 的常见问题 展开 ****函数简介 函数功能:把格式化的数据写入某个字符串 头文件:stdio.h 函数原型:int sprintf( char *buffer, const char *format, [ argument] … ) ; 返回值:字符串长度(strlen) 相关函数:[1] int sprintf_s(char *buffer,size_t sizeOfBuffer,const char *format, [argument] ... ); int _sprintf_s_l(char *buffer,size_t sizeOfBuffer,const char *format,locale_t locale ,[argument] ... ); int swprintf_s(wchar_t *buffer,size_t sizeOfBuffer,const wchar_t *format ,[argument]...); int _swprintf_s_l(wchar_t *buffer,size_t sizeOfBuffer,const wchar_t *format,locale_t locale ,[argument]…); template int sprintf_s(char (&buffer)[size],const char *format, [argument] ... ); // C++ only template int swprintf_s(wchar_t (&buffer)[size],const wchar_t *format ,[argument]...); // C++ only ****参数说明及应用举例 sprintf格式的规格如下所示。[]中的部分是可选的。 %[指定参数][标识符][宽度][.精度]指示符 若想输出`%'本身时, 请这样`%%'处理。 1. 处理字符方向。负号时表示从后向前处理。 2. 填空字元。0 的话表示空格填0;空格是内定值,表示空格就放着。 3. 字符总宽度。为最小宽度。 4. 精确度。指在小数点后的浮点数位数。 =-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 转换字符 =-=-=-=-=-=-=-=-=-=-=-=-=-=-=- %% 印出百分比符号,不转换。 %c 整数转成对应的ASCII 字元。 %d 整数转成十进位。 %f 倍精确度数字转成浮点数。 %o 整数转成八进位。 %s 整数转成字符串。 %x 整数转成小写十六进位。 %X 整数转成大写十六进位。 =-=-=-=-=-=-=-=-=-=-=-=-=-=-=- $money = 123.1 $formatted = sprintf ("%06.2f", $money); // 此时变数$ formatted 值为"123.10" $formatted = sprintf ("%08.2f", $money); // 此时变数$ formatted 值为"00123.10" $formatted = sprintf ("%-08.2f", $money); // 此时变数$ formatted 值为 "123.1000" $formatted = sprintf ("%.2f%%", 0.95 * 100); // 格式化为百分比 ?> ¢%08.2f 解释: %开始符 0是"填空字元" 表示,如果长度不足时就用0来填满。 8格式化后总长度 2f小数位长度,即2位 ¢第3行值为"00123.10" 解释: 因为2f是(2位)+小数点符号(1)+前面123(3位)=6位,总长度为8位,故前面用[填空字元]0表示,即00123.10 ¢第4行值为"123.1000" 解释: -号为反向操作,然后填空字元0添加在最后面了 /******************************************************** 以下选自《CSDN 社区电子杂志——C/C++杂志》 *********************************************************/ 在将各种类型的数据构造成字符串时,sprintf 的强大功能很少会让你失望。由于sprintf 跟printf 在用法上几乎一样,只是打印的目的地不同而已,前者打印到字符串中,后者则直接在命令行上输出。这也导致sprintf 比printf 有用得多。 sprintf 是个变参函数,定义如下: int sprintf( char *buffer, const char *format [, argument] ... ); 除了前两个参数类型固定外,后面可以接任意多个参数。而它的精华,显然就在第二个参数: 格式化字符串上。 printf 和sprintf 都使用格式化字符串来指定串的格式,在格式串内部使用一些以“%”开头的格式说明符(format specifications)来占据一个位置,在后边的变参列表中提供相应的变量,最终函数就会用相应位置的变量来替代那个说明符,产生一个调用者想要的字符串。 格式化数字字符串 sprintf 最常见的应用之一莫过于把整数打印到字符串中,所以,sprintf 在大多数场合可以替代 itoa。 如: //把整数123 打印成一个字符串保存在s 中。 sprintf(s, "%d", 123); //产生"123" 可以指定宽度,不足的左边补空格: sprintf(s, "%8d%8d", 123, 4567); //产生:" 123 4567" 当然也可以左对齐: sprintf(s, "%-8d%8d", 123, 4567); //产生:"123 4567" 也可以按照16 进制打印: sprintf(s, "%8x", 4567); //小写16 进制,宽度占8 个位置,右对齐 sprintf(s, "%-8X", 4568); //大写16 进制,宽度占8 个位置,左对齐 这样,一个整数的16 进制字符串就很容易得到,但我们在打印16 进制内容时,通常想要一种左边补0 的等宽格式,那该怎么做呢?很简单,在表示宽度的数字前面加个0 就可以了。 sprintf(s, "%08X", 4567); //产生:"000011D7" 上面以”%d”进行的10 进制打印同样也可以使用这种左边补0 的方式。 这里要注意一个符号扩展的问题:比如,假如我们想打印短整数(short)-1 的内存16 进制表示形式,在Win32 平台上,一个short 型占2 个字节,所以我们自然希望用4 个16 进制数字来打印它: short si = -1; sprintf(s, "%04X", si); 产生“FFFFFFFF”,怎么回事?因为spritnf 是个变参函数,除了前面两个参数之外,后面的参数都不是类型安全的,函数更没有办法仅仅通过一个“%X”就能得知当初函数调用前参数压栈时被压进来的到底是个4 字节的整数还是个2 字节的短整数,所以采取了统一4 字节的处理方式,导致参数压栈时做了符号扩展,扩展成了32 位的整数-1,打印时4 个位置不够了,就把32 位整数-1 的8 位16 进制都打印出来了。 如果你想看si 的本来面目,那么就应该让编译器做0 扩展而不是符号扩展(扩展时二进制左边补0 而不是补符号位): sprintf(s, "%04X", (unsigned short)si); 就可以了。或者: unsigned short si = -1; sprintf(s, "%04X", si); sprintf 和printf 还可以按8 进制打印整数字符串,使用”%o”。注意8 进制和16 进制都不会打印出负数,都是无符号的,实际上也就是变量的内部编码的直接的16 进制或8 进制表示。 控制浮点数打印格式 浮点数的打印和格式控制是sprintf 的又一大常用功能,浮点数使用格式符”%f”控制,默认保留小数点后6 位数字,比如: sprintf(s, "%f", 3.1415926); //产生"3.141593" 但有时我们希望自己控制打印的宽度和小数位数,这时就应该使用:”%m /nf”格式,其中m 表示打印的宽度,n 表示小数点后的位数。比如:sprintf(s, "%10.3f", 3.1415626); //产生:" 3.142" sprintf(s, "%-10.3f", 3.1415626); //产生:"3.142 " sprintf(s, "%.3f", 3.1415626); //不指定总宽度,产生:"3.142" 注意一个问题,你猜 int i = 100; sprintf(s, "%.2f", i); 会打出什么东东来?“100.00”?对吗?自己试试就知道了,同时也试试下面这个: sprintf(s, "%.2f", (double)i); 第一个打出来的肯定不是正确结果,原因跟前面提到的一样,参数压栈时调用者并不知道跟i相对应的格式控制符是个”%f”。而函数执行时函数本身则并不知道当年被压入栈里的是个整数,于是可怜的保存整数i 的那4 个字节就被不由分说地强行作为浮点数格式来解释了,整个乱套了。不过,如果有人有兴趣使用手工编码一个浮点数,那么倒可以使用这种方法来检验一下你手工编排的结果是否正确。 字符/Ascii 码对照 我们知道,在C/C++语言中,char 也是一种普通的scalable 类型,除了字长之外,它与short,int,long 这些类型没有本质区别,只不过被大家习惯用来表示字符和字符串而已。(或许当年该把这个类型叫做“byte”,然后现在就可以根据实际情况,使用byte 或short 来把char 通过typedef 定义出来,这样更合适些)于是,使用”%d”或者”%x”打印一个字符,便能得出它的10 进制或16 进制的ASCII 码;反过来,使用”%c”打印一个整数,便可以看到它所对应的ASCII字符。以下程序段把所有可见字符的ASCII 码对照表打印到屏幕上(这里采用printf,注意”#”与”%X”合用时自动为16 进制数增加”0X”前缀):for(int i = 32; i < 127; i++) { printf("[ %c ]: %3d 0x%#04X\n", i, i, i); } 连接字符串 sprintf 的格式控制串中既然可以插入各种东西,并最终把它们“连成一串”,自然也就能够连接字符串,从而在许多场合可以替代strcat,但sprintf 能够一次连接多个字符串(自然也可以同时在它们中间插入别的内容,总之非常灵活)。比如: char* who = "I"; char* whom = "CSDN"; sprintf(s, "%s love %s.", who, whom); //产生:"I love CSDN. " strcat 只能连接字符串(一段以??结尾的字符数组或叫做字符缓冲,null-terminated-string),但有时我们有两段字符缓冲区,他们并不是以??结尾。比如许多从第三方库函数中返回的字符数组,从硬件或者网络传输中读进来的字符流,它们未必每一段字符序列后面都有个相应的??来结尾。如果直接连接,不管是sprintf 还是strcat 肯定会导致非法内存操作,而strncat 也至少要求第 一个参数是个null-terminated-string,那该怎么办呢?我们自然会想起前面介绍打印整数和浮点数时可以指定宽度,字符串也一样的。比如: char a1[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G'}; char a2[] = {'H', 'I', 'J', 'K', 'L', 'M', 'N'}; 如果: sprintf(s, "%s%s", a1, a2); //Don't do that! 十有八九要出问题了。是否可以改成: sprintf(s, "%7s%7s", a1, a2); 也没好到哪儿去,正确的应该是: sprintf(s, "%.7s%.7s", a1, a2);//产生:"ABCDEFGHIJKLMN" 这可以类比打印浮点数的”%m/nf”,在”%m.ns”中,m 表示占用宽度(字符串长度不足时补空格,超出了则按照实际宽度打印),n 才表示从相应的字符串中最多取用的字符数。通常在打印字符串时m 没什么大用,还是点号后面的n 用的多。自然,也可以前后都只取部分字符: sprintf(s, "%.6s%.5s", a1, a2);//产生:"ABCDEFHIJKL" 在许多时候,我们或许还希望这些格式控制符中用以指定长度信息的数字是动态的,而不是静态指定的,因为许多时候,程序要到运行时才会清楚到底需要取字符数组中的几个字符,这种动态的宽度/精度设置功能在sprintf 的实现中也被考虑到了,sprintf 采用”*”来占用一个本来需要一个指定宽度或精度的常数数字的位置,同样,而实际的宽度或精度就可以和其它被打印的变量一样被提供出来,于是,上面的例子可以变成: sprintf(s, "%.*s%.*s", 7, a1, 7, a2); 或者: sprintf(s, "%.*s%.*s", sizeof(a1), a1, sizeof(a2), a2); 实际上,前面介绍的打印字符、整数、浮点数等都可以动态指定那些常量值,比如: sprintf(s, "%-*d", 4, 'A'); //产生"65 " sprintf(s, "%#0*X", 8, 128); //产生"0X000080","#"产生0X sprintf(s, "%*.*f", 10, 2, 3.1415926); //产生" 3.14" 打印地址信息 有时调试程序时,我们可能想查看某些变量或者成员的地址,由于地址或者指针也不过是个32 位的数,你完全可以使用打印无符号整数的”%u”把他们打印出来: sprintf(s, "%u", &i); 不过通常人们还是喜欢使用16 进制而不是10 进制来显示一个地址: sprintf(s, "%08X", &i); 然而,这些都是间接的方法,对于地址打印,sprintf 提供了专门的”%p”:sprintf(s, "%p", &i); 我觉得它实际上就相当于: sprintf(s, "%0*x", 2 * sizeof(void *), &i); 利用sprintf 的返回值 较少有人注意printf/sprintf 函数的返回值,但有时它却是有用的,spritnf 返回了本次函数调用最终打印到字符缓冲区中的字符数目。也就是说每当一次sprinf 调用结束以后,你无须再调用一次strlen 便已经知道了结果字符串的长度。如: int len = sprintf(s, "%d", i); 对于正整数来说,len 便等于整数i 的10 进制位数。 下面的是个完整的例子,产生10 个[0, 100)之间的随机数,并将他们打印到一个字符数组s 中, 以逗号分隔开。 #include #include #include int main() { srand(time(0)); char s[64]; int offset = 0; for(int i = 0; i < 10; i++) { offset += sprintf(s + offset, "%d,", rand() % 100); } s[offset - 1] = '\n';//将最后一个逗号换成换行符。 printf(s); return 0; } 设想当你从数据库中取出一条记录,然后希望把他们的各个字段按照某种规则连接成一个字符串时,就可以使用这种方法,从理论上讲,他应该比不断的strcat 效率高,因为strcat 每次调用都需要先找到最后的那个??的位置,而在上面给出的例子中,我们每次都利用sprintf 返回值把这个位置直接记下来了。 MSDN中例子: // crt_sprintf.c// compile with: /W3// This program uses sprintf to format various// data and place them in the string named buffer. #include int main( void ) { char buffer[200], s[] = "computer", c = 'l'; int i = 35, j; float fp = 1.7320534f; // Format and print various data: j = sprintf( buffer, " String: %s\n", s ); // C4996 j += sprintf( buffer + j, " Character: %c\n", c ); // C4996 j += sprintf( buffer + j, " Integer: %d\n", i ); // C4996 j += sprintf( buffer + j, " Real: %f\n", fp );// C4996 // Note: sprintf is deprecated; consider using sprintf_s instead printf( "Output:\n%s\ncharacter count = %d\n", buffer, j ); } Copy Output: String: computer Character: l Integer: 35 Real: 1.732053 character count = 79 ****使用sprintf 的常见问题 sprintf 是个变参函数,使用时经常出问题,而且只要出问题通常就是能导致程序崩溃的内存访 问错误,但好在由sprintf 误用导致的问题虽然严重,却很容易找出,无非就是那么几种情况,通 常用眼睛再把出错的代码多看几眼就看出来了。 sprintf_s()是sprintf()的安全版本,通过指定缓冲区长度来避免sprintf()存在的溢出风险。在使用VS2008时如果你使用了sprintf函数,那么编译器会发出警告:使用sprintf存在风险,建议使用sprintf_s。这个安全版本的原型是:int sprintf_s(char *buffer,size_t sizeOfBuffer,const char *format [,argument] ... ); 缓冲区溢出 第一个参数的长度太短了,没的说,给个大点的地方吧。当然也可能是后面的参数的问 题,建议变参对应一定要细心,而打印字符串时,尽量使用”%.ns”的形式指定最大字符数。 忘记了第一个参数 低级得不能再低级问题,用printf 用得太惯了。//偶就常犯。:。( 变参对应出问题 通常是忘记了提供对应某个格式符的变参,导致以后的参数统统错位,检查检查吧。尤 其是对应”*”的那些参数,都提供了吗?不要把一个整数对应一个”%s”,编译器会觉得你 欺她太甚了(编译器是obj 和exe 的妈妈,应该是个女的,:P)。 strftime sprnitf 还有个不错的表妹:strftime,专门用于格式化时间字符串的,用法跟她表哥很像,也 是一大堆格式控制符,只是毕竟小姑娘家心细,她还要调用者指定缓冲区的最大长度,可能是为 了在出现问题时可以推卸责任吧。这里举个例子: time_t t = time(0); //产生"YYYY-MM-DD hh:mm:ss"格式的字符串。 char s[32]; strftime(s, sizeof(s), "%Y-%m-%d %H:%M:%S", localtime(&t)); sprintf 在MFC 中也能找到他的知音:CString::Format,strftime 在MFC 中自然也有她的同道: CTime::Format,这一对由于从面向对象哪里得到了赞助,用以写出的代码更觉优雅。 4. 任务间通信(2) (1) 互斥 二进制信号量能有效地对共享资源的访问进行互锁。与禁止中断和抢占上锁不同,二进制信号量仅限制了相关资源的互斥范围。使用该技术时需创建信号量来保护资源,信号量在最初创建时状态为可用(full)。 /*includes 语句*/ #include “vxworks.h” #include “semLib.h” SEM_ID semMutex; /*创建一个二进制信号量,该信号量最初为可用的 阻塞在信号量上,按优先级顺序等待信号量 */ semMutex = semBCreate(SEM_Q_PRIORITY,SEM_FULL); 当任务访问资源时,首先必须获得信号量。只要任务持有信号量,其他所有需要访问该资源的任务将被阻塞。当该任务结束资源访问时释放信号量,允许其他任务访问资源。 因此,对资源的所有访问需要互斥时,可使用函数semTake()和semGive()来实现。 semTake(semMutex, WAIT_FOREVER); . . /* 临界区域,任何时候仅单个任务可以访问*/ . semGive(semMutex); (2) 同步 当信号量用于任务同步时,信号量可用作任务等待的一个状态或事件。初始时信号量不可用。任务或中断服务程序通过释放信号量来表明事件的发生(对于完整的中断服务程序的讨论)。调用semTake()函数提取信号量的其他任务处于等待状态,这些等待任务被阻塞,直至事件发生,并释放信号量。 注意,信号量在互斥和同步时状态次序不同。对于互斥,信号量初始时为可用(full),每个任务先提取信号量,然后再释放。但是对于同步,信号量初始时为不可用(empty),每个任务都需要等待提取被其他任务释放的信号量。 在例2-1中,init()函数建立了一个二进制信号量,把中断服务程序与一个事件相连接,并发起一个任务去处理该事件。函数task1()一直运行到调用semTake()函数。把这个点上,除非有事件发生并引起中断服务程序调用函数semGive(),否则将一直处于阻塞状态。当完成中断服务程序后,task1()函数恢复执行,处理该事件。在一个专用的任务上下文中处理这种事件具有一个优点:中断级上的任务处理少,因而减少了中断响应时间。这种事件的处理非常适合于实时系统应用。 例2-1:使用信号量实现任务同步 /*本例显示了信号量使用于同步的用途*/ /*includes语句*/ #include “vxWorks.h” #include “semLib.h” #include “arch/arch/ivarch.h” /*用结构体类型代替arch*/ SEM_ID syncSem /*同步信号量ID*/ init ( int someIntNum ) { /*连接中断服务函数*/ intConnect(INUM_TO_IVEC(someIntNum), eventInterruptSvcRout, 0); /*建立信号量*/ syncSem = semBCreate(SEM_Q_FIFO, SEM_EMPTY); /*发起用于同步的任务*/ taskSpawn(“sample”, 100, 0, 20000, task1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0); } task1(void) { … semTake(syncSem, WAIT_FOREVER); /*等待事件发生*/ printf(“tas k 1 got the semaphore\n”); … /*启动事件*/ } eventInterruptSvcRout(void) { … semGive(syncSem); /*让任务1启动事件*/ … } 广播同步允许所有阻塞在同一个信号量上的任务自动解除阻塞。这种同步方式正确的应用行为经常需要一个任务集合,在集合中任何一个任务有机会处理更多事件之前,任务集合将整体地处理一个事件。semFlush()函数是通过解除信号量上所有任务的阻塞状态从而实现同步的。 1.互斥信号量 互斥信号量是一种用于解决内在互斥问题的特殊的二进制信号量,包括优先级倒置,删除安全以及资源的递归访问。 互斥信号量的基本行为与二进制信号量一致,不同之外如下: 它仅用于互斥; 它仅能由提取它(即调用semTake())的任务释放; 不能在中断服务程序中释放; semFlush()函数操作非法。 (1)优先级倒置 图2-11表示了优先级的倒置状态。 当一个高优先级任务需要等待一段不确定时间,让低优先级任务完成时,需要发生优先级倒置。考虑到如图2-11所示的情况,t1, t2和t3分别是优先级为高,中和低的三个任务。t3占有由二进制信号量保护的某种资源,从而进入临界状 态;当t1抢占了t3,由于需要提取相同的信号量从而引发竞争时,t1被阻塞了。如果能保证t1在t3正常完成该资源之前被阻塞,将不会出现资源抢占,也就不会出现上述问题。但是,由于低优先级任务容易被中等优先级的任务(如t2)抢占,t3可能会再次被阻塞。一旦发生这样的情况,受阻塞的t1可能又要等待一段不确定的时间。 互斥信号量选项SEM_INVERSION_SAF能够继承优先级算法。优先级继承协议确保在资源阻塞的所有任务中优先级最高的且拥有资源执行资格的任务将优先执行。一旦任务的优先级被提高,它以提高后的优先级执行;直到释放其占有的全部互斥信号量后,该任务将返回到正常或者标准的优先级。因此“继承的任务”被保护以防止被任何中间优先级的任务抢占。该选项要求与优先级队列(SEM_Q_PRIORITY)一起使用。 图2-12中,当t1因信号量阻塞时,通过把t3优先级提升到t1优先级,从而解决了优先级倒置问题。这样不仅保护了t3;而且间接地保护t1, 以防止被t2抢占。 下例用优先级继承算法创建了一个互斥信号量: semId = semMCreate(SEM_Q_PRIORITY |SEM_INVERSION_SAFE); (2)删除安全 互斥的另一个问题涉及到任务的删除。一个受信号量保护的临界区域内经常需要保护执行任务避免被意外地删除。删除一个在临界区执行的任务可能会导致意想不到的后果。资源可能处于破坏状态,同时受保护的信号量不再可用,直接终止对该资源的所有访问。 原语taskSafe()和taskUnSafe()提供了一种任务删除的方法。但是在使用互斥信号量选项SEM_DELETE_SAFE时,每次使用semTake()将隐含调用taskSafe(),使用semGive()将隐含调用taskUnsafe()。使用这种方式,任务在占用信号量时不会被删除。该选项由于最终代码进入内核的部分较少,因此比使用原语taskSafe()和taskUnSafe()更加有效。 semId = semMCreate(SEM_Q_FIFO |SEM_DELETE_SAFE); (3)递归资源访问 互斥信号量能够递归获得。这意味着持有信号量的任务在最终释放其之前能够 多次地提取。递归非常适用于一组需要相互调用的子程序同时又需进行资源互斥访问的情况。由于系统保持跟踪当前哪个任务持有互斥信号量,因此这是能够实现的。 在释放信号量之前,递归获取的互斥信号量被释放和提取的次数应该相等。这通过一个计数器跟踪实现,每调用semTake()一次计数器加一,每调用semGive()一次计数器减一。 例2-2: 互斥信号量的递归使用 /* 函数A访问某资源时需要提取mySem; 函数A需要调用函数B,函数B也需要提取mySem */ /* includes */ #include “vxWorks.h” #include “semLib.h” SEM_ID mySem; /**/ init() { mySem = semMCreate(SEM_Q_PRIORITY); } funcA() { semTake(mySem, WAIT_FOREVER); printf(“funcA: Got mutual-exclusionsemaphore\n”); … funcB(); … semGive(mySem); printf(“funcA: Released mutual-exclusionsemaphore\n”); } funcB() { semTake(mySem, WAIT_FOREVER); printf(“funcB: Got mutual-exclusionsemaphore\n”); … semGive(mySem); printf(“funcB: Releases mutual-exclusionsemaphore\n”); } 2.计数器信号量 计数器信号量是实现任务同步和互斥的另一种手段。计数器信号量与二进制信 号量相似,它还跟踪信号量被释放的次数。每释放一个信号量,计数器加一;每提取一个信号量,计数器减一。当计数器计数为零时,试图提取信号量的任务将被阻塞。与二进制信号量一样,如果信号量被释放时存在被阻塞的任务,那么被阻塞的任务将被解除阻塞。然而,与二进制信号量不同的是,如果信号量被释放时不存在阻塞的任务,那么计数器将加一。这意味着一个被释放二次的信号量,可以无阻塞地被提取二次。表2-13列出了初始值为3的计数器信号量被任务提取和释放时的时间顺序。 计数器信号量适用于保护多份复制的资源。例如,可以使用一个初始值为5的计数器信号量来协调5个磁带驱动器工作;或使用初始值为256的计数器信号量来实现一个有256个入口的环形缓冲器。计数器信号量的初始值以参数形式用于semCCreate()中。 1.特定信号量选项 Wind标准信号量接口包括两个特定的选项。 (1)超时 信号量不可超时,超时可作为阻塞状态时另一种解决方法。提取信号量可限制在一段特定的时间内;若在该时间段内没有提取信号量,则意味着操作失败。这种行为由semTake()的参数控制的;该参数为指定的tick时间数目,表示任务在阻塞状态等待的时间。若任务在分配时间内成功提取信号量,semTake()返回OK。当信号量规定的超时值已过,semTake()返回ERROR,系统设置errno值。一个以NO_WAIT(0)为参数的semTake()调用意味着系统不需要等待,若调用不可用,设置errno值为S_objLib_OBJ_UNA V AILABLE。一个参数为正超时值的semTake()调用,若调用同样不可用,则返回S_objLib_OBJ_TIMEOUT。一个以WAIT_FOREVER(-1)为超时值的调用表示无限期的等待。 (2)队列 Wind信号量能够对阻塞在信号量上的任务进行排队。它们基于先进先出顺序或者优先级顺序之一进行排队,请参考图2-13 优先级顺序更好地维护了系统优先级构造的意图,但却以调用semTake()进行优先级的排序为代价。一个先进先出队列则不需要排队开销,而且能保证恒定的 时间性能。使用semBCreate(), semMCreate()或semCCreate()建立信号量时确定队列类型。使用优先级继承选项(SEM_INVERSION_SAFE)的信号量必须选择优先级顺序队列。 1.信号量和VxWorks事件 本节描述如何使用信号量处理VxWorks事件,当然也可以使用VxWorks其他结构体处理VxWorks事件。 (1)使用事件 如果任务请求给其发送事件,那么可用信号量来给任务发送事件。为能获得信号量发送的事件,任务通过信号量调用semEvStart()来寄存;从那点开始,每次调用semGive()来释放信号量;只要没有其他任务阻塞在该信号量上,信号量将给已寄存的任务发送事件。调用semEvStop(), 信号量停止发送事件。 任务时候每个信号量仅能寄存一个任务。使用eventLib库中的函数,能够恢复信号量给任务发送事件。 在某些应用中,信号量建立者希望知道何时信号量发送事件将会失败。如果任务通过信号量寄存,且随后在取消任务寄存之前被删除,那么这种假想能够实现。在这种情况下,一个给定操作可能会导致信号量给已经被删除的任务发送事件。这种做法显然不适合。如果用SEM_EVENTSEND_ERROR_NOTIFY选项建立信号量,给定的操作将返回一个错误,否则vxWorks将自动处理错误。使用eventReceive()函数,任务可能被信号量发送的事件阻塞。如果删除该信号量,与阻塞在信号量上的任务一样,阻塞在事件上的任务返回到就绪状态。(2)退出VxWorks API VxWorks事件执行时不要求跟踪当前任务寄存的所有资源。因此,资源能够给不存在的任务发送事件。例如,一个任务可能被删除或可能进行了自析构操作,但仍然寄存在接收事件的资源内,这种错误只能在释放资源时发现,通过semGive()汇报返回ERROR。然而在这种情况下,错误并意味着没有释放信号量,或者信息没有被合理地传递;仅仅意味着资源不能给寄存的任务发送事件。这种行为与当前VxWorks操作系统里存在的行为不同,但与pSOS消息队列和信号量的行为相同。 (3)性能影响 当任务被信号量阻塞时,调用semGive()不产生性能影响。但不明该情况时(例如,信号量空闲),因为必须给任务发送事件,调用semGive()需要花费更长的时间。而且调用可能挂起等待事件的任务,即意味会出现抢占调用;甚至出现无任务等待信号量的情况。 任务等待从信号量发送事件时,因需唤醒任务,所以semDestroy()的性能会受到影响。注意,在该情况下不要再发送事件。 时间延迟函数 函数名: delay 功能: 将程序的执行暂停一段时间(毫秒) 用法: void delay(unsigned millis econds); 重画屏幕区域的函数 函数名:getimage 功能:将指定区域的一个位图存到主存中 用法:void far getimage( int left, int top, int right, int bottom, v oid far *bitmap); 函数名:putimage 功能:在屏幕上输出一个位图 用法:void far putimage( int x, i nt y, void far *bitmap, int op ); 图像大小函数 函数名: imagesize 功能: 返回保存位图像所需的字节数 用法: unsigned far imagesize( in t left, int top, int right, int bo ttom ); 异或模式函数 函数名: setwritemode 功能: 设置图形方式下画线的输出模式 用法: void far setwritemode(int mode); 参数MODE可以被设置位COPY_PUT 或者XOR_PUT两种模式。当mode被设置为XOR_PUT,其后的图形操作将都采用异或方式。此外之前提到的putimage()函数也可以采用异或模式向屏幕复制图像。 检测键盘输入函数 函数名: kbhit 功能: 检查当前按下的键 用法: int kbhit(void); 键盘接口函数 函数名: bioskey 功能: 直接使用BIOS服务的键盘接口用法: int bioskey(int cmd); 该函数通过bois中断0x16执行键盘操作,由参数cmd来决定具体的操作。Cmd 具体操作 0 读取按键的ascii码 1 测试是否有按键如果没有按键返回0 如果按键为ctrl+brk 返回-1 如果是其他按键返回按键本身键值(直到此按键被取出后恢复0) 2 返回shift key 状态 以下是当cmd为2的时候,返回值的具体含义 cmd返回值触发特殊键 常用C语言标准库函数2012 C语言编译系统提供了众多的预定义库函数和宏。用户在编写程序时,可以直接调用这些库函数和宏。这里选择了初学者常用的一些库函数,简单介绍了各函数的用法和所在的头文件。 1.测试函数 Isalnum 原型:int isalnum(int c) 功能:测试参数c是否为字母或数字:是则返回非零;否则返回零 头文件:ctype.h Isapha 原型:int isapha(int c) 功能:测试参数c是否为字母:是则返回非零;否则返回零 头文件:ctype.h Isascii 原型:int isascii(int c) 功能:测试参数c是否为ASCII码(0x00~0x7F):是则返回非零;否则返回零 头文件:ctype.h Iscntrl 原型:int iscntrl(int c) 功能:测试参数c是否为控制字符(0x00~0x1F、0x7F):是则返回非零;否则返回零 头文件:ctype.h Isdigit 原型:int isdigit(int c) 功能:测试参数c是否为数字:是则返回非零;否则返回零。 头文件:ctype.h Isgraph 原型:int isgraph(int c) 功能:测试参数c是否为可打印字符(0x21~0x7E):是则返回非零;否则返回零头文件:ctype.h Islower 原型:int islower(int c) 功能:测试参数c是否为小写字母:是则返回非零;否则返回零 头文件:ctype.h Isprint 原型:int isprint(int c) 功能:测试参数c是否为可打印字符(含空格符0x20~0x7E):是则返回非零;否则返回零 头文件:ctype.h Ispunct 原型:int ispunct(int c) 功能:测试参数c是否为标点符号:是则返回非零;否则返回零 头文件:ctype.h Isupper 原型:int isupper(inr c) 功能:测试参数c是否为大写字母:是则返回非零;否则返回零 Isxdigit 原型:int isxdigit(int c) 功能:测试参数c是否为十六进制数:是则返回非零;否则返回零 2.数学函数 abs 原型:int abs(int i) 功能:返回整数型参数i的绝对值 头文件:stdlib.h,math.h acos 原型:double acos(double x) 功能:返回双精度参数x的反余弦三角函数值 头文件:math.h asin 原型:double asin(double x) 功能:返回双精度参数x的反正弦三角函数值 头文件:math.h atan 原型:double atan(double x) 功能:返回双精度参数的反正切三角函数值 头文件:math.h atan2 原型:double atan2(double y,double x) 功能:返回双精度参数y和x由式y/x所计算的反正切三角函数值 头文件:math.h cabs memset函数使用详解 今天做题卡在MEMSET函数的使用上了,本来以为int a[100]; memset(a, MAX,sizeof(a));就是将a数组全部赋值为MAX,现在才知道他的填充是以字节为单位,一般用于对字符型变量的初值进行赋值. 更详细的解说见百度百科--> https://www.360docs.net/doc/fb3538862.html,/view/982208.htm 1。void *memset(void *s,int c,size_t n) 总的作用:将已开辟内存空间s 的首n 个字节的值设为值c。 2。例子 #include void main(){ char *s="Golden Global View"; clrscr(); memset(s,'G',6); printf("%s",s); getchar(); return 0; } 3。memset() 函数常用于内存空间初始化。如: char str[100]; memset(str,0,100); 4。memset()的深刻内涵:用来对一段内存空间全部设置为某个字符,一般用在对定义的字符串进行初始化为‘ ’或‘/0’;例:char a[100];memset(a, '/0', sizeof(a)); memcpy用来做内存拷贝,你可以拿它拷贝任何数据类型的对象,可以指定拷贝的数据长度;例:char a[100],b[50]; memcpy(b, a, sizeof(b));注意如用sizeof(a),会造成b的内存地址溢出。 strcpy就只能拷贝字符串了,它遇到'/0'就结束拷贝;例:char a[100],b[50];strcpy(a,b);如用strcpy(b,a),要注意a中的字符串长度(第一个‘/0’之前)是否超过50位,如超过,则会造成b 的内存地址溢出。 5.补充:一点心得 memset可以方便的清空一个结构类型的变量或数组。 如: strcpy ,strncpy ,strlcpy地用法 好多人已经知道利用strncpy替代strcpy来防止缓冲区越界。 但是如果还要考虑运行效率的话,也许strlcpy是一个更好的方式。 1. strcpy 我们知道,strcpy 是依据/0 作为结束判断的,如果to 的空间不够,则会引起buffer overflow。strcpy 常规的实现代码如下(来自OpenBSD 3.9): char * strcpy(char *to, const char *from) { char *save = to; for (; (*to = *from) != '/0'; ++from, ++to); return(save); } 但通常,我们的from 都来源于用户的输入,很可能是非常大的一个字符串,因此strcpy 不够安全。 2. strncpy 在ANSI C 中,strcpy 的安全版本是strncpy。 char *strncpy(char *s1, const char *s2, size_t n); 但strncpy 其行为是很诡异的(不符合我们的通常习惯)。标准规定n 并不是sizeof(s1),而是要复制的char 的个数。一个最常见的问题,就是strncpy 并不帮你保证/0 结束。 char buf[8]; strncpy( buf, "abcdefgh", 8 ); 看这个程序,buf 将会被"abcdefgh" 填满,但却没有/0 结束符了。 另外,如果s2 的内容比较少,而n 又比较大的话,strncpy 将会把之间的空间都用/0 填充。这又出现了一个效率上的问题,如下: char buf[80]; strncpy( buf, "abcdefgh", 79 ); 上面的strncpy 会填写79 个char,而不仅仅是"abcdefgh" 本身。 strncpy 的标准用法为:(手工写上/0) 区别:两个函数都是进行n字节内存内容的拷贝,入口参数和返回参数也都一样,可是这两个函数在内部实现上是有一定区别的,这主要是因为dest内存区域和src内存区域可能有一下四种不同的情况, 注意count的影响: 从图中可以看出,src的内存区域和dest的内存区域相对位置和重叠关系有四种情况,memcpy没有考虑重叠的情况,而memmove考虑到了全部情况,因此memcpy函数的时候可能出现意向不到的结果。 这两个函数的实现: ***********下面两个是错误的实现************** void* memcpy(void* dest, void* source, size_t count) { void* ret = dest; //copy from lower address to higher address while (count--) *dest++ = *source++; //不知道两个指针的类型,不可以这样自加。 return ret; } void* memmove(void* dest, void* source, size_t count) { void* ret = dest; if (dest <= source || dest >= (source + count)) { //Non-Overlapping Buffers //copy from lower addresses to higher addresses while (count --) *dest++ = *source++; } else{ //Overlapping Buffers //copy from higher addresses to lower addresses dest += count - 1; source += count - 1; while (count--) *dest-- = *source--; // 情况同上 } return ret; } ***********************正确的如下************************** void* mymemcpy(void* dest, void* source, size_t count) { char*ret = (char*)dest; char*dest_t = ret; char*source_t = (char*)source; while (count--){ *dest_t++=*source_t++; } return ret; } C++拷贝函数详解 1.什么是拷贝构造函数: CA(const CA& C)就是我们自定义的拷贝构造函数。可见,拷贝构造函数是一种特殊的构 造函数,函数的名称必须和类名称一致,它的唯一的一个参数是本类型的一个引用变量,该参 数是const类型,不可变的。例如:类X的拷贝构造函数的形式为X(X& x)。 当用一个已初始化过了的自定义类类型对象去初始化另一个新构造的对象的时候,拷 贝构造函数就会被自动调用。 也就是说,当类的对象需要拷贝时,拷贝构造函数将会被调用。以下情况都会调用拷贝构造函数: ①程序中需要新建立一个对象,并用另一个同类的对象对它初始化,如前面介绍的那样。 ②当函数的参数为类的对象时。 在调用函数时需要将实参对象完整地传递给形参,也就是需要建立一个实参的拷贝,这就 是按实参复制一个形参,系统是通过调用复制构造函数来实现的,这样能保证形参具有和实参 完全相同的值。 ③函数的返回值是类的对象。 在函数调用完毕将返回值带回函数调用处时。 此时需要将函数中的对象复制一个临时对象并传给该函数的调用处。如 Box f( ) //函数f的类型为Box类类型 {Box box1(12,15,18); return box1; //返回值是Box类的对象 } int main( ) {Box box2; //定义Box类的对象box2 box2=f( ); //调用f函数,返回Box类的临时对象,并将它赋值给 box2 } 如果在类中没有显式地声明一个拷贝构造函数,那么,编译器将会自动生成一个默认的 拷贝构造函数,该构造函数完成对象之间的位拷贝。位拷贝又称浅拷贝,后面将进行说明。 自定义拷贝构造函数是一种良好的编程风格,它可以阻止编译器形成默认的拷贝构造函数,提高源码效率。 浅拷贝和深拷贝 在某些状况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里的 库函数并不是C语言的一部分,它是由编译系统根据一般用户的需要编制并 提供给用户使用的一组程序。每一种C编译系统都提供了一批库函数,不同的 编译系统所提供的库函数的数目和函数名以及函数功能是不完全相同的。ANSI C标准提出了一批建议提供的标准库函数。它包括了目前多数C编译系统所提供 的库函数,但也有一些是某些C编译系统未曾实现的。考虑到通用性,本附录 列出ANSI C建议的常用库函数。 由于C库函数的种类和数目很多,例如还有屏幕和图形函数、时间日期函数、 与系统有关的函数等,每一类函数又包括各种功能的函数,限于篇幅,本附录不 能全部介绍,只从教学需要的角度列出最基本的。读者在编写C程序时可根据 需要,查阅有关系统的函数使用手册。 1.数学函数 使用数学函数时,应该在源文件中使用预编译命令: #include memcpy函数详解 1)memcpy函数用法解析 结构如下: void *memcpy( void *dest, const void *src, size_t count ); 作用: 在dest处拷贝src处的字节,并以count来计算需要拷贝的字节数量,进行内存的拷贝。 参数: dest:新的存贮区的开始部位src:需要拷贝的开始部位count:需要拷贝的字节数备注:dest,src,它们都是从各自的地址处进行写入,如果是p而不是&p,那么奖会取得p的值(地址),在该值的地址处进行读出或写入。 例: int* intPoint = new int(3333); int* intPoint1; memcpy( &intPoint1, &intPoint, 4 );//在intPoint1的地址处写入intPoint地址处的值,也就是intPoint指针值。 cout << *intPoint1 << endl;//使intPoint1指向了intPoint. 或 int* intPoint = new int(3333); int intPoint1; memcpy( &intPoint1, intPoint, 4 ); cout << intPoint1 << endl; 2)memcpy函数的实现与应用 memcpy函数较memmove相比,存在的不足是没有考虑到目的地址与源地址相重合,本文 对memcpy作了修改,弥补其不足。 memcpy函数的特点是: 1. 使用memcpy函数前,实参dest必须初始化,否则可能会出错,原因见2。 2. 函数原理是将void *src 强制转换为char *s,然后只负责拷贝n个字节到dest里,不 C语言中最常用标准库函数- candyliuxj - CSDN博客 C语言中最常用标准库函数收藏 标准头文件包括: <asset.h> <ctype.h> <errno.h> <float.h> <limits.h> <locale.h> <math.h> <setjmp.h> <signal.h> <stdarg.h> <stddef.h> <stdlib.h> <stdio.h> <string.h> <time.h> 一、标准定义(<stddef.h>) 文件<stddef.h>里包含了标准库的一些常用定义,无论我们包含哪个标准头文件,<stddef.h>都会被自动包含进来。 这个文件里定义: l 类型size_t (sizeof运算符的结果类型,是某个无符号整型); l 类型ptrdiff_t(两个指针相减运算的结果类型,是某个有符号整型); l 类型wchar_t (宽字符类型,是一个整型,其中足以存放本系统所支持的所有本地环境中的 字符集的所有编码值。这里还保证空字符的编码值为0); l 符号常量NULL (空指针值); l 宏offsetor (这是一个带参数的宏,第一个参数应是一个结构类型,第二个参数应是结构 成员名。offsetor(s,m)求出成员m在结构类型t的变量里的偏移量)。 注:其中有些定义也出现在其他头文件里(如NULL)。 二、错误信息(<errno.h>) <errno.h>定义了一个int类型的表达式errno,可以看作一个变量,其初始值为0,一些标准库函数执行中出错时将它设为非0值,但任何标准库函数都设置它为0。 <errno.h>里还定义了两个宏EDOM和ERANGE,都是非0的整数值。数学函数执行中遇到参数错误,就会将errno 置为EDOM,如出现值域错误就会将errno置为ERANGE。 三、输入输出函数(<stdio.h>) 文件打开和关闭: FILE *fopen(const char *filename, const char *mode); int fclose(FILE * stream); C++常用操作函数载入动态库 bool LoadDll() { HttpDownDll = LoadLibrary("HTTPDOWNDLL.dll"); if(HttpDownDll == NULL) { MessageBox(0, "载入动ˉ态库a错洙误ó!", "错洙误ó", MB_OK|MB_ICONWARNING); FreeLibrary(HttpDownDll); return false; } SendCommand = (SendCommandFunc)GetProcAddress(HttpDownDll, "SendCommand"); if(SendCommand == NULL) { MessageBox(0, "GetProcAddress错洙误ó!", "错洙误ó", MB_OK|MB_ICONWARNING); FreeLibrary(HttpDownDll); return false; } return true; } 卸载动态库 bool FreeDll() { FreeLibrary(HttpDownDll); SendCommand = NULL; return true; } 字符串分割 std::vector memcpy 函数原型 void *memcpy(void *dest, const void *src, size_t n); 功能 从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中 所需头文件 #include // memcpy.c #include MFC数组类 CByteArray: CDWordArray: CPtrArray: CUIntArray: CWordArray: CStringArray: 常用成员函数 1.int Add(ARG_TYPE newElement ); throw(CMemoryException ); 2.TYPE& ElementAt(int nIndex ); 3.void FreeExtra(); 4.TYPE GetAt(int nIndex )const 5.int GetSize()const; 6.int GetUpperBound()const; 7.(1)void InsertAt(int nIndex, ARG_TYPE newElement, int nCount = 1 ); throw(CMemoryException ); (2)void InsertAt(int nStartIndex, CArray* pNewArray ); throw(CMemoryException ); 8.void RemoveAll(); 9.void SetAt(int nIndex, ARG_TYPE newElement ); 10.void SetAtGrow(int nIndex, ARG_TYPE newElement ); throw(CMemoryException ); 11.void SetSize(int nNewSize, int nGrowBy = -1 ); throw(CMemoryException ); 例: CStringArray m_strArray m_strArray.SetSize(5,5); m_strArray[0] = "111111111111"; m_strArray[2] = "333333333333"; m_strArray[4] = "555555555555"; 增加元素 int m_nIndex = 0; CString m_strValue = "Default"; int m_nRadio = 0; if (m_nRadio == 0) m_strArray.SetAtGrow(m_nIndex, m_strValue); else if (m_nRadio == 1) m_strArray.Add(m_strValue); else m_strArray.InsertAt(m_nIndex, m_strValue, 1); 删除 int m_nIndex= 0; m_strArray.RemoveAt(m_nIndex); MFC的链表类 模板类Clist CTypedPtrList 面试中面试官经常会让写程序,根据题目的难度会在算法和编程习惯上各有侧重。比如写一个memcpy函数,这个题算法简单明确,因此重点考察编程习惯、工程思想。 该题目的算法如下 void memcpy(void *dst, void *src, int count) { while(count--) { *dst = *src; dst++; src++; } } 问题是void*不能直接累加*dst = *src也是不对的。 void memcpy(void *dst, void *src, int count) { unsigned char *pdst = (unsigned char *)dst; unsigned char *psrc = (unsigned char *)src; while(count--) { *pdst = *psrc; pdst++; psrc++; } } 在32位系统中,可复制的最多内存是多少?类型会不会不够用?内存复制不应该修改原始内存吧。 因此,函数声明修改如下void memcpy(void *dst, const void *src, size_t count) 这样就万事大吉了吗?如果传入了空指针呢? 接着修改吧 void memcpy(void *dst, const void *src, size_t count) { assert(dst != NULL); assert(src != NULL); unsigned char *pdst = (unsigned char *)dst; const unsigned char *psrc = (const unsigned char *)src; while(count--) { *pdst = *psrc; pdst++; psrc++; } } 如果有这样的数组char ina[]={0,1,2,3,4,5,6,7,8,9,10,11}; 进行如下调用memcpy(&ina[1], &ina[0], 5);会发生什么情况?由于原始数据和目的数据在空间上存在重叠,这样导致复制过 C++常用库函数 1、常用数学函数 头文件#include 2、常用字符串处理函数 头文件#include 3、其他常用函数 头文件#include 4、实现键盘和文件输入/输出的成员函数 头文件#include C++的头文件! #include #include 1.//*********************************************************************** char* reverse(char* Array) { int len = strlen(Array); for(int i=0;i 常用C语言标准库函数 C语言编译系统提供了众多的预定义库函数和宏。用户在编写程序时,可以直接调用这些库函数和宏。这里选择了初学者常用的一些库函数,简单介绍了各函数的用法和所在的头文件。 1.测试函数 Isalnum 原型:int isalnum(int c) 功能:测试参数c是否为字母或数字:是则返回非零;否则返回零 头文件:ctype.h Isapha 原型:int isapha(int c) 功能:测试参数c是否为字母:是则返回非零;否则返回零 头文件:ctype.h Isascii 原型:int isascii(int c) 功能:测试参数c是否为ASCII码(0x00~0x7F):是则返回非零;否则返回零 头文件:ctype.h Iscntrl 原型:int iscntrl(int c) 功能:测试参数c是否为控制字符(0x00~0x1F、0x7F):是则返回非零;否则返回零头文件:ctype.h Isdigit 原型:int isdigit(int c) 功能:测试参数c是否为数字:是则返回非零;否则返回零。 头文件:ctype.h Isgraph 原型:int isgraph(int c) 功能:测试参数c是否为可打印字符(0x21~0x7E):是则返回非零;否则返回零 头文件:ctype.h Islower 原型:int islower(int c) 功能:测试参数c是否为小写字母:是则返回非零;否则返回零 头文件:ctype.h Isprint 原型:int isprint(int c) 功能:测试参数c是否为可打印字符(含空格符0x20~0x7E):是则返回非零;否则返回零 头文件:ctype.h Ispunct 原型:int ispunct(int c) 功能:测试参数c是否为标点符号:是则返回非零;否则返回零 void含义 void的字面意思是“无类型”,void *则为“无类型指针”,void *可以指向任何类型的数据。 void几乎只有“注释”和限制程序的作用,定义一个void变量没有意义,不妨试着定义:void a; 这行语句编译时会出错,提示“illegal use of type 'void'”。不过,即使void a的编译不会出错,它也没有任何实际意义。 void真正发挥的作用在于: (1)对函数返回的限定; (2)对函数参数的限定。 众所周知,如果指针p1和p2的类型相同,那么我们可以直接在p1和p2间互相赋值;如果p1和p2指向不同的数据类型,则必须使用强制类型转换运算符把赋值运算符右边的指针类型转换为左边指针的类型。 例如: float *p1; int *p2; p1 = p2; 其中p1 = p2语句会编译出错,提示“'=' : cannot convert from 'int *' to 'float *'”,必须改为:p1 = (float *)p2; 而void *则不同,任何类型的指针都可以直接赋值给它,无需进行强制类型转换: void *p1; int *p2; p1 = p2; 但这并不意味着,void *也可以无需强制类型转换地赋给其它类型的指针。因为“无类型”可以包容“有类型”,而“有类型”则不能包容“无类型”。道理很简单,我们可以说“男人和女人都是人”,但不能说“人是男人”或者“人是女人”。下面语句编译出错: void *p1; int *p2; p2 = p1; 提示“'=' : cannot convert from 'void *' to 'int *'”。 编辑本段void的使用 下面给出void关键字的使用规则: 规则一 如果函数没有返回值,那么应声明为void类型 在C语言中,凡不加返回值类型限定的函数,就会被编译器作为返回整型值处理。但是许多程序员却误以为其为void类型。例如: add ( int a, int b ) { return a + b; } Keil c51函数参考 一、数学函数 返回:val的浮点常用对数。 (9)sin 原型:float sin(float x); 功能:sin函数计算浮点数x的正弦值。 参数:x必须在-65535~65535之间,或产生一个NaN错误。 返回:sin函数返回x的正弦。 (10)cos 原型:float cos(float x); 功能:COS函数计算浮点数X的余弦。 参数:X的值必须在-65535~65535之间,或产生一个NaN错误。返回:COS函数返回X的余弦。 (11)tan 原型:float tan(float x); 功能:tan函数计算浮点数x的正切值。 参数:x必须在-65535~65535之间,或错误值NaN。 返回:tan函数返回x的正切。 (12)asin 原型:float asin(float x); 功能:求反正弦 参数:浮点数x,取值必须在-1~1之间。 返回:X的反正弦,值在-π/2~π/2之间。 (13)acos 原型:float acos(float x); 功能:求反余弦 参数:浮点数x,取值必须在-1~1之间。 返回:x的反余弦,值在0~π之间。 (14)atan 原型:float atan(float x); 功能:求反正切 参数:浮点数x,取值必须在-1~1之间。 返回:X的反正切,值在-π/2~π/2之间。 (15)sinh 原型:float sinh(float x); 功能:sinh函数计算浮点数X的双曲正弦。 参数:x必须在-65535~65535之间,或产生一个NaN错误。 返回:sinh函数返回x的双曲正弦。 函数简介:memmove、memcpy和memccpy简介 函数实现:strcpy()、memcpy()、memmove()、memset()的实现 memmove、memcpy和memccpy简介 memmove、memcpy和memccpy三个函数都是内存的拷贝,从一个缓冲区拷贝到另一个缓冲区。 memmove(void *dest,void*src,int count) memcpy(void *dest,void *src,int count) memccpy(void*dest,void*src,int ch,int count) 表头文件: #include 做游戏常用的函数
C语言标准库函数2012
memset函数使用详解
strncpy函数的用法
memcpy和memmove的区别与实现
【重要】C++拷贝函数详解 20150111
C语言常用的库函数
memcpy函数详解
C语言中最常用标准库函数 - candyliuxj - CSDN博客
C常用操作函数
memcpy
C++常用库函数
实现Memcpy函数
C++常用标准库函数
常见系统函数
常用C语言标准库函数
void的在函数和变量中详解
Keil c51常用库函数汇总参考
memmove、memcpy和memccpy简介