在数学中,符号“∑”和“Π”分别用来表示求和与求积。
首先是函数的累积求和,n取[m, k]中的连续整数值。

这个变量n可以换成其他任意字母,比如x。我们把下面的“n=m”和上面的“k”称作这个和式的下标。在上下文明确的情况下,下标可以省略。
求和符号同样可以表示无穷级数。


求和与求积的用法是完全相同的。当下标不是连续整数时,下标也可以有不同的表达方式。


最后附上一些常见的求和公式。





For the next train
在数学中,符号“∑”和“Π”分别用来表示求和与求积。
首先是函数的累积求和,n取[m, k]中的连续整数值。
这个变量n可以换成其他任意字母,比如x。我们把下面的“n=m”和上面的“k”称作这个和式的下标。在上下文明确的情况下,下标可以省略。
求和符号同样可以表示无穷级数。
求和与求积的用法是完全相同的。当下标不是连续整数时,下标也可以有不同的表达方式。
最后附上一些常见的求和公式。
进程是操作系统中运行的程序实例。而多进程程序和多线程程序相比,具有更健壮,更简单的特点。
在GNU/Linux操作系统中,创建一个新进程,可以使用fork,clone函数以及使用exec函数族调用其他程序替换当前进程镜像。
这里主要讲fork函数。
fork函数的原型为:
#include <unistd.h>
pid_t fork(void);
pid_t是系统定义的类型,一般被定义为short int。
这里看一个最简单的调用示例。
#include <stdio.h>
#include <unistd.h>
int main(void)
{
pid_t pid;
pid = fork();
printf("My process ID is %d.\n", getpid());
return 0;
}
这样就最简单的创建了一个子进程,并且打印出了进程的pid。
fork函数是分裂执行的,这也就是fork(分叉)命名的原因吧。如何理解这个“分裂”呢,看这段程序。
#include <stdio.h>
#include <unistd.h>
int main(void)
{
pid_t pid;
pid = fork();
if (pid > 0) {
printf("I'm parent process.\n");
/* 父进程的pid大于0 */
} else if (pid == 0) {
printf("I'm child process.\n");
/* 子进程的pid等于0 */
} else {
printf("Cannot create process.\n");
/* 如果pid小于0,表示出错 */
}
return 0;
}
内核在第一次调用fork()时,将当前进程的所有内存空间和文件描述符等资源复制一份给创建的子进程(实际上采用了“Copy-on-write”(写时复制),第一次试图对内存进行写操作的时候才复制,提高了效率)。所以fork调用后有2个进程在同时执行后面的代码。如何区分呢?
在父进程中,pid变量被标记为一个正整数;而子进程的pid被标记为0;当然,当pid为负数时,系统只有1个进程运行,表示创建进程出错。
如何证明这一点呢?看下面这个程序:
#include <stdio.h>
#include <unistd.h>
int main(void)
{
pid_t pid1, pid2, pid3;
pid1 = fork();
pid2 = fork();
pid3 = fork();
printf("I'm a process.\n");
return 0;
}
这个程序会打印出几个”I’m a process.”字符串?答案是8个(2的3次方)。如果再加一个fork答案就会是16个(2的4次方)。运行结果证明了这一点。第一次fork调用产生一个子进程,第二次两个进程各产生一个,第三次四个进程各又产生一个……所以结果是2*2*2=8个。(fork炸弹?哈哈)
现在你会发现,调用fork()的程序需要ctrl+c才能退出。这是因为父进程在等待子进程退出。如果父进程在子进程结束之前退出了,那么子进程就会成为所谓的“僵尸进程”。
要结束子进程可以使用wait函数。
#include <unistd.h>
pid_t wait(int * status);
返回退出子进程的pid。调用后可以从status了解到wait的调用状态。
以下的宏可以用来校验status变量。
WIFEXITED | 正常退出,值为true |
WEXITSTATUS | 返回子进程exit状态,为int |
WIFSIGNALED | 子进程是否因为信号结束,是则为true |
WTERMSIG | 返回子进程退出的信号号(上个宏为true时才有意义) |
如果当前有多个子进程,系统怎么能知道我要结束哪一个呢?所以,有了waitpid函数。
#include <unistd.h>
pid_t waitpid(pid_t pid, int * status, int options);
pid参数表示需要等待结束的子进程。几种值的情况如下:
>0 | 等待pid的进程退出 |
0 | 等待任何一个与调用进程组ID相同的子进程退出 |
-1 | 等待任何一个子进程退出(相当于wait()) |
<-1 | 等待任何一个组ID与pid参数绝对值相同的子进程退出 |
options提供了一些额外的选项来控制waitpid,目前只支持WNOHANG和WUNTRACED两个选项,可以用|连接起来。
WNOHANG,如果没有子进程退出,它也会立即返回,不会一直等下去。
WUNTRACED,用于跟踪调试。
如果不想用options,可以传一个参数0。
waitpid的status多了两个校验的宏,不过仅在设置WUNTRACED后可用。
WIFSTOPPED | 如果子进程已经停止,返回true |
WSTOPSIG | 返回使子进程停止的型号(上个宏为true时才有意义) |
如果调用出错,返回-1,并且errno被设置成特定的值;如果WNOHANG被设置且没有子进程退出,返回0;否则返回子进程的pid号。
不过,有时候不需要这么麻烦。
#include <unistd.h>
#include <signal.h>
int kill(pid_t pid, int sig_num);
kill用于向进程发送信号。pid的各种值情况如下表:
>0 | 信号发送到pid指定的进程 |
0 | 信号发送到调用进程同组的所有进程 |
-1 | 信号发送到除init外的所有进程 |
<0 | 信号发送到pid绝对值指定进程组中所有进程 |
还有函数raise,用于给自己发信号。
#include <unistd.h>
#include <signal.h>
int raise(int sig_num);
/* 等价于 kill(getpid(), sig_num); */
所以,终止子进程也可以这样:
#include <unistd.h>
#include <signal.h>
int main(void)
{
pid_t pid = fork();
kill(pid, SIGKILL);
return 0;
}
这里讲了fork函数创建子进程的一些用法。关于exec函数族和信号,将会在以后的文章里说到。
希望这篇文章能带给您以收获!
整数->字符串可以使用stdio.h中的sprintf函数,有的人可能会说到itoa,但其实itoa不是C标准库的函数,是微软自己添加的。
sprintf的原型是:
int sprintf ( char * str, const char * format, ... );
和printf用法相同。当然也可用于其它类型如double。
例:
char str[20];
int s = 1000000;
sprintf(str, "%d", s);
字符->整数同样使用的也是stdio.h中的sscanf函数,stdlib.h中也有atoi和strtol可以进行转换。
int sscanf ( const char * str, const char * format, ... );
int atoi ( const char * str );
long int strtol ( const char * nptr, char ** endptr, int base);
sscanf和atoi的用法都很简单。值得一提的是strtol这个函数。第一个参数是源字符串,第二个参数用于接收非法字符串的首地址,第三个参数是转换后的进制。
什么叫非法字符串的首地址呢?比如nptr的值是”1234f5eg”,base是10,endptr在调用后的值就是”f5eg”。如果base是16,那么endptr的值就是”g”(f和e是16进制的合法字符,而在10进制中却不是)。可以看出非法字符的类型和base有关。由于要修改指针的值,所以需要用到二重指针。另外,开头和结尾的空格会被忽略,中间的空格会被视为非法字符。
例:
char buf[] = "12435 fawr22g"
char *stop;
printf("%d\n", (int)strtol(buf,&stop,10));
printf("%s\n",stop);
输出结果为
12435
fawr22g
另外,给出一个atoi的实现(glibc里的atoi是直接用strtol实现的):
#include <string.h>
#include <ctype.h>
int atoi(const char *s)
{
int sign = (s[0] == '-') ? -1 : 1;
int i, j, res = 0;
int b = 1;
for (j = strlen(s) - 1; j > i; --j) {
b *= 10;
}
for (i = isdigit(s[0]) ? 0 : 1; i < strlen(s); ++i) {
res += (s[i] - '0') * b;
b /= 10;
}
return res;
}