第六章

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的环境变量。可以看到输出正确。

results matching ""

    No results matching ""