2

私はこれを理解することはできません。Code::Blocks を使用して Windows マシンでこのコードをコンパイルすると問題なく動作しますが、Cygwin または学校の実際の Unix マシンで Make を使用してコンパイルしようとすると、以下の奇妙な動作が発生します。translate() に「client1.txt」を渡しています。

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);
}
4

3 に答える 3

6

問題は「文字列を上書きする」よりも微妙だと思います。それは、出力行のデータを上書きするという問題です。このコードを試してください:

#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

ファイル パスの引数文字列の最後にキャリッジ リターンがあり、これがこの明らかな難問につながります。

odまたはhd同様のプログラムを介して、プログラムからの出力をフィードできます。私は と呼ばれ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:
$

推測しなければならないのはclient1.txt、Windows ボックスで作成され、テキスト転送ではなくバイナリを使用して Unix に転送されたファイルからファイル名 ( ) を読み取ったgets()ことです。その前に。

メインコードが使用していることがわかりますfgets()—よりもはるかに優れていgets()ます! — ただし、CRLF の行末を処理する機能はありません。コードは、ファイル名が正常に開かれたことを確認しません。これにより、遅かれ早かれコア ダンプが発生します (特に、ディスク上の入力ファイル名が CR'\r'で終わる可能性はほとんどないため、オープンはほぼ確実に失敗します)。

  • ファイル ストリーム ポインタまたはファイル記述子を使用する前に、ファイルを開く関数が成功することを常に確認してください

私を信じてください; のように頻繁に使用され、テストされているルーチンにバグが見つかることは、実際にはめったにありませんstrcat()

于 2012-04-26T05:44:32.897 に答える
1

このような「説明のつかない奇妙な」動作を示す AC プログラムは、実際にはある種のメモリ破損を示唆しています。マルチスレッド プログラムではなおさらです。そのため、コードのこの部分に「問題を絞り込んだ」可能性があります。これは、症状が発生する場所であり、バグが発生する場所ではないためです。

translate()通常のコンテキスト外で実行してみましたか? gdb でプログラムを開始し、main() で中断して、translate("client1.txt"). それは正しく動作しますか?

はいの場合、これはプログラムの他の部分がメモリを破損していることを本当に示唆しています。そして、どの部分を見つける唯一の方法は、すべてのコードを調べるか、@jbleners がコメントで提案したように valgrind のようなツールを使用することです。

于 2012-04-26T05:15:34.257 に答える
0

確かに、期待どおりに動作するはずです。

考えられる代替手段の 1 つは、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") が生成されます。

于 2012-04-26T04:19:08.340 に答える