第六章
6-1 编译程序清单6-1中的程序,使用ls -l命令显示可执行文件的大小,解释为什么可执行文件的大小远小于10MB,但是程序中包含了一个10MB的数组?
#include <stdio.h>
#include <stdlib.h>
char globBuf[65536];
int primes[] = {2, 3, 5, 7};
static int square(int x)
{
int result;
result = x * x;
return result;
}
static void doCalc(int val)
{
printf("The square of %d is %d\n", val, square(val));
if (val < 1000)
{
int t;
t = val * val * val;
printf("The cube of %d is %d\n", val, t);
}
}
int main(void)
{
static int key = 9973;
static char mbuf[10240000];
char *p;
p = malloc(1024);
doCalc(key);
exit(EXIT_SUCCESS);
}
编译后程序大小是8.8k。显然程序中有两个大的未初始化的数据段,这些数组在程序运行时才会被分配存储空间。只有被初始化的数据段才会使得程序体积变大。如果程序按照下面的方式进行修改,程序的体积就会变大。
char globBuf[65536] = {0,1,0,1};
使用如上的声明方式,是初始化数据段,数组中所有内容,包括后面的0,占用程序体积,编译后现在是76k,注意数组未填充部分是0。
static char mbuf[10240000] = {0,10,0,1};
使用如上的声明方式,也初始化了数据段,数组中所有内容占用程序体积,编译后现在是9.9M,注意数组未填充部分是0。
char mbuf[10240000] = {0,10,0,1};
使用如上的声明方式,不占用数据段,数组中剩余部分的0不占用程序体积,数组中的内容在文本段,占用程序体积,变量记录在栈中,在运行中将文本段拷贝到栈中,现在是8.8k,注意实际上体积仍然变大了,因为文本段增加了。注意这样的代码一般是无法运行的,因为代码在这里要求分配一个10MB大小的段,会超出一般系统提供的栈的大小。
6-2 编写程序,观察longjmp函数试图跳转到一个已经返回的函数中会出现什么。
这是一个程序:
#include <stdlib.h>
#include <setjmp.h>
#include <stdio.h>
jmp_buf env;
void subfunc(void)
{
if (setjmp(env) == 0)
{
printf("1\n");
}
else
{
printf("2\n");
}
}
void tryjmp(void)
{
longjmp(env, 1);
printf("3\n");
}
int main(void)
{
subfunc();
tryjmp();
printf("4\n");
return 0;
}
会出现什么?运行之后,结果如下:
1
2
4
事实上已经很难解释为什么会这样运行,程序没有出错,但是运行的逻辑却很难说通。longjmp的位置应该是subfunc函数,然而subfunc函数返回位置是main函数,接着又运行了tryjmp函数,理论上应该是死循环,但实际上并没有出现。subfunc函数似乎是返回了tryjmp的出口位置。
6-3 使用getenv()函数,putenv()函数来实现setenv()和unsetenv()函数。unsetenv函数需要将多个同名的环境变量都移除。
这两个函数都涉及到字符串的处理,我们需要用到<string.h>中的函数,例如字符串比较strcmp,字符串追加strcat,计算字符串长度strlen。在setenv中,我们判断是否需要set,当环境变量中没有对应的name值或者overwrite为1时,我们将一个串put到环境中。putenv操作会自动覆盖掉原来的环境变量,我们不需要额外操作。在清除环境变量时,我们判断原来是否有对应的环境变量,如果有,我们将其组合成字符串。然后一个一个遍历原来的环境变量,如果有,则将其加入到新的环境变量中,否则跳过。最后,释放掉原来的环境变量,同时将新的环境变量设置为当前的环境变量。
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#define MAX_ENV_LEN 1024
#define MAX_ENV_SIZE 4096
extern char **environ;
int mysetenv(const char * name, const char *value, int overwrite){
char * old_value = getenv(name);
if(old_value == NULL || overwrite == 1){
void * env = calloc(MAX_ENV_LEN, 0);
memcpy(env, name, strlen(name));
strcat(env,"=");
strcat(env,value);
printf("put env %s\n", env);
putenv(env);
return 0;
}
return -1;
}
int myunsetenv(const char * name){
char * old_value = getenv(name);
void * env = calloc(MAX_ENV_LEN, 0);
if(old_value == NULL) return -1;
memcpy(env, name, strlen(name));
strcat(env,"=");
strcat(env,old_value);
char ** newenv = calloc(MAX_ENV_SIZE * sizeof(char **), 0);
int i = 0;
for(char **ep=environ;*ep!=NULL;ep++){
if(strcmp(env, *ep) != 0){
newenv[i++] = *ep;
}
else{
printf("clear env %s\n", *ep);
}
}
free(environ);
environ = newenv;
return 0;
}
void printenv(){
printf("The envs are:\n");
char **ep;
for(ep=environ;*ep!=NULL;ep++)
puts(*ep);
}
int main(void){
clearenv();
mysetenv("abcdefg", "hijklmn", 0);
printenv();
mysetenv("abcdefg", "1234567", 1);
printenv();
myunsetenv("abcdefg");
printenv();
}
我们将上面的代码编译之后运行,可以看到如下的输出:
$ gcc 6-3.c
$ ./a.out
put env abcdefg=hijklmn
The envs are:
abcdefg=hijklmn
put env abcdefg=1234567
The envs are:
abcdefg=1234567
clear env abcdefg=1234567
The envs are:
首先清空环境变量,然后我们向环境变量加入了abcdefg=hijklmn,之后我们覆盖了这个环境变量abcdefg=1234567,最后我们删除了这个abcdefg的环境变量。可以看到输出正确。