sprintf生成分段错误

时间:2018-02-23 00:30:14

标签: c string printf

我从下面的程序中遇到了分段错误。

#include <stdio.h>
#include <string.h>

void removeProcess(int*, int);
void removeProcessN(char**, int, int);

void main() {
    int numPro = 0, quanTime = 0, contTime = 0, i, elemNum, time = 0;

//Supply variables with user input
    printf("Enter number of processes: ");
    scanf("%d", &numPro);
    printf("Enter context switch time: ");
    scanf("%d", &contTime);
    printf("Enter quantum of time: ");
    scanf("%d", &quanTime);

//Create array of number of process time
    int proTime[numPro];
    //Create string array for better output
    char *proNames[numPro];

//Retrieves process time from user
    for (i = 0; i < numPro; i++){
        printf("Enter execution time for process %d: ", i);
        scanf("%d", proTime + i);
        sprintf(proNames[i], "p%d", i);
    }

    elemNum = 0;


//While a process remains active
    while (numPro != 0) {
//Retrieves the element being worked with
        elemNum = elemNum % numPro;

//Describe process working with
        printf("Executing process %s\nStart time = %d\n", proNames[elemNum], time);

        proTime[elemNum] -= quanTime;

//If process time complete, remove process
        if (proTime[elemNum] <= 0){
            removeProcess(proTime, elemNum);
            removeProcessN(proNames, elemNum, numPro);
            --numPro;
        }

//Add amount of time with context time
        time = time + quanTime + contTime;
        elemNum++;
    }
}

/**
*@param *array pointer to an array of integers
*@param elem int of the element to remove
* Removes an element 'elem' from the supplied integer array.
*/
void removeProcessN(char **array, int numElem, int elem) {

    char *temparray[numElem - 1];

//Copy array to temparray except for elem to remove
    int i;
    for (i = 0; i < elem; i++) {
        if (i == numElem) {
            continue;
        } else {
            temparray[i] = array[i];
        }
    }

//End by setting the pointer of array to the temparray
    array = temparray;
}

/**
*@param *array pointer to an array of integers
*@param elem int of the element to remove
* Removes an element 'elem' from the supplied integer array.
*/
void removeProcess(int *array, int elem) {
//Number of elements in the array
    int numElem = sizeof(array) / sizeof(int);

    int temparray[numElem - 1];

//Copy array to temparray except for elem to remove
    int i;
    for (i = 0; i < numElem; i++) {
        if (i == elem) {
            continue;
        } else {
            temparray[i] = array[i];
        }
    }

//End by setting the pointer of array to the temparray
    array = temparray;
}

我知道分段错误来自sprintf。我试图模拟操作系统如何使用循环法完成一个过程。我尝试过使用sprintf,因为这是教程在尝试操作字符串时在线使用的内容。 removeProcessN只是从数组proNames中删除索引。我大多只关心sprintf。

当我做sprintf时我尝试了malloc但是在那时它甚至都没有编译。如果有人可以提供解释我会很感激。

1 个答案:

答案 0 :(得分:3)

这里的问题是proNames是一个指针数组,但它们是 没有初始化,所以将它传递给sprintf来写东西,会崩溃。您 将使用双数组或使用malloc分配内存。但是作为 你只打印整数和整数的字符串表示有一个 最大长度,用malloc分配内存会更难,因为你 必须检查malloc是否不返回NULL,你必须释放内存 等等。

所以我会这样做:

char proNames[numPro][30]; // 28 characters for an int (usually 4 bytes long)
                           // should be more than enough

//Retrieves process time from user
    for (i = 0; i < numPro; i++){
        printf("Enter execution time for process %d: ", i);
        scanf("%d", proTime + i);
        sprintf(proNames[i], "p%d", i);
    }

您的removeProcessN也需要更改:

void removeProcessN(int numElem, int elem, int dim, char (*array)[dim]) {

    for(int i = elem; i < numElem - 1; ++i)
        strcpy(array[i], array[i+1]);

    array[numElem - 1][0] = 0; // setting last element to empty string
}

