对 printf 的调用似乎覆盖了字符串数组

标签 c arrays c-strings

您好,我正在用 C 语言为 Raspberry 开发一个程序(正在进行的项目可以在 here 找到)。

我注意到 task1 函数中存在一些错误,因此我在桌面(运行 Ubuntu)中创建了一个等效程序来查找错误,其中 task1 被重新调整如下:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "stub.h"
#include "globals.h"
#include "utils.h"
#include <pthread.h>
#define INPIN 25
void takePic(char picname[24]);

//This thread reads the PIR sensor output
void task1()
{
    unsigned char val_read = 0;
    static unsigned char alarm_on = FALSE;
    static const unsigned int maxNumPics=10;
    static char folderPath[] = "/home/usr/Documents/alarmSys_rasp/alarm_new/pics/";
    char fileNameTodelete[73];
    fileNameTodelete[0] = '\0';
    strcat(fileNameTodelete, folderPath);
    //INITIALIZING
    pinMode(INPIN, INPUT);
    //create folder where to save pics
    createFolder(folderPath);

    char* names[24];
    char picname[24];
    int res = 0;
    picname[0] = '\0';
    static unsigned int numPicsInFolder;
    //delete if more than 10 files
    while((numPicsInFolder =  filesByName(names, folderPath))>maxNumPics)
    {

        fileNameTodelete[0] = '\0';
        strcat(fileNameTodelete, folderPath);
        strcat(fileNameTodelete, names[0]);
        printf("%s\n", fileNameTodelete);
        remove(fileNameTodelete);
    }

    static unsigned int nexEl;
    nexEl = numPicsInFolder % maxNumPics;
    printf("Entering while\n");
    while(1)
    {
        //static const unsigned int del = 300;

        val_read = digitalRead(INPIN);
        if (val_read && (!alarm_on)) //motion detected
        {
            printf("\nDetected movement\n");
            if (numPicsInFolder >= maxNumPics)
            {
                printf("\nMax num pics\n");
                fileNameTodelete[0] = '\0';
                strcat(fileNameTodelete, folderPath);
                strcat(fileNameTodelete, names[nexEl]);
                printFiles(names, numPicsInFolder);
                printf("File to be deleted %d: %s, ", nexEl, names[nexEl]);
                //printf("%s\n", fileNameTodelete);

                if ((res = remove(fileNameTodelete))!=0)
                {
                    printf("Error deleting file: %d\n", res);
                }

            }
            else
            {
                printf("\nNot reached max num pics\n");
                numPicsInFolder++;
            }
            //update buffer
            takePic(picname);
            printf("value returned by takePic: %s\n", picname);
            //names[nexEl] = picname;
            strcpy(names[nexEl], picname); //ERROR HERE
            printFiles(names, numPicsInFolder);

            printf("curr element %d: %s\n",nexEl, names[nexEl]);

            nexEl++;
            nexEl %= maxNumPics;
            printf("\nDetected movement: alarm tripped\n\n");
            alarm_on = TRUE;
            /*Give some time before another pic*/
        }
        else if (alarm_on && !val_read)
        {
            alarm_on = FALSE;
            printf("\nAlarm backed off\n\n");
        }
    }

}


void takePic(char picname[24])
{
    /*Build string to take picture*/
    int err;
    //finalcmd is very long
    char finalcmd[150];
    finalcmd[0] = '\0';
    getDateStr(picname);

    char cmd1[] = "touch /home/usr/Documents/alarmSys_rasp/alarm_new/pics/";
    char cmdlast[] = "";


    strcat(finalcmd, cmd1);
    strcat(picname, ".jpg");
    strcat(finalcmd, picname);
    strcat(finalcmd, cmdlast);
    system(finalcmd);

    if ((err=remove("/var/www/html/*.jpg"))!=0)
    {
        printf("Error deleting /var/www/html/*.jpg, maybe not existing\n" );
    }
    //system(finalcmd_ln);
    //pthread_mutex_lock(&g_new_pic_m);
    g_new_pic_flag = TRUE;
    printf("\nPicture taken\n\n");

}

描述

主函数调用文件task1.c中定义的task1函数。每次验证条件 (val_read && (!alarm_on)) 时,该函数都会在文件夹 ./pics/ 中创建一个文件(在模拟中,每 2 次满足此条件)循环)。该功能只允许文件夹中包含 10 个文件。如果已经有 10 个,它将删除最旧的一个并通过调用函数 takePic 创建新文件。

文件名存储在字符串数组 char* names[24]; 中,变量 nexEl 指向该数组中名称为最旧的文件,以便将其替换为刚刚创建的新文件的名称。

问题

