【问题标题】:In C, will a literal string address always be greater than any allocated string address?在 C 中,文字字符串地址是否总是大于任何分配的字符串地址?
【发布时间】:2012-04-20 04:40:53
【问题描述】:

我正在尝试将文字字符串与分配的字符串区分开来,因此我不会意外尝试 free() 文字字符串,这会导致段错误。

我可能尝试 free() 文字字符串的原因涉及可以嵌入的 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");

无论我的堆有多大,这总是有效吗?

【问题讨论】:

  • +1 用于在比较之前转换为unsigned long,提高了SafeFree() 函数的整体可移植性(尽管它不起作用)。 uint_ptr 类型会稍微好一点,甚至。
  • 你也可以查看this SO thread
  • 谢谢,0x69,我在那个线程上看到了你的答案,setjmp、longjmp 是唯一可行的(虽然不推荐)方法。我决定用一个简单的if(doFreeOne) free(stringOne); 步骤将我的char *strcatex(char *stringOne, char *stringTwo); 函数重写为char *strcatex(char *stringOne, int doFreeOne, char *stringTwo, int doFreeTwo);。不过,感谢您展示 setjmp、longjmp 的工作原理。

标签: c string memory-management


【解决方案1】:

没有这样的保证,不要依赖它。
字符串文字在只读实现定义区域的某处分配了内存,无法以可移植的方式知道它将是什么,因此您不应该对它做出任何假设。

【讨论】:

  • 感谢您的信息。不过,这很不幸。某种类型的 setjmp() 和 longjmp() 异常处理方法可以避免段错误吗?
  • @SamPorch:使用有效指针调用free() 的责任在于调用者所以我发现你的方法过度杀伤了防御性编程。
  • 我明白了。或许我应该重新考虑一下我的 strcatex() 函数。
【解决方案2】:

情况比你想象的还要“糟糕”。比较仅针对指向同一对象(或仅指向超出的字节)的指针定义。因此,通常对于两个不同的对象(无论是静态分配为字符串文字还是使用malloc 动态分配),您甚至都无法询问一个对象的地址是否小于另一个对象。在大多数平台上,这样的比较都是可行的,但您不能严格地依赖它。

【讨论】:

  • OP 将指针转换为实现定义的整数类型,然后比较整数。我同意你的说法,但我认为对于想要比较指针的人来说,OP 至少是以正确的方式做的。
【解决方案3】:

不仅没有保证,相反的系统也很常见。例如,在 MacOS(我的笔记本电脑)上:

#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 &lt; q 的测试通常编译为仅比较段内偏移量。例如,如果pq 都指向它们(不同)段的开始,则它们的偏移量都为零,即使地址不同。因此p &lt; qp &gt; q 都是假的,而p != q

【讨论】:

    【解决方案4】:

    为了好玩,我写了一个尝试根据其他文字的地址进行猜测。它通过将潜在文字的地址与另一个已知文字、堆栈地址和堆地址进行比较来工作。如果潜在地址比其他地址更接近文字,则假定潜在地址是文字。在实践中可能不可靠。这是它的简化版本:

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

    完整的可运行测试:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-05-03
      • 1970-01-01
      • 2013-10-02
      • 2010-10-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多