请注意,我在最后一个位置移动了array参数,否则numElem 未知,编译器将返回错误。

现在你可以这样称呼它:

removeProcessN(elemNum, numPro, 30, proNames);

30来自char proNames[numProp][30];声明。

我想对你的函数removeProcessN的最后一行发表评论:

//End by setting the pointer of array to the temparray
array = temparray;

这是不对的,首先是因为temparray是局部变量并且不再存在 函数返回时存在。 array是函数中的局部变量, 所以改变它不会影响任何人。

内存分配的替代方案如下所示:

char *proNames[numPro];

//Retrieves process time from user
    for (i = 0; i < numPro; i++){
        printf("Enter execution time for process %d: ", i);
        scanf("%d", proTime + i);
        int len = snprintf(NULL, 0, "p%d", i);
        proNames[i] = malloc(len + 1);
        if(proNames[i] == NULL)
        {
            // error handling, free the previously allocated
            // memory, and return/exit
        }

        sprintf(proNames[i], "p%d", i);
    }

removeProcessN

void removeProcessN(char **array, int numElem, int elem) {

    char *to_remove = array[elem];

    for(int i = elem; i < numElem - 1; ++i)
        array[i] = array[i+1];

    free(to_remove);

    array[numElem - 1] = NULL; // setting last element to NULL
                               // makes freeing easier as
                               // free(NULL) is allowed
}

你最初调用removeProcessN的方式就没问题了。

如果你最终为所有进程调用removeProcessN,那么就是所有内存 应该被释放,因为removeProcessN释放它。如果有一些元素 保留在数组中,然后你必须在以后释放它们。

  

OP在评论

中发布      

我的理论是temparray是指向array的指针,所以我可以从主数组中删除索引。   所以当我说array = temparray时,数组的指针指向temparray。我知道它适用于removeProcess。字符串有什么不同吗?

array = temparray removeProcess也没有效果,array仍然有效 一个局部变量并改变它所指向的位置根本没有效果,因为 您只是在更改局部变量。

除了代码错误之外:

int numElem = sizeof(array) / sizeof(int);

这仅适用于纯数组,它不适用于指针,因为 sizeof(array)返回指针int需要存储的大小。 与其他函数一样,您需要将数组的站点传递给函数。

如果你说这个功能有效,那么只是偶然,因为它 产生未定义的行为。通过错误地计算元素的数量, temparray的大小不正确,因此您可以temparray[i] = array[i]; 访问超出界限,导致未定义的行为。未定义的行为 意味着你无法预测会发生什么,它可能是任何东西 崩溃格式化硬盘。未定义导致的结果 行为毫无用处。

再次array = temparray;只会更改局部变量array的位置 指向,removeProcess的来电者没有看到。

正确的版本是:

int removeProcess(int *array, int elem, int numElem) {

    if(array == NULL)
        return 0;

    // nothing to do if the elemnt to be removed is
    // the last one
    if(elem == numElem - 1)
        return 1;

    // overwriting the memory, because memory
    // regions overlap, we use memmove
    memmove(array + elem, array + elem + 1, numElem - elem - 1);

    return 0;
}

所以,说清楚:

我们来看看这段代码:

void sum(int *array, size_t len);
{
    int c[len];

    array = c;
}

void bar(void)
{
    int x[] = { 1, 3, 5 };

    size_t len = sizeof x / sizeof *x;

    sum(x, sizeof x / sizeof *x);
    printf("x[0] = %d, x[1] = %d, x[2] = %d\n", x[0], x[1], x[2]);
}

sum只有bar中传递的指针的副本,因此来自bar的 从某种角度来看,sum更改了副本,因此bar将会打印出来 x[0] = 1, x[1] = 3, x[2] = 5

但如果您希望调用者看到任何更改,那么您可以通过 指针:

void sum(int *array, size_t len)
{
    int c[len];
    for(size_t i = 0; i < len; ++i)
        array[i] += 10;

    array = c;
}

使用此版本bar会打印x[0] = 11, x[1] = 13, x[2] = 15和 并且array = cbar无效。