一 进程相关函数
1 exit、_exit和return
exit(0):正常运行程序并退出程序;exit(1):非正常运行导致退出程序;return:返回函数。
return是语言级别的,它表示了调用堆栈的返回;而exit是系统调用级别的,它表示了一个进程的结束。
在stdlib.h中exit函数是这样子定义的:void exit(int status)。这个系统调用是用来终止一个进程的,无论在程序中的什么位置,只要执行exit,进程就会从终止进程的运行。讲到exit这个系统调用,就要提及另外一个系统调用,_exit,_exit()函数位于unistd.h中,相比于exit(),_exit()函数的功能最为简单,直接终止进程的运行,释放其所使用的内存空间,并销毁在内存中的数据结构,而exit()在于在进程退出之前要检查文件的状态,将文件缓冲区中的内容写回文件。
非主函数中调用return和exit效果很明显,但是在main函数中调用return和exit的现象就很模糊,多数情况下现象都是一致的。
2 assert()宏用法
assert
是宏,而不是函数。在C的assert.h
头文件中。原型定义:
#define assert(expr)\
((expr)\
?__ASSERT_VOID_CAST(0)\
:__assert_fail(__STRING(expr),__FILE__,__LINE__,__ASSERT_FUNCTION))
/*DefinedInGlibc2.15*/
assert的作用是现计算表达式 expression ,如果其值为假(即为0),那么它先向stderr打印一条出错信息,然后通过调用 abort来终止程序运行。windows平台上assert宏只在release模式下有效。Linux上则没有这个限制,无论Debug还是release模式assert都生效,但是如果你想让assert失效,编译的时候加上-DNDEBUG
即可。
#define NDEBUG
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cassert>
using namespace std;
int main(int argc, char *argv[])
{
bool is = false;
assert(is);
printf("hell");
return 0;
}
//输出:hell
这个宏通常原来判断程序中是否出现了明显非法的数据,如果出现了终止程序以免导致严重后果,同时也便于查找错误。
3 sockpair
4 access
access函数检查调用进程是否可以对指定的文件执行某种操作。原型:
int access(const char * pathname, int mode)
pathname:需要检测的文件路径名;mode:需要的操作模式,取值如下:
F_OK 值为0,判断文件是否存在
X_OK 值为1,判断对文件是可执行权限
W_OK 值为2,判断对文件是否有写权限
R_OK 值为4,判断对文件是否有读权限
注:后三种可以使用或“|”的方式,一起使用,如W_OK|R_OK
成功执行时,返回0。失败返回-1,errno被设为以下的某个值
EINVAL: 模式值无效
EACCES: 文件或路径名中包含的目录不可访问
ELOOP : 解释路径名过程中存在太多的符号连接
ENAMETOOLONG:路径名太长
ENOENT:路径名中的目录不存在或是无效的符号连接
ENOTDIR: 路径名中当作目录的组件并非目录
EROFS: 文件系统只读
EFAULT: 路径名指向可访问的空间外
EIO:输入输出错误
ENOMEM: 不能获取足够的内核内存
ETXTBSY:对程序写入出错
5 sleep函数
在linux编程中,有时候会用到定时功能,常见的是用sleep(time)函数来睡眠time秒;但是这个函数是可以被中断的,也就是说当进程在睡眠的过程中,如果被中断,那么当中断结束回来再执行该进程的时候,该进程会从sleep函数的下一条语句执行;这样的话就不会睡眠time秒了。
#include <unistd.h>
unsigned int sleep (unsigned int seconds);//n秒
int usleep (useconds_t usec);//n微秒
sleep(time)返回值是睡眠剩下的时间。
以下情况不会中断。
do{
time = sleep(time);
}while(time > 0);
二 输出输入
1 C/C++输入输出函数
1. 缓冲区回车符
在C++中如果采用cin进行输入,则在缓存区留下回车字符,可以使用cin.get()或者是gets()读出。
2. cin类型不符
istream类重载了抽取运算符<< ,>>, 所以cin >> 支持c++内置的所有基本数据类型。cin对象将标准输入表示为字节流,然后根据抽取运算符<<(本质就是一个函数)的参数类型对字节流进行类型转换,转换为所需的类型。如果输入的前面一部分字符为空白(空格、换行符、和制表符),他们会跳过,直到遇到非空白字符。当输入没有满足程序期望的时候,比如对于一个int类型的变量a,却输入字符H,这种情况下,抽取运算发将不会改变变量a的值,并返回0,经常被放在循环条件中来终止循环。
3. cin.get()
cin.get()每次读取一整行并把由Enter键生成的换行符留在输入缓冲区中。
4. cin.getline()
cin.getline()每次读取一整行,并把由Enter键生成的换行符抛弃,回车符并不留在缓冲区。
5. getline()
getline()每次读取到特定字符,并把特定字符抛弃,特定字符并不留在缓冲区。
getline()的原型是istream& getline ( istream &is , string &str , char delim );位于头文件<string>中。
其中 istream &is 表示一个输入流,譬如cin;string&str表示把从输入流读入的字符串存放在这个字符串中(可以自己随便命名,str什么的都可以);char delim表示遇到这个字符停止读入,在不设置的情况下系统默认该字符为’\n’,也就是回车换行符(遇到回车停止读入,且回车符会被丢弃)。
string line;
cout << "please cin a line:";
getline(cin, line, '#');
cout << "The line you give is:" << line <<endl;
参考
C++中cin的详细用法:https://blog.csdn.net/k346k346/article/details/48213811
6. gets和fgets
gets
函数会报错,所以需要使用fgets
。fgets
读入的字符里面会包含’\n’,需要注意。
7. sprintf_s和sprintf
sprintf_s
和sprintf
的区别在于,sprintf只会将数据写入缓冲区,而sprintf_s首先会检查缓冲区大小,然后写入数据,并且将数据后一位置为0,而其他位置为-3,所以在第二个参数缓冲区大小的时候,需要大于写入的数据一个字节。
sprintf函数是一个变参函数,前两个函数有固定类型,会进行安全检查,后面的参数都不是类型安全的。使用的时候要小心了。
摘录网上的一个例子来说明:
假如我们想打印短整数(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);就可以了。
2 变量地址和指针
- 普通变量
int a;
cout << &a;
- 数组变量
int a[20];
cout << &a;
cout << (int *)a;
cout<< (void *)a;
- 指针
int b = 10;
int *a = &b;
cout << (int *)a;
cout<< (void *)a;
3 cout进制输出
cout<<hex<<i<<endl; //输出十六进制数
cout<<oct<<i<<endl; //输出八进制数
cout<<dec<<i<<endl; //输出十进制数
cout << setbase(16) << t << endl;
4 有关缓冲区的注意点
int main()
{
printf("hello world!!");//此时可以打出hello world!!
exit(0); //exit(0);首先会刷新缓冲区,然后调用_exit(0)结束程序
}
int main()
{
printf("hello world!!");//此时打不出来 hello world
_exit(0);//其作用就是结束函数
}
int main()
{
printf("hello world!!\n");//此时可以打出 hello world 因为“\n”也有刷新缓冲区的作用
_exit(0);
}
int main()
{
puts("hello world!!");//此时可以打出 hello world 因为 puts()函数自带有“\n”
_exit(0);
}
三 参数解析函数
1 getopt_long
2 basename(分析路径成员 )
c语言:#include <libgen.h>
c++语言:#include <string.h>
原型:char *basename(char *path);
函数说明:
basename把以 null 结尾的路径名分解为目录和文件名。basename 则返回最后一个 ‘/’ 后面的内容。如果路径名以 ‘/’ 结尾, 该 ‘/’ 被认为不是 路径名的一部分。如果路径名 path 不包含斜杠 ‘/’, basename 返回 path 的副本。如果路径名 path 是 “/”,basename均返回 “/”. 如果 路径名 path 是NULL指针或指向空串,则返回 “.”。basename 有可能更改 path 的内容, 因此如果需要保护原有路径名, 应该传送副本作为参数。此外, 返回的指针可能指向一块静态分配的内存, 会被下次调用覆盖。
3 getopt(分析命令行参数)
头文件:#include<unistd.h>
原型:int getopt(int argc,char * const argv[ ],const char * optstring);
函数说明:参数argc和argv是由main()传递的参数个数和内容。参数optstring 则代表欲处理的选项字符串。此函数会返回在argv 中下一个的选项字母,此字母会对应参数optstring 中的字母。如果选项字符串里的字母后接着冒号“:”,则表示还有相关的参数,全域变量optarg 即会指向此额外参数。如果getopt()找不到符合的参数则会印出错信息,并将全域变量optopt设为“?”字符,如果不希望getopt()印出错信息,则只要将全域变量opterr设为0即可。
短参数的定义:etopt()使用optstring所指的字串作为短参数列表,象"1ac:d::"就是一个短参数列表。短参数的定义是一个’-‘后面跟一个字母或数字,象-a, -b就是一个短参数。每个数字或字母定义一个参数。
其中短参数在getopt定义里分为三种:
1. 不带值的参数,它的定义即是参数本身。
2. 必须带值的参数,它的定义是在参数本身后面再加一个冒号。
3. 可选值的参数,它的定义是在参数本身后面加两个冒号 。
在这里拿上面的"1ac:d::"作为样例进行说明,其中的1,a就是不带值的参数,c是必须带值的参数,d是可选值的参数。
在实际调用中,’-1 -a -c cvalue -d’, ‘-1 -a -c cvalue -ddvalue’, ‘-1a -ddvalue -c cvalue’都是合法的。这里需要注意三点:
1. 不带值的参数可以连写,象1和a是不带值的参数,它们可以-1 -a分开写,也可以-1a或-a1连写。
2. 参数不分先后顺序,’-1a -c cvalue -ddvalue’和’-d -c cvalue -a1’的解析结果是一样的。
3. 要注意可选值的参数的值与参数之间不能有空格,必须写成-ddvalue这样的格式,如果写成-d dvalue这样的格式就会解析错误。
返回值:
getopt()每次调用会逐次返回命令行传入的参数。
当没有参数的最后的一次调用时,getopt()将返回-1。
当解析到一个不在optstring里面的参数,或者一个必选值参数不带值时,返回’?’。
当optstring是以’:‘开头时,缺值参数的情况下会返回’:’,而不是’?’ 。
范例:
#include <stdio.h>
#include <unistd.h>
int main(int argc, int *argv[])
{
int ch;
opterr = 0;
while ((ch = getopt(argc,argv,"a:bcde"))!=-1)
{
switch(ch)
{
case 'a':
printf("option a:'%s'\n",optarg);
break;
case 'b':
printf("option b :b\n");
break;
default:
printf("other option :%c\n",ch);
}
}
printf("optopt +%c\n",optopt);
}
四 字符操作函数及其实现
1 Strcat函数
char *strcat(char *strDest, const char *strScr) //将源字符串加const,表明其为输入参数
{
char * address = strDest; //该语句若放在assert之后,编译出错
assert((strDest != NULL) && (strScr != NULL)); //对源地址和目的地址加非0断言
while(*strDest) //是while(*strDest!=’\0’)的简化形式
{ //若使用while(*strDest++),则会出错,因为++是不受循环
strDest++; //约束的。所以要在循环体内++;因为要是*strDest最后指
} //向该字符串的结束标志’\0’。
while(*strDest++ = *strScr++)
{
NULL; //该循环条件内可以用++,
} //此处可以加语句*strDest=’\0’;有无必要?
return address; //为了实现链式操作,将目的地址返回
}
以下是在VC6.0中调试的例子,函数名用strcata代替。
#include <stdio.h>
#include <assert.h>
char *strcata(char *strDest,const char *strScr)
{
char * address = strDest;
assert((strDest != NULL) && (strScr != NULL));
while(*strDest)
{
strDest++;
}
while(*strDest++ = *strScr++) //(*strDest++ = *strScr++)!='\0'
{
NULL;
}
return address;
}
void main()
{
char str1[100]={"i love"};
char str2[50]={"China"};
printf("%s\n",strcata(str1,str2));
}
2 Strcpy函数
char *strcpy(char *strDest, const char *strScr)
{
char *address=strDest;
assert((strDest != NULL) && (strScr != NULL));
while(*strScr) //是while(*strScr != ’\0’)的简化形式;
{
*strDest++ = *strScr++;
}
*strDest = '\0'; //当strScr字符串长度小于原strDest字符串长度
return address; //时,如果没有改语句,就会出错了。
}
以下是在VC6.0中调试的例子,函数名用strcpya代替。
#include <stdio.h>
#include <assert.h>
char *strcpya(char *strDest, const char *strScr)
{
char *address = strDest;
assert((strDest != NULL) && (strScr != NULL));
while(*strScr)
{
*strDest++ = *strScr++;
}
*strDest = '\0';
return address;
}
void main()
{
char str1[100]={"i love"};
char str2[50]={"China"};
printf("%s\n",strcpya(str1,str2));
}
3 Strcmp函数
int strcmp (const char *str1,const char *str2)
{
int len = 0;
assert((str1 != '\0') && (str2 != '\0'));
while(*str1 && *str2 && (*str1 == *str2))
{
str1++;
str2++;
}
return *str1-*str2;
}
以下是在VC6.0中调试的例子,函数名用strcmpa代替。
#include <stdio.h>
#include <assert.h>
int strcmpa (const char *str1,const char *str2)
{
int len = 0;
assert((str1 != '\0') && (str2 != '\0'));
while(*str1 && *str2 && (*str1==*str2))
{
str1++;
str2++;
}
return *str1-*str2;
}
void main()
{
char str1[100] = {"i love"};
char str2[50] = {"China "};
printf("%d\n",strcmpa(str1,str2));
}
4 Strlen函数
int strlen(const char *str)
{
int len = 0;
assert(str != NULL);
while(*str++)
{
len++;
}
return len;
}
以下是在VC6.0中调试的例子,函数名用strlena代替。
#include <stdio.h>
#include <assert.h>
int strlena(const char *str)
{
int len = 0;
assert(str != NULL);
while(*str++)
{
len++;
}
return len;
}
void main()
{
char str1[100] = {"i love"};
char str2[50] = {"China "};
printf("%d\n",strlena(str1));
}
1 strstr()
strstr()函数用来检索子串在字符串中首次出现的位置,其原型为:
char *strstr(const char *str, const char *substr);
【参数说明】str为要检索的字符串,substr为要检索的子串。
【返回值】返回字符串str中第一次出现子串substr的地址;如果没有检索到子串,则返回NULL。
2 strcmp()
strcmp()用来比较字符串(区分大小写),其原型为:
int strcmp(const char *s1, const char *s2);
【参数】s1, s2 为需要比较的两个字符串。
【返回值】若参数s1 和s2 字符串相同则返回0。s1 若大于s2 则返回大于0 的值。s1 若小于s2 则返回小于0 的值。
3 strcasecmp()
strcasecmp()用来比较字符串(忽略大小写),其原型为:
int strcasecmp (const char *s1, const char *s2);
【参数】s1, s2 为需要比较的两个字符串。
【返回值】若参数s1 和s2 字符串相同则返回0。s1 若大于s2 则返回大于0 的值。s1 若小于s2 则返回小于0 的值。
4 index()
index()函数查找字符并返回首次出现的位置
相关函数:rindex
,srechr
,strrchr
;
头文件:#include <string.h>
;
char * index(const char *s, int c);
【参数】index()用来找出参数s 字符串中第一个出现的参数c地址,然后将该字符出现的地址返回。字符串结束字符(NULL)也视为字符串一部分。
【返回值】如果找到指定的字符则返回该字符所在地址,否则返回0。
五 随机数
计算机的随机数都是由伪随机数,即是由小M多项式序列生成的,其中产生每个小序列都有一个初始值,即随机种子。(注意: 小M多项式序列的周期是65535,即每次利用一个随机种子生成的随机数的周期是65535,当你取得65535个随机数后它们又重复出现了。)
我们知道rand()函数可以用来产生随机数,但是这不是真正意义上的随机数,是一个伪随机数,是根据一个数(我们可以称它为种子)为基准以某个递推公式推算出来的一系列数,当这系列数很大的时候,就符合正态公布,从而相当于产生了随机数,但这不是真正的随机数,当计算机正常开机后,这个种子的值是定了的,除非你破坏了系统。
1.rand()
功能:随机数发生器
用法:int rand(void)
所在头文件: stdlib.h
rand()的内部实现是用线性同余法做的,它不是真的随机数,因其周期特别长,故在一定的范围里可看成是随机的。
rand()返回一随机数值的范围在0至RAND_MAX 间。RAND_MAX的范围最少是在32767之间(int)。用unsigned int 双字节是65535,四字节是4294967295的整数范围。0~RAND_MAX每个数字被选中的机率是相同的。
用户未设定随机数种子时,系统默认的随机数种子为1。
rand()产生的是伪随机数字,每次执行时是相同的;若要不同,用函数srand()初始化它。
2.srand()
功能:初始化随机数发生器
用法: void srand(unsigned int seed)
所在头文件: stdlib.h
srand()用来设置rand()产生随机数时的随机数种子。参数seed必须是个整数,如果每次seed都设相同值,rand()所产生的随机数值每次就会一样。
3.使用当前时钟作为随机数种子
rand()产生的随机数在每次运行的时候都是与上一次相同的。若要不同,用函数srand()初始化它。可以利用srand((unsigned int)(time(NULL))的方法,产生不同的随机数种子,因为每一次运行程序的时间是不同的。
4.产生随机数的用法
- 给srand()提供一个种子,它是一个unsigned int类型;
- 调用rand(),它会根据提供给srand()的种子值返回一个随机数(在0到RAND_MAX之间);
- 根据需要多次调用rand(),从而不间断地得到新的随机数;
- 无论什么时候,都可以给srand()提供一个新的种子,从而进一步“随机化”rand()的输出结果。
//0~RAND_MAX之间的随机数程序
#include <iostream>
#include <stdlib.h>
#include <time.h>
using namespace std;
int main()
{
srand((unsigned)time(NULL));
for(int i = 0; i < 10;i++ )
cout << rand() << '/t';
cout << endl;
return 0;
}
5.产生一定范围随机数的通用表示公式
要取得[a,b)的随机整数,使用(rand() % (b-a))+ a;
要取得[a,b]的随机整数,使用(rand() % (b-a+1))+ a;
要取得(a,b]的随机整数,使用(rand() % (b-a))+ a + 1;
通用公式:a + rand() % n;其中的a是起始值,n是整数的范围。
要取得a到b之间的随机整数,另一种表示:a + (int)b * rand() / (RAND_MAX + 1)。
要取得0~1之间的浮点数,可以使用rand() / double(RAND_MAX)。
六 main函数
https://blog.csdn.net/weixin_42078760/article/details/80500048
1 main函数与WinMain函数区别
前者为控制台程序入口主函数,后者为Windows API窗体程序入口函数,在windef.h文件中定义。
2 main函数与wmain函数 | WinMain函数与wWinMain函数的区别
wmain函数式main函数的宽字符版本,一般在Unicode编码下使用。
3 _tmain函数与main/wMain | _tWinMain函数与WinMain/wWinMain的区别
_tmain函数是wmain函数和main函数Unicode版本的别名,_tWinMain函数是wWinMain函数和WinMain函数Unicode版本的别名,二者都是在tchar.h文件中定义,目的是为了自动适应是否定义了Unicode。
如果为Unicode编码,_tmain就是wmain,_tWinMain就是wWinMain;否则,_tmain则为main,_tWinMain就是WinMain。
注意:使用这两个函数时要加入<tchar.h>引用。
七 C++中L和_T()之区别
https://www.cnblogs.com/yaowen/archive/2013/01/15/2861727.html
八 获取执行文件路径
https://www.cnblogs.com/Duck-Quick/p/9102830.html