【问题标题】:Changing an element in an array of structs changes every element更改结构数组中的元素会更改每个元素
【发布时间】:2021-02-17 22:45:27
【问题描述】:

我正在尝试更改数组中结构的变量,但由于某种原因,当我更改一个时,其余部分也会更改,代码如下。我尝试过使用 -> 和 (*logEntries[i]),但都不起作用。

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

typedef struct logEntry{
    char *name;
    struct tm * time;
    int code;
}logEntry;

char *readline(){
    int lineSize = 100;
    int counter = 0;
    int active = 1;
    char *currLine = malloc(sizeof(char)*lineSize);    
    char c;
    
    while (active == 1){
     c = getchar();
     
     if (c == EOF || c == '\n'){
         currLine[counter] = '\0';
         return currLine;
     }
     else{
         currLine[counter] = c;
         counter++;
     }
     
     if(counter > lineSize){
         lineSize += 100;
         currLine = realloc(currLine, lineSize*sizeof(char));
     }
    }
}

char **SeparateTokens(char *line){
    int counter = 0;
    int lineLength = sizeof(line);
    char **tokens = malloc(lineLength*sizeof(char*));
    char *token;
    
    token = strtok(line, " \t\r\n");
    
    while (token != NULL){
     tokens[counter] = token;
     counter++;
     
     if(counter > lineLength){
         lineLength += 100;
         tokens = realloc(tokens, lineLength*sizeof(char*));
     }
     
     token = strtok(NULL, " \t\r\n=");
    }
    
    tokens[counter] = NULL;
    
    return tokens;
}

int Execute(char **args,struct logEntry *logEntries, int logCount){    
         for(int i = 0; i < logCount; i++){
             printf("%s\t%s\t%d\n",strtok(asctime(logEntries[i].time), "\n"), logEntries[i].name, logEntries[i].code);
         }
         return 1;
    return 0;
}

int main(){
    int active = 1;
    char *line;
    char **args;
    logEntry* logEntries = malloc(100*sizeof(logEntry));
    int logLength = 100;
    int logEntriesCounter = 0;
    time_t timeInSeconds;
    int status;
    
    while(active == 1){
     line = readline();
     args = SeparateTokens(line);
     status = Execute(args, logEntries, logEntriesCounter);
     if(status == 1){
         time(&timeInSeconds);
         logEntries[logEntriesCounter].name = args[0];
         logEntries[logEntriesCounter].time = localtime(&timeInSeconds);
         logEntries[logEntriesCounter].code = status;
         logEntriesCounter++;
         if(logEntriesCounter > logLength){
             logLength += 100;
             logEntries = realloc(logEntries, logLength*sizeof(logEntry));
         }
     }       
     free(line);
     free(args);    
    }
}

args 的指针在每个命令执行结束时被释放,我不相信 logEntriesCounter 的值在每个循环结束时会变为 NULL 或负值,因为唯一的修改是 ++。 log 命令应该根据我的输出结果打印出每个使用过的命令的历史记录,以及时间和返回码,每个元素都被更改。

【问题讨论】:

  • 您的代码无法编译。你有一个流浪的else 和一些未声明的变量。
  • 当我改变一个时,其余的也会改变。这到底是什么意思?请给出确切的预期结果与实际结果。
  • @ch4se 如果我们无法重现问题,我们将无法帮助您。尝试将其缩减为能够演示问题的最小的工作代码。这样做你也可能会发现问题。
  • 我推测您没有为logEntries 的字段分配任何缓冲区,而是使用相同的指针分配它们。但如果没有minimal reproducible example,它仍将是一个猜测。
  • 我需要查看separateTokens() 来确定,但我认为它返回了一个指向separateTokens() 本地变量的指针。所以args[0] 始终是内存中的同一个位置,因为logEntries[logEntriesCounter].name 是一个指向内存的指针,当内存发生变化时,所有内容都指向已更改的数据。确保separateTokens() 没有返回指向局部变量的指针。您可以发布该功能的代码吗?

标签: arrays c struct


【解决方案1】:

localtime 重用相同的内存来返回其结果。当您多次调用localtime 时,您将覆盖之前的结果。如果你检查logEntries[logEntriesCounter].time的地址,你会发现每个入口都指向同一个内存。

我们可以在这里证明这一点。

#include <time.h>
#include <stdio.h>

int main() {
    struct tm* times[3];

    for( int i = 0; i < 3; i++ ) {
        time_t t = time(NULL) + 1000 * i;
    
        // localtime returns the same memory each time
        times[i] = localtime(&t);
    }

    for( int i = 0; i < 3; i++ ) {
        printf("%d - %p - %s", i, (void *)times[i], asctime(times[i]));
    }
}

// 0 - 0x7f9c60c05ae0 - Wed Feb 17 16:47:17 2021
// 1 - 0x7f9c60c05ae0 - Wed Feb 17 16:47:17 2021
// 2 - 0x7f9c60c05ae0 - Wed Feb 17 16:47:17 2021

每个元素都包含相同的指针和日期。 times 的所有三个元素都指向同一个内存。每次调用 localtime 都会覆盖该共享内存。

大部分 time.h 都会遇到这个问题。为避免这种情况,请使用 _r 变体,如 localtime_r 和 asctime_r。 r 代表"reentrant"。这些要求您为其结果分配内存。

#include <time.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    struct tm* times[3];

    for( int i = 0; i < 3; i++ ) {
        time_t t = time(NULL) + 1000 * i;

        // Allocate memory for the result.
        // Can't use stack memory, that's local to each iteration of the loop.
        struct tm *ltime = malloc(sizeof(struct tm));
        times[i] = localtime_r(&t, ltime);
    }

    for( int i = 0; i < 3; i++ ) {
        // Allocate a buffer for asctime_r.
        // Since we're not storing it, it's safe to reuse the buffer.
        char buf[26];
        printf("%d - %p - %s", i, (void *)times[i], asctime_r(times[i], buf));
    }

    // And be sure to free the allocated memory.
    for( int i = 0; i < 3; i++ ) {
        free(times[i]);
    }

}

// 0 - 0x7ff3b3c05ae0 - Wed Feb 17 16:14:36 2021
// 1 - 0x7ff3b3e04330 - Wed Feb 17 16:31:16 2021
// 2 - 0x7ff3b3e04370 - Wed Feb 17 16:47:56 2021

或者,不要将struct tm 存储为指针。然后本地时间的返回值将被复制到结构中。

#include <time.h>
#include <stdio.h>

int main() {
    struct tm times[3];

    for( int i = 0; i < 3; i++ ) {
        time_t t = time(NULL) + 1000 * i;
        // The return value is copied.
        times[i] = *localtime(&t);
    }

    for( int i = 0; i < 3; i++ ) {
        printf("%d - %p - %s", i, (void *)&times[i], asctime(&times[i]));
    }
}

// 0 - 0x7ffeedba02b0 - Wed Feb 17 16:15:24 2021
// 1 - 0x7ffeedba02e8 - Wed Feb 17 16:32:04 2021
// 2 - 0x7ffeedba0320 - Wed Feb 17 16:48:44 2021

从长远来看,习惯使用 _r 变体并使用指针会更好。

【讨论】:

    猜你喜欢
    • 2011-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-10
    • 2016-08-08
    • 1970-01-01
    相关资源
    最近更新 更多