问题如下:数组 char* names[24] 在第一次迭代时正确填充,但在第二次迭代中某些元素已被覆盖。当文件夹具有最大文件数 (10) 时,可能会更新阵列,就会出现问题。 似乎对 printf 的调用覆盖了它的一些元素,例如其中一个元素包含字符串 “删除/var/www/html/*.jpg 时出错,可能不存在\n " 打印在函数 takePic 内。

我在管理字符串数组时遗漏了什么或做错了什么?

实用功能

此处简要描述并报告了程序中使用的其他功能。 函数 getDateStryyyy_mm_dd_hh_mm_ss 格式构建表示当前日期的字符串。

函数filesByName构建一个字符串数组,其中每个字符串都是文件夹./中文件的名称,从最后创建的文件到最新的文件排序。

函数printFiles打印前一个数组。

void getDateStr(char str[20])
{
    char year[5], common[3];

    time_t t = time(NULL);
    struct tm tm = *localtime(&t);
    str[0]='\0';
    sprintf(year, "%04d", tm.tm_year+1900);
    strcat(str, year);
    sprintf(common, "_%02d", tm.tm_mon + 1);

    strcat(str, common);

    sprintf(common, "_%02d", tm.tm_mday);
    strcat(str, common);

    sprintf(common, "_%02d", tm.tm_hour);
    strcat(str, common);

    sprintf(common, "_%02d", tm.tm_min);
    strcat(str, common);

    sprintf(common, "_%02d", tm.tm_sec);
    strcat(str, common);
    //printf("%s\n", str);

}

unsigned int countFiles(char* dir)
{
    unsigned int file_count = 0;
    DIR * dirp;
    struct dirent * entry;
    dirp = opendir(dir); /* There should be error handling after this */
    while ((entry = readdir(dirp)) != NULL) {
        if (entry->d_type == DT_REG) { /* If the entry is a regular file */
             file_count++;

        }
    }
    return file_count;

}

void printFiles(char* names[24], unsigned int file_count)
{
    for (int i=0; i<file_count; i++)
    {
        printf("%s\n", names[i]);
    }
}

unsigned int filesByName(char* names[24], char* dir)
{
    unsigned int file_count = 0;
    DIR * dirp;
    struct dirent * entry;
    dirp = opendir(dir); /* There should be error handling after this */

    while ((entry = readdir(dirp)) != NULL) {
        if (entry->d_type == DT_REG) { /* If the entry is a regular file */
             //strncpy(names[file_count], entry->d_name,20);
            //names[file_count] = malloc(24*sizeof(char));
            names[file_count] = entry->d_name;
             file_count++;
        }
    }
    closedir(dirp);
    char temp[24];
    if (file_count>0)
    {
        for (int i=0; i<file_count-1; i++)
        {
            for (int j=i; j<file_count; j++)
            {
                if (strcmp(names[i], names[j])>0)
                {
                    strncpy(temp, names[i],24);
                    strncpy(names[i], names[j],24);
                    strncpy(names[j], temp, 24);
                    }
            }
        }
    }
    return file_count;
}

为了进行模拟,我还创建了以下函数(digitalRead 实际上是 Raspberry 的 wiringPi C 库的函数):

int digitalRead(int INPIN)
{
    static int res = 0;
    res = !res;
    return res;
}

最佳答案

task1中,您有char *names[24]。这是一个 char 指针数组。

filesByName 中,您可以这样做

名称[file_count] = 条目->d_name;

但应该这样做

names[file_count] = strdup(entry->d_name);

因为您无法保证 d_name 在函数返回后甚至在循环内持续存在或唯一。您已经接近注释掉的 malloc 调用。

因为您[可能]多次调用filesByName,所以它需要检查names[file_count]是否为非空,以便它可以执行free 在执行 strdup 之前[从先前的调用中释放旧的/过时的值]以防止内存泄漏。

同样,在task1中,

strcpy(names[nexEl], picname); //ERROR HERE

会有类似的问题,应该替换为:

if (names[nexEl] != NULL)
    free(names[nexEl]);
names[nexEl] = strdup(picname);

可能还有其他地方需要类似的调整。并且,请注意,在 task1 中,names 应预先初始化,将为 NULL


解决此问题的另一种方法是更改​​ names [各处] 的定义:

char *names[24];

至:

char names[24][256];

这可以避免一些 malloc/free 操作。

关于对 printf 的调用似乎覆盖了字符串数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49476365/

相关文章:

c - 设置嵌入式系统的运行配置

java - 在另一个 JSON 数组中解码 JSON 数组中的信息

当我只更新该数组的一个索引时,javascript数组的两个索引值都会更新

c - 如何将 char** 中的每个字符串打印到 printf()?

c - C 中的字符串常量

c - 为什么我在涉及二维数组的代码中遇到此错误?

c - 为什么 printf 函数在数字顺序与格式不对应的情况下按顺序打印结果?

c - 递增指针并分配一个新值 - 堆栈损坏

ruby - 阵列半展平

c - 如何确保套接字 I/O 的 NULL 终止字符串 C 编程