2014-01-16 8 views
1

저는 항상 char 포인터, 문자열, 문자열 및 대부분의 포인터 관련 개념에 문제가있었습니다. 이 ;-) 어쩌면 내가strcpy는 Arduino의 이전 시리얼을 엉망으로 만들고있는 것 같습니다.

세계적으로 선언 너무 늙었 어 :

char * message;

serialOut 매우 짧은 8 문자 문자열, 식별자 (X10D) 한 후 데이터 (nnn) 및 널 종결 자. 나는 시리얼을 통해 전송 된 데이터가 앞에서 다듬어지고, idetifier가 누락 된 것을 발견하고있다. 첫 번째 통과에서는 완전하고 정확하지만 후속 통과에서는 세 자리 만 수신되었습니다.

message은 디버깅을 위해 화면에 출력하는 디버그 메시지입니다.

deviceonOff이 올바르게 채워집니다.

기능 일으키는 문제 :

byte device = btag-X10_TAG_OFFSET; 
byte onOff; 
char serialOut[8]; 
memset (serialOut, 0, 8); 
if(x10[device - 1]==1){ 
    onOff = 0; 
} else { 
    onOff = 1; 
} 
x10[device-1]=-2; 
sprintf(serialOut, "X10D%02i%i", device, onOff); 
Serial.println(serialOut); 
strcpy(message, serialOut); // this line appears to 'modify' the previous line 

내가 마지막 줄을 제거하고 그것을 바꿀 경우 :

message = serialOut; 

선행하는 직렬 통신이 완료됩니다!

둘 다 가지고 있지 않다면 다른 쪽 끝의 데이터가 가비지 (아직 해독되지는 않지만 인쇄 할 수없는 문자로 표시됩니다. 이것이 디버그를 설정하는 이유입니다).

나는 이것이 관련 될 수 없다고 생각하지만, 평등은 문제를 '고치는'것처럼 보인다.

+0

지금 마지막 줄을 모두 제거하면 정상으로 돌아갑니다. 직렬 회선에 라디오 간섭이 있습니까? 나는 잘 모르겠다. 분명히'message = serialOut' 결과는 쓰레기가됩니다. 그렇다면 코드에서 나중에 사용하기 위해'serialOut'을'message'에 어떻게 복사합니까? – Madivad

+0

내가 정말로 얻지 못하는 이유는 memcpy, memmove, strcpy를 사용하는 이유입니다. serial.println을 실행하면 오류가있는 데이터가 전송 된 후에 결과가 – Madivad

답변

1

message은 포인터이므로 올바른 메모리를 가리켜 야합니다. 그렇게하지 않으면 전역 변수이므로 0이됩니다 (즉, NULL을 가리킴). 복사하면 의 정의되지 않은 동작이됩니다. 당신이를 할당 경우 message 점을, serialOut는 지역 변수 것 같습니다과 기능이 반환에 정의되어 때 범위를 벗어난 것 같은

는, 대신 정의되지 않은 동작이 다른 경우가 serialOut를 가리 키도록 사용되지 않는 메모리로.

두 가지 확실한 해결 방법은 message에 복사하려는 항목을 저장할 수있는 충분한 크기의 배열을 만들거나 복사하기 전에 매번 충분한 공간을 동적으로 할당/재 할당하는 것입니다.

+1

입니다. 아니오, Arduino에 동적으로 아무것도 할당하지 마십시오. 매우 제한된 8 비트 MCU를 가지고 있습니다. – Lundin

+0

그래서'message'를 정의하는 가장 좋은 방법은 무엇입니까? 예 :'char message [10]; mem30 (메시지, 0, 10); ' – Madivad

+0

@Madivad 메시지가 10 자 (문자열 종결 자 포함)보다 길지 않으면 가장 좋은 해결책 일 것입니다. 오, 당신은'memset','sprintf' /'strcpy'를 사용할 필요가 없으며 다른 모든 문자열 함수는 자동으로 문자열 터미네이터를 추가 할 것입니다. –

0

