2012-04-20 2 views
3

실수로 segfault를 일으키는 리터럴 문자열을 시도하지 않도록 할당 된 문자열과 리터럴 문자열을 구별하려고합니다.C에서 리터럴 문자열 주소는 항상 할당 된 문자열 주소보다 커야합니까?

내가 리터럴 문자열 thusly 히 내장 할 수있는 strcat와() 래퍼 포함) (무료로 시도 할 수 있습니다 이유 : 이유에 관계없이 strcatex("My name ", strcatex("is ", strMyName));

을 고려 :

void* mallocex(int intBytes) 
{ 
    void* newPtr; 

    newPtr = malloc(intBytes); 

    if(newPtr == 0) 
     return 0; 
    else if(newPtr > maxPointer) 
     maxPointer = newPtr; 

    return newPtr; 
} 

int SafeFree(void **ptr) 
{ 
    if((unsigned long) ptr > (unsigned long) maxPointer) 
     return 0; 
    else 
     free(*ptr); 

    return 1; 
} 

사용 예제 :

char *newString; 
newString = (char*) mallocex(12); 

strcpy(newString, "Example one"); 

SafeFree(&newString); 
SafeFree("Example two"); 

힙 크기에 관계없이 항상 작동합니까?

+0

+1 (비록 작동하지 않습니다). 'uint_ptr'유형은 약간 더 좋을 것입니다. –

+0

[이 SO 스레드]를 확인할 수도 있습니다 (http://stackoverflow.com/questions/2322647/how-to-detect-the-passing-of-a-string-literal-to-a-function-in-c) –

+0

고마워요, 0x69, 그 스레드에서 귀하의 답변을 참조하십시오, 그 setjmp, longjmp 가능한 유일한 방법입니다 (권장하지 않음). 나는 char * strcatex (char * stringOne, char * stringTwo); 함수를 char * strcatex (char * stringOne, int doFreeOne, char * stringTwo, int doFreeTwo);로 재 작성하기로 결정했다. if (doFreeOne) free (stringOne);'단계입니다. setjmp, longjmp가 어떻게 작동하는지 보여 주셔서 감사합니다. –

답변

5

보증에 의거하지 마십시오.
문자열 리터럴은 읽기 전용 구현 정의 영역의 어딘가에 메모리가 할당되어 있습니다.이 영역은 이동식으로 무엇이 될지를 알 수 없으므로 이에 대해 어떠한 가정도해서는 안됩니다.

+0

정보를 제공해 주셔서 감사합니다. 그것은 불행한 일이다. 일부 유형의 setjmp() 및 longjmp() 예외 처리 방법이 segfault를 피하기 위해 작동합니까? –

+4

@SamPorch : 유효한 포인터로'free()'를 호출해야하는 책임은 호출자에게 달려 있습니다. 그래서 방어 프로그래밍에 과도하게 접근 한 접근법을 발견합니다. –

+0

나는 본다. 아마도 strcatex() 함수를 다시 생각해보아야합니다. –

1

상황은 상상하는 것보다 "악화"됩니다. 비교는 동일한 객체를 가리키는 포인터 (또는 그 이상의 바이트)를 가리키는 포인터에 대해서만 정의됩니다. 일반적으로 문자열 리터럴로 또는 동적으로 malloc 또는 그 이상으로 할당 된 두 개의 다른 객체에 대해서는 다른 하나보다 작은 주소가 있는지 여부를 묻지도 못합니다. 대부분의 플랫폼에서 이와 같은 비교가 가능하지만 엄밀한 의미에서 의존 할 수는 없습니다.

+0

OP는 포인터를 구현 정의 인 정수 유형으로 캐스트 한 다음 정수를 비교합니다. 나는 당신의 발언에 동의하지만, 나는 포인터를 비교하기를 원하는 누군가에게 OP가 적어도 올바른 방법으로 그것을하고 있다고 생각한다. –

0

보증이없는 것뿐만 아니라 정반대 인 시스템은 매우 일반적입니다. 예를 들어 맥 OS (내 컴퓨터)에 :

#include <stdio.h> 
#include <stdlib.h> 

void pr_addr(const char *name, void *addr) { 
    printf("%s: %p\n", name, addr); 
} 

int main(void) { 
    pr_addr("literal", "literal"); 
    pr_addr("heap", malloc(10)); 
    return 0; 
} 
$ cc -o foo -W -Wall -O foo.c 
$ ./foo 
literal: 0x1ff1 
heap: 0x100160 

그래서 여기 rodata 섹션은 malloc 경기장 잘 아래의 시작, 당신이 당신의 시스템에 표시되는 내용의 반대입니다.

일반적으로 조각 세분화 또는 기타 구조화 된 포인터 유형이있는 시스템은 인접하지 않은 개체 간의 의미없는 비교로 마무리됩니다. p < q에 대한 테스트는 종종 세그먼트 내 오프셋의 비교로 컴파일됩니다. 예를 들어, pq이 모두 (다른) 세그먼트의 시작을 가리키는 경우 주소가 다르더라도 오프셋 모두가 0입니다. 따라서 p < qp > q은 모두 거짓이며 아직 p != q입니다.

0

재미 있기 때문에 다른 리터럴의 주소를 기반으로 추측하려고 시도했습니다. 잠재적 리터럴의 주소를 다른 알려진 리터럴, 스택 주소 및 힙 주소와 비교하여 작동합니다. 잠재적 주소가 다른 주소보다 리터럴에 더 가깝다면 잠재적 주소가 리터럴이라고 가정합니다. 아마도 실제로는 신뢰할 수 없습니다. 다음은 단순한 버전입니다.

int is_literal_simplistic(char *s) { 
    char *literal = "literal"; 
    char stack[] = "stack"; 
    char *heap = malloc(1); 
    free(heap); 
    unsigned long literal_delta = labs(literal - s); 
    unsigned long stack_delta = labs(stack - s); 
    unsigned long heap_delta = labs(heap - s); 
    return (literal_delta < stack_delta && literal_delta < heap_delta); 
} 

여기에는 좀 더 간결한 버전이 나와 있습니다.아마 수 있습니다 간단 :

int is_literal(char *s) { 
    char *heap = malloc(1); 
    free(heap); 
    unsigned long literal_delta = labs("literal" - s); 
    unsigned long stack_delta = labs((char *)&s - s); 
    unsigned long heap_delta = labs(heap - s); 
    return (literal_delta < stack_delta && literal_delta < heap_delta); 
} 

전체 실행 가능한 테스트 : 비교하기 전에 서명되지 않은 long``에 캐스팅하여`SafeFree() '함수의 전체 휴대 성을 개선하기위한

#include <stdio.h> 
#include <math.h> 
#include <stdlib.h> 

//#define DEBUG 

#ifdef DEBUG 
void debug_literal(unsigned long literal, 
        unsigned long stack, 
        unsigned long heap, 
        unsigned long literal_delta, 
        unsigned long stack_delta, 
        unsigned long heap_delta) { 
    printf("literal(%lx) stack(%lx) heap(%lx)\n", literal, stack, heap); 
    printf("literal(%lu) stack(%lu) heap(%lu)\n", literal_delta, stack_delta, heap_delta); 
    int answer = (literal_delta < stack_delta && literal_delta < heap_delta); 
    printf("\t%s\n", answer ? "literal" : "other"); 
} 
#else 
void debug_literal(unsigned long literal, 
        unsigned long stack, 
        unsigned long heap, 
        unsigned long literal_delta, 
        unsigned long stack_delta, 
        unsigned long heap_delta) { 
} 
#endif 

int is_literal_simplistic(char *s) { 
    char *literal = "literal"; 
    char stack[] = "stack"; 
    char *heap = malloc(1); 
    free(heap); 
    unsigned long literal_delta = labs(literal - s); 
    unsigned long stack_delta = labs(stack - s); 
    unsigned long heap_delta = labs(heap - s); 
    debug_literal((unsigned long)literal, (unsigned long)stack, (unsigned long)heap, 
        literal_delta, stack_delta, heap_delta); 
    return (literal_delta < stack_delta && literal_delta < heap_delta); 
} 

int is_literal(char *s) { 
    char *heap = malloc(1); 
    free(heap); 
    unsigned long literal_delta = labs("literal" - s); 
    unsigned long stack_delta = labs((char *)&s - s); 
    unsigned long heap_delta = labs(heap - s); 
    debug_literal(0,0,0, literal_delta, stack_delta, heap_delta); 
    return (literal_delta < stack_delta && literal_delta < heap_delta); 
} 

void test_literal_function(int(*liternal_fn)(char *)) { 
    char *literal = "literal_test"; 
    char stack[] = "stack_test"; 
    char *heap = malloc(40); 

    printf("\t%s\n", liternal_fn(literal) ? "literal" : "other"); 
    printf("\t%s\n", liternal_fn(stack) ? "literal" : "other"); 
    printf("\t%s\n", liternal_fn(heap) ? "literal" : "other"); 
    printf("\n"); 

    free(heap); 
} 

int main() { 
    test_literal_function(is_literal_simplistic); 
    test_literal_function(is_literal); 

    return 0; 
}