2012-04-26 3 views
2

저는 이것을 이해할 수 없습니다. 이 코드를 Code :: Blocks가있는 Windows 컴퓨터에서 컴파일하면 제대로 작동하지만 Cygwin 또는 실제 유닉스 컴퓨터에서 Make를 컴파일하려고하면 학교에서 이상한 동작이 발생합니다. translate()에 "client1.txt"를 전달합니다.strcat가 내 문자열을 덮어 씁니다.

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); 
} 
+0

여기 유닉스에서 gcc를 사용하여 테스트 했으므로 제대로 작동합니다. 어쩌면 컨텍스트/정보를 조금 더 제공 할 수 있습니까? –

+0

그럼 바로 출력 해 봅시다. 출력은 "client1.txt \ n.translated \ n.translated"이지만 "client1.txt \ nclient1.txt.translated \ nclient1.txt.translated"여야합니까? 후자가 내가 얻는 것이다. 그것은 당신의 경우에'strcat' 대신에'strcpy'를 호출하는 것과 같은 종류의 소리입니다. – Edmund

+0

나는'do_work' 함수에서 (지금 당장은 매우 유용한 설명 이름을 무시할 것입니다.) 여러분은'strncpy'를 제한된'strcpy'로 사용하고 있습니다. 실제로' t는 문자열을 nul-terminate하는 것이 보장되지 않기 때문이다. 이것은 약간의 귀찮은 코너 케이스를 만들 가능성이 있습니다. 아마 여기 버그는 아니지만, 아직 사용하지 않는 것이 좋습니다. –

답변

0

음, 확실히 이 작동해야한다.

한 가지 가능한 대안은 대신 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") .

+0

나는 당신이 가지고있는 것을 시험해 보았는데, 여전히 그것을 덮어 쓰고 있고, 단지 "번역 된"이라고 말하고 있습니다. 이것은 말이되지 않습니다! – Sam

+1

@Sam : 문제를 컴파일하고 보여주는 완벽한 프로그램을 게시 할 수 있습니까? –

+0

Ed. 나는 strcat output_filepath가 "client1.txt"이고 ".translated"가되기 전에 그렇게 확신한다. 나는 strcat 작품을 알고 있지만 왜 나 에게이 문제를 제공할지 모르겠다. – Sam

1

이러한 "설명 할 수없는 이상한"동작을 나타내는 C 프로그램은 실제로는 멀티 스레드 프로그램에서 메모리 손상을 일으킬 수 있습니다. 따라서 코드의이 부분에 문제가 "좁혀진"것일 수 있습니다. 그 이유는 그것이 증상이 발생하는 곳이기 때문입니다. 그러나 버그가있는 곳이 아니기 때문입니다.

정상적인 상황에서 translate()을 실행 해 보셨습니까? gdb에서 프로그램을 시작하고 main()을 중단 한 후 translate("client1.txt")을 실행하십시오. 올바르게 동작합니까?

그렇다면 실제로 프로그램의 다른 부분이 메모리를 손상시키고 있음을 알 수 있습니다. 그리고 모든 코드를 연구하는 부분을 찾아내는 유일한 방법은 주석에서 @jbleners가 제안한 valgrind와 같은 도구를 사용하는 것입니다.

+0

translate()를 단독 파일로 실행하면 올바른 출력이 생성됩니다. 정말로 약간의 기억 문제가 있어야합니다. 나는 valgrind를 사용하고 내가 찾을 수있는 것을 보려고 노력할 것입니다. – Sam

6

문제가 '문자열 덮어 쓰기'보다 더 미묘하다고 생각합니다. 출력 라인에있는 데이터를 덮어 쓰는 것이 더 문제입니다. 이 코드를보십시오 : 맥 OS X 10.7.3에

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

출력 :이 명백한 수수께끼에 이르게 파일 경로 인수 문자열의 끝에 캐리지 리턴을 가지고

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가) 윈도우 박스에서 생성 된 파일에서 텍스트 전송이 아닌 바이너리를 사용하여 유닉스로 전송하면, gets()으로 읽는다면 그 전에 캐리지 리턴이 아닌 개행 문자가 제거 될 것이기 때문일 것이다.

나는 주 코드가 fgets() —을 사용하여 훨씬 더 gets()! —하지만 CRLF 줄 끝을 처리 할 준비가되어 있지 않습니다. 코드에서 파일 이름이 성공적으로 열렸는지 확인하지 않습니다. 조만간 코어 덤프로 이어질 것입니다 (특히 디스크의 입력 파일 이름이 CR '\r'으로 끝날 가능성이 거의 없으므로 열기가 거의 실패 할 것이기 때문에).

  • 항상
    하지만, 항상 파일 열기 기능은 파일 스트림 포인터 또는 파일 기술자를 사용하기 전에 성공 있는지 확인합니다.

나를 믿으십시오. 많이 사용되지 않고 strcat()으로 테스트 된 루틴에서 버그를 발견하게되는 경우는 거의 없습니다.