동적 할당은 8 비트 MCU가 아니고 스택 힙 충돌 가능성이 있기 때문에 AVR에서는 좋지 않습니다. 위의 문제는 당신이 글로벌 char *를 가지고 있으며, 글로벌이되어 NULL으로 초기화됩니다. [이것은 C 표준의 일부이며, 이것에 대한 제어권이 없습니다. 최종 실행 파일의 __do_clear_bss 섹션에서 수행됩니다]. 이 NULL 포인터에 strcpy()을 수행하는 순간 주소 0에서 쓰기 시작합니다. 이제 이것이 x86 시스템이었고 응용 프로그램 수준에서 작업 중이었던 경우 소프트웨어가 세그먼트 오류로 중단되었습니다. AVR에는 메모리 보호 기능이 없으므로 strcpy()은 문자열을 RAM의 0 번 주소로 복사하기 시작합니다. 이것은 절대 주소를 사용하는 경우 레지스터 파일로 바로 넘어갑니다. 즉,이 레지스터에 캐시 된 모든 변수가 이제 휴지통에 있음을 의미합니다., AVR-GCC 버전 4.8.2 및 avr-gcc -O3 -mmcu=atmega168 -o avr.elf avr.c

수동 설정 AVR 명령어에서

와 컴파일러를 호출하여 수행하는 경우

-- snip -- 
00000096 <main>: 
-- snip -- 
    a0: cd b7   in r28, 0x3d ; 61 
    a2: de b7   in r29, 0x3e ; 62 
    a4: 86 e0   ldi r24, 0x06 ; 6 
    a6: e0 e0   ldi r30, 0x00 ; 0 
    a8: f1 e0   ldi r31, 0x01 ; 1 
    aa: de 01   movw r26, r28 
    ac: 11 96   adiw r26, 0x01 ; 1 
    ae: 01 90   ld r0, Z+ 
    b0: 0d 92   st X+, r0 
    b2: 8a 95   dec r24 
    b4: e1 f7   brne .-8   ; 0xae <main+0x18> 
    b6: be 01   movw r22, r28 
    b8: 6f 5f   subi r22, 0xFF ; 255 
    ba: 7f 4f   sbci r23, 0xFF ; 255 
    bc: 80 e0   ldi r24, 0x00 ; 0 
    be: 90 e0   ldi r25, 0x00 ; 0 
    c0: 0e 94 63 00  call 0xc6 ; 0xc6 <strcpy> 
-- snip -- 
000000c6 <strcpy>: 
    c6: fb 01   movw r30, r22 
    c8: dc 01   movw r26, r24 
    ca: 01 90   ld r0, Z+ 
    cc: 0d 92   st X+, r0 
    ce: 00 20   and r0, r0 
    d0: e1 f7   brne .-8   ; 0xca <strcpy+0x4> 
    d2: 08 95   ret 

:로 컴파일

#include <string.h> 
int main(void) { 
    char p[] = "hello"; 
    strcpy(0, p); 
    while(1); 
} 

:이 테스트 케이스를 고려 "SRAM이있는 부품의 경우 레지스터 파일, I/O 메모리 및 내부 SRAM (및 해당되는 경우 외부 SRAM)으로 구성되는 데이터 공간은 레지스터에서 데이터 공간으로의 변위의 유무에 관계없이 1 바이트 간접 저장됩니다. . SRAM이없는 부품의 경우 데이터 공간은 레지스터 파일로만 구성됩니다. " 상기를 뒷받침하는

. 분명히, 컴파일러는 레지스터를 휴지통에 넣었다는 것을 실제로 알지 못하고, 다른 것들에 행복하게 사용할 것이고, 심지어는 더 이상 쓰레기를 버릴 것입니다. strcpy() 후에 그것들을 읽으면 문제가 생길 것이다. 전역 화하려는 경우 버퍼를 만들어 char message[8];과 같이 문제를 해결할 수 있습니다. 전역 변수이므로 모든 요소가 자동으로 0으로 초기화됩니다. 왜 데이터가 휴지통으로 옮겨 지는지에 대해서는 이유를 파악하기 위해 더 많은 코드를 확인해야합니다. 대부분의 경우 메모리 손상으로 인해 발생합니다.

건배.

관련 문제