【问题标题】:strcat overwrites my stringstrcat 覆盖我的字符串
【发布时间】:2012-04-26 04:03:46
【问题描述】:

我想不通。当我在我的 Windows 机器上使用 Code::Blocks 编译这段代码时,它工作得很好,但是当我尝试在 Cygwin 或学校的实际 Unix 机器下使用 Make 编译它时,我得到了下面的奇怪行为。我将“client1.txt”传递给 translate()。

void translate(char* filepath){

  char output_filepath[181];
  strcpy(output_filepath, filepath);
    printf("%s\n", output_filepath);      //this prints out "client1.txt" which is correct
  char* ptr = strcat(output_filepath, ".translated");
    printf("%s\n", output_filepath);      //this prints out ".translated" which is wrong
    printf("%s\n", ptr);                  //also prints out ".translated" wrong again

...more stuff...
}

当尝试在 output_filepath 上使用 fgets 时,这会导致分段错误。有谁知道发生了什么?我需要解释更多吗?它必须能够在 Unix 下编译。

这是整个程序,我希望它不会太长。这是我的老师给我们的一个程序,但我什至无法运行它。它只是给了我一个分段错误,我已经在上面的部分中找到了它。

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

struct tparam{
int tid;
};

int NTHREADS  =  5;
#define LOOPS         10000
int qfilled = 0;
int qin = 0;
int qout = 0;
char queue[3000][2][161];
char dic[7000][2][161];
int dic_size = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t flag_mutex = PTHREAD_MUTEX_INITIALIZER;

char * dummystatus[10];

char * lookup(char * word)
{
int i;
for(i = 0; i < dic_size; ++i)
{
    if(strcmp(word, dic[i][0])==0)
        return dic[i][1];
}
return word;
}
void translate(char filepath[])
{
char output_filepath[181];
strcpy(output_filepath, filepath);
strcat(output_filepath, ".translated");

FILE * client_file = fopen(filepath,"r");
FILE * translated_client_file = fopen(output_filepath,"w");
char line [161];
char * tmpPtr;
char * token;
while((tmpPtr=fgets(line, 160, client_file))!= NULL) {
    if(strcmp(line,"\n") == 0 || line == NULL)
        continue;
    token = strtok_r(line, " \t\n", dummystatus);

    while(token != NULL && strcmp(token,"\n") != 0){
        fputs(lookup(token), translated_client_file);
        fputs(" ", translated_client_file);
        token = strtok_r(NULL, " \t\n", dummystatus);
    }
    fputs("\n", translated_client_file);
}
fclose(client_file);
}
void *do_work(void * p) {
  struct tparam * param = (struct tparam *)p;

  while(qfilled != 1);//wait for queue to be filled

  int cindex;
  while(1){
      //check for more clients
  pthread_mutex_lock(&mutex);
  if(qout >= qin){
    pthread_mutex_unlock(&mutex);
    break;
  }
  //process client
  cindex = qout;
  printf("Thread %d is handling client %s\n",param->tid, queue[cindex][1]);
  qout++;
  pthread_mutex_unlock(&mutex);

  char filepath[161];
  if(queue[cindex][0][strlen(queue[cindex][0])-1] == '\n')
      strncpy(filepath,queue[cindex][0],strlen(queue[cindex][0])-1);
  else
    strcpy(filepath,queue[cindex][0]);
  translate(filepath);
  printf("Thread %d finished handling client %s\n",param->tid, queue[cindex][1]);

      //usleep(rand()%100000+10000);
  }
  pthread_exit(NULL);
}

void addDicEntry(char line[]){
char * first = strtok_r(line, " \t", dummystatus);
char * second = strtok_r(NULL, " \t", dummystatus);
char englishWord[161];
if(1==1 || second != NULL)
{
    strcpy(dic[dic_size][0], first);
    strncpy(englishWord, second, strlen(second)-1);
    englishWord[strlen(second)-1] = '\0';
    strcpy(dic[dic_size][1], englishWord);
    dic_size++;
}
}

int main(int argc, char *argv[]) {

srand(time(NULL));
  if(argc < 2){
printf("No dictionary file provided\n");
exit(1);
  }
  //read dictionary
  int i;
  for(i = 0; i < 10; ++i)
dummystatus[i] = (char*)malloc(10);

  FILE * dic_file = fopen(argv[1],"r");
  if(dic_file == NULL)
 {
printf("Dictionary file does not exist\n");
exit(1);
  }
  char line [161];
  char * tmpPtr;
  while((tmpPtr=fgets(line, 160, dic_file))!= NULL) {
if(strcmp(line,"\n") == 0 || strcmp(line,"") == 0 || line == NULL)
    break;
addDicEntry(line);
  }
  fclose(dic_file);
  //End read dictionary

  //Creating threads
  if(argc >= 3)
NTHREADS = atoi(argv[2]);

  pthread_t * threads = (pthread_t *)malloc(NTHREADS*sizeof(pthread_t));
  pthread_attr_t attr;

  pthread_attr_init(&attr);
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
  for (i=0; i<NTHREADS; i++) {
struct tparam * param = (struct tparam *)malloc(sizeof(struct tparam *)*1);
param->tid = i+1;
//printf("Thread %d is being created\n",param->tid);
pthread_create(&threads[i], &attr, &do_work, param);
  }
 //End creating threads

  //insert clients in Q
  FILE * clients_file = fopen("clients.in","r");

  char cid_str[10];
  while((tmpPtr=fgets(line, 160, clients_file))!= NULL) {
if(strcmp(line,"\n") == 0 || strcmp(line,"") == 0 || line == NULL)
    break;
pthread_mutex_lock(&mutex);
strcpy(queue[qin][0],line);
sprintf(cid_str, "%d", qin+1);
strcpy(queue[qin][1],cid_str);
qin++;
pthread_mutex_unlock(&mutex);
  }
  fclose(clients_file);
//for (i=0; i<qin; i++)
//printf("%s\n", queue[i][0]);
  qfilled = 1;
  printf("Q Filled\n");
  //End insert clients in Q

  //printf("Waiting for Threads\n");
  for (i=0; i<NTHREADS; i++)
pthread_join(threads[i], NULL);
  //printf("Threads Finished\n");

  pthread_attr_destroy(&attr);
  pthread_exit(NULL);
}

【问题讨论】:

  • 我们刚刚在 Unix 下使用 gcc 测试了这个,它工作正常。也许您可以提供更多上下文/信息?
  • 所以让我直截了当,输出是:“client1.txt\n.translated\n.translated”,但应该是“client1.txt\nclient1.txt.translated\nclient1.txt” .翻译”?后者是我得到的。就你而言,这听起来像是在调用 strcpy 而不是 strcat
  • 我想指出,在您的 do_work 函数中(我现在将忽略那个极具描述性的名称...)您使用 strncpy 作为有界 @987654327 @,它实际上不是,因为它不能保证终止字符串。这可能会产生一些麻烦的极端情况,这可能不是这里的错误,但您仍然应该考虑不要使用它。
  • "希望不会太长";我会说是的。
  • 尝试使用类似 valgrind 的工具来检查内存错误。

标签: c unix strcat


【解决方案1】:

我怀疑这个问题比“覆盖你的字符串”更微妙;它更多的是覆盖输出线上的数据的问题。试试这个代码:

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

void translate(char* filepath)
{
  char output_filepath[181];

  strcpy(output_filepath, filepath);
    printf("%s\n", output_filepath);      //this prints out "client1.txt" which is correct
  char* ptr = strcat(output_filepath, ".translated");
    printf("%s\n", output_filepath);      //this prints out ".translated" which is wrong
    printf("%s\n", ptr);                  //also prints out ".translated" wrong again
}

int main(void)
{
    translate("client1.txt\r");
    return(0);
}

Mac OS X 10.7.3 上的输出:

client1.txt
.translated
.translated

您在文件路径参数字符串的末尾有一个回车符,这导致了这个明显的难题。

您可以通过odhd 或类似程序提供程序的输出。我的叫odx,但任何事情都可以(而且你的程序被称为x39,除了它是一个新文件名之外没有任何充分的理由):

$ ./x39 | odx
0x0000: 63 6C 69 65 6E 74 31 2E 74 78 74 0D 0A 63 6C 69   client1.txt..cli
0x0010: 65 6E 74 31 2E 74 78 74 0D 2E 74 72 61 6E 73 6C   ent1.txt..transl
0x0020: 61 74 65 64 0A 63 6C 69 65 6E 74 31 2E 74 78 74   ated.client1.txt
0x0030: 0D 2E 74 72 61 6E 73 6C 61 74 65 64 0A            ..translated.
0x003D:
$

如果我不得不猜测,您从在 Windows 机器上创建的文件中读取文件名 (client1.txt),然后使用二进制而不是文本传输将其传输到 Unix,您可能使用gets() 读取它,因为将删除换行符而不是之前的回车符。

我看到主要代码使用fgets() — 比gets() 好得多! — 但它不具备处理 CRLF 行尾的能力。您的代码不会检查文件名是否已成功打开;这迟早会导致核心转储(特别是因为磁盘上的输入文件名不太可能以 CR '\r' 结尾,因此打开几乎肯定会失败)。

  • 总是,但总是在使用文件流指针或文件描述符之前检查文件打开函数是否成功。

相信我;确实很少会在像strcat() 这样大量使用和测试的例程中发现错误。

【讨论】:

    【解决方案2】:

    表现出这种“无法解释的怪异”行为的 C 程序确实表明存在某种内存损坏——在多线程程序中更可能出现这种情况。因此,您可能已经将问题“缩小”到代码的这一部分,因为这是症状出现的地方,但这不是错误所在。

    您是否尝试过在其正常上下文之外运行translate()?在 gdb 中启动您的程序,在 main() 上中断,然后运行 ​​translate("client1.txt")。它的行为是否正确?

    如果是,这确实表明程序的某些其他部分正在破坏内存。找出哪一部分的唯一方法是研究所有代码,或者使用 cmets 中 @jbleners 建议的 valgrind 之类的工具。

    【讨论】:

    • 在自己的文件中单独运行 translate() 会产生正确的输出。确实一定有一些内存问题。我会尝试使用 valgrind 看看我能找到什么。
    【解决方案3】:

    嗯,它当然应该按您的预期工作。

    一种可能的替代方法是使用sprintf

    void translate(char *filepath) { 
        char output_filepath[181];
    
        sprintf(output_filepath, "%s.translated", filepath);
        printf("%s\n", output_filepath);
    }
    

    应该产生与您正在使用的结果相同的结果(应该),但如果您遇到某种错误,也许不同的功能会更好地工作。快速测试表明它对我有用,但我很确定使用 strcpy/strcat 的版本也可以,所以你必须测试才能知道。

    编辑:这是一个完整的演示程序:

    #include <stdio.h>
    
    void translate(char *filepath) { 
        char output_filepath[181];
    
        sprintf(output_filepath, "%s.translated", filepath);
        printf("%s\n", output_filepath);
    }
    
    int main(){
        translate("client1");
        return 0;
    }
    

    对我来说,使用我目前使用的编译器(VC++ 10、g++ 4.7),这会产生预期的输出(“client1.translated”)。

    【讨论】:

    • 我尝试了你所拥有的,但它仍然覆盖并只给我“.translated”。这没有意义!
    • @Sam:你能发布一个完整的程序来编译和演示这个问题吗?
    • 编辑。我确信在 strcat output_filepath 之前是“client1.txt”,在它变成“.translated”之后。我知道 strcat 有效,但我不知道为什么它会给我这个问题。
    猜你喜欢
    • 2011-07-05
    • 2017-01-24
    • 2022-01-21
    • 1970-01-01
    • 2014-06-27
    • 1970-01-01
    • 1970-01-01
    • 2012-04-08
    • 2018-12-18
    相关资源
    最近更新 更多