在C语言中,一个字面量字符串的地址是否总是大于任何已分配字符串的地址?

3
我试图区分字面字符串和已分配的字符串,以便我不会意外尝试释放字面字符串,这将导致段错误。
我可能尝试释放字面字符串的原因包括可以嵌入的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");

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


在比较之前将变量转换为“unsigned long”类型,可以提高“SafeFree()”函数的可移植性(尽管它无法工作),并且使用“uint_ptr”类型效果略佳。 - Pascal Cuoq
你可以查看这个SO线程 - Agnius Vasiliauskas
谢谢,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的工作原理。 - Sam Porch
4个回答

5

没有这样的保证,不要依赖它。
字符串字面量在只读实现定义的某个内存区域中分配,无法以可移植的方式知道它将在哪里,因此您不应做出任何假设。


谢谢提供信息。虽然这很不幸,但是使用setjmp()和longjmp()异常处理方式能够避免段错误吗? - Sam Porch
4
@SamPorch说:调用free()并释放一个有效指针的责任在于调用者,因此我认为你采取的过度防御性编程方法有些多余。 - Alok Save
我明白了。也许我应该重新考虑我的strcatex()函数。 - Sam Porch

1
情况比你想象的还要“糟糕”。比较仅对指向同一对象(或指向其后面一字节)的指针定义。因此,通常对于两个不同的对象(无论是静态分配为字符串文字还是动态使用 malloc 等分配),甚至不能询问其中一个是否具有比另一个更小的地址。在大多数平台上,这样的比较将起作用,但严格意义上讲,你不能依赖它。

OP将指针强制转换为整数类型,这是实现定义的,然后进行整数比较。我同意你的评论,但我认为对于想要比较指针的人来说,OP至少是以正确的方式进行比较。 - Pascal Cuoq

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

完整可运行的测试:

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

0

不仅没有保障,相反情况更为普遍的系统也很常见。例如,在我的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 < q的测试通常编译为仅在段内偏移量之间进行比较。例如,如果pq都指向它们(不同的)段的开头,则它们的偏移量都为零,即使地址不同。因此,p < qp > q都为false,但p != q


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接