3

I am creating scripting language. When I allocate thing ,it's allocate the thing and returns the address and then I do whatever with it and then delete it. I can't control the variables in it like creating struct in my lang (Struct with pointer and bool to check if pointer is pointing to valid data) and etc because it'll make my lang slower and bigger in the RAM.

For example: (My scripting language is easily to understood. I doubt you'll not understand this ,but I'll put some comments in it anyway)

MyStruct = { //Function. For create object with it use 'new' before it.
    TestAliveVar=0
}
Func = { //I'll explain what exactly this function does every place it runs.
    if (!exists(arg0)) //C++: ???
        exit;
    arg0.TestAliveVar=1
    println "Still alive!";
}
var MyVar=new MyStruct(); //Returns address of the new object in the heap
                          //and runs on it the `MyStruct` function.
Func(MyVar);              //Sets his 'TestAliveVar' to 1
                          //and prints 'Still Alive!' with new line
delete(MyVar);            //C++: free(MyVar);
Func(MyVar);              //Does nothing

The question is how to create the function exists you saw in this code. BTW I can run C++ codes in this lang.

MessyCode
  • 333
  • 1
  • 2
  • 9
  • 4
    It's hard to imagine how keeping a "valid" flag could possibly be slower than testing a pointer for validity. – Ben Voigt Aug 03 '12 at 00:27
  • @Ugo If I sends pointer to everywhere in the code without struct ,and delete it somewhere then how I can check if the pointer is still valid? – MessyCode Aug 03 '12 at 00:27
  • @BenVoigt If I think about how to implement this in my lang. It'll fit in any expression's values memory and alot more. BTW ,my values in my lang is big as well ,I don't think I need to make it even bigger. – MessyCode Aug 03 '12 at 00:29
  • Your "scripting language" looks a lot like C++. What's the motivation or use case behind creating a different, and subtly incompatible derivative language? Furthermore, the code you currently have is buggy. You're mixing `new` with `free`, which is never correct. The C++ equivalent of `delete` is not `free`, it's `delete`. – Cody Gray - on strike Aug 03 '12 at 00:30
  • 3
    What I meant was that checking whether a pointer is "valid" will have many false positives and also be extremely slow. Even if you take a performance hit to have a structure, that's still faster than the design you're asking about. – Ben Voigt Aug 03 '12 at 00:31
  • @Cody: If his language's `delete` calls `free` in C++, then his language's `new` probably calls `calloc`. – Ben Voigt Aug 03 '12 at 00:31
  • Why can't you just set the pointer to null when you delete it? – Cody Gray - on strike Aug 03 '12 at 00:31
  • @CodyGray About the new and the free ,that's not the question about. I just wanted to show you my lang's syntax. – MessyCode Aug 03 '12 at 00:32
  • @BenVoigt It's because my calculate method has list of values that exists in objects. About the slow one ,allocate more memory - more slow it'll be. – MessyCode Aug 03 '12 at 00:34
  • @CodyGray I want to make in my lang a feat that checks if memory is not deleted yet. Think about it i'll put this pointer everywhere in the program and then delete it. All my lang'll crash. – MessyCode Aug 03 '12 at 00:36
  • Not really sure why you'd call `calloc` in C++ either. But I guess I don't understand the whole question. If memory has been deleted, you can set the pointer to null and check for that. There's no "exists" feature in C++, the language has no portable concept of pointer validity. Its valid if it points to a valid address. There's no guarantee the memory at that address contains anything useful. And trying to check for this leads to myriad problems, at least on certain platforms like Windows. Related questions: http://stackoverflow.com/questions/381718, http://stackoverflow.com/questions/482315 – Cody Gray - on strike Aug 03 '12 at 00:45
  • And more: http://stackoverflow.com/questions/993324/how-to-check-if-a-pointer-is-valid, http://stackoverflow.com/questions/551069/testing-pointers-for-validity-c-c – Cody Gray - on strike Aug 03 '12 at 00:50
  • 5
    Remember to [fill out the checklist!](http://colinm.org/language_checklist.html) – R. Martinho Fernandes Aug 03 '12 at 01:28

6 Answers6

9

You can use shared_ptr<> to hold your pointer, and use weak_ptr<> to pass your pointer around to consumers of the object. You delete the object by destroying the shared_ptr<> object, and then all the weak_ptr<>s will become expired.

std::weak_ptr<int> wptr;
assert(wptr.expired());
{
    std::shared_ptr<int> intptr(new int);
    wptr = intptr;
    assert(!wptr.expired());
}
assert(wptr.expired());

So, your exists check would be to check if the weak_ptr<> is expired or not.

To make the usage of the construct a little more concrete:

Script code                 Hypothetical C++ code that gets executed
----                        ----
var MyVar=new MyStruct();   var_map["MyVar"]
                                = std::shared_ptr<Obj>(new Obj("MyStruct"));
Func(MyVar);                invoke("Func", std::weak_ptr<Obj>(var_map["MyVar"]));
exists(arg0)                !args[0].expired()
delete(MyVar);              var_map.erase("MyVar");

If the script is to operate in a multi-threaded environment, then the weak_ptr<> state is a critical section.

jxh
  • 69,070
  • 8
  • 110
  • 193
  • 1
    I guess (and might be wrong) that he is looking for a solution that would protect his interpreter from accessing a pointer passed from a script that points to invalid memory... –  Aug 03 '12 at 00:49
  • like what VladLazarenko said. – MessyCode Aug 03 '12 at 00:50
  • 1
    @VladLazarenko: I don't understand. I don't see anything incompatible between my solution and your statement. – jxh Aug 03 '12 at 00:54
  • I'd mention that `expired` gives meaningful positives but can give false negatives. – R. Martinho Fernandes Aug 03 '12 at 01:25
  • Sorry but to check everytime if value is pointer and etc ,it's very expensive. – MessyCode Aug 03 '12 at 01:27
  • @R.MartinhoFernandes: Do you mean in a MT scenario? – jxh Aug 03 '12 at 01:30
  • @MessyCode: I am not sure I follow. The checking for valid pointer was the requirement from the question. – jxh Aug 03 '12 at 01:30
  • @user315052: Would your solution work for accepting a valid pointer to the middle of some array? –  Aug 03 '12 at 01:32
  • @user315052 yes. And what language these days can afford to ignore multithreading? – R. Martinho Fernandes Aug 03 '12 at 01:34
  • @VladLazarenko: It could, but I would make the script notion of a pointer change to be a base and offset in that case. – jxh Aug 03 '12 at 01:35
  • @R.MartinhoFernandes: Mention added for multi-threaded. Thanks and regards – jxh Aug 03 '12 at 01:39
  • @user315052 I want another solution ,I think I found something when I stepped in the delete operator. I saw something that handles some information about blocks ,like size lines and etc. I'll answer my question later. Thanks you guys anyway :D. – MessyCode Aug 03 '12 at 01:56
5

Detecting whether memory no longer is alive could be done e.g. by maintaining a set of known dead pointer. Any pointer you create gets added to an alive set, and when you delete the object you move the pointer to the dead set.

The really tricky part will be reusing memory. What do you do when you want to reuse the same address for a different object? You can't tell by looking at the pointer, because the pointers look the same. So unless you never want to reuse memory, you'll have to change your requirements.

  1. One possible way would be an added field in the structure. I know you said you don't want that, but many comments already suggest this as the best solution, and I tend to concur.
  2. Another possible way would be an added layer of indirection, so that you don't really pass out pointers to objects but instead indices into a list of living objects or whatever.
  3. You might also consider reference counting and garbage collection. That way objects would only get deleted when no one refers to them any more. Quite a lot of work, but as a user of a scripting languages I'd expect it to provide garbage collection.
MvG
  • 57,380
  • 22
  • 148
  • 276
  • I guess you would need to store both pointer and memory width. Because anything in that range is valid. For example, if his script is passing a pointer to the middle element of a valid array. But yeah, +1 –  Aug 03 '12 at 00:50
  • @VladLazarenko: You need the width only if the script can do pointer arithmetic. If array access e.g. is wrapped inside opaque functions, then knowing whether the array as a whole is alive might be enough. – MvG Aug 03 '12 at 00:53
  • The nearest what I can get is the 3rd. But to implement it ,it'll take a lot of performance. Any 'pop' (I have stack with my expression calculator) in expression need to put Smart Pointer code. – MessyCode Aug 03 '12 at 01:12
  • Any implementation of this approach has undefined behavior, possibly even if memory is never reused: pointer values that refer into deallocated memory can’t even be compared portably. – Davis Herring Mar 13 '22 at 18:26
2

This is a really bad idea. Whether a pointer is safe to use is based on more than just the value of the pointer, it's based on the entire history of the pointer. For instance, let's say that you allocate some memory, then deallocate it but you keep the pointer to it. This pointer is now invalid. Now something else gets allocated to the memory where the previous pointer is. Now if you try to detect if the old pointer is valid, it appears that it is, because the memory it points to is allocated, but if you try to use it, you get undefined behavior. If you read from it, you'll get garbage. If you try to write to it you'll probably corrupt the heap.

If all you want to do is detect is whether your process can access the memory that the pointer points to, that is possible but not portable, and definitely not a good idea (it will also be very slow). You basically have try to read from or write to it, and then catch the OS exception or signal that results. As I said, even this is a really bad idea. All it tells you is whether the OS will kill your process if you try to access it; not whether it's actually safe to use.

For more on why this is a bad idea, check out these blog posts from Raymond Chen, who works on some low-level stuff Windows stuff:

IsBadXxxPtr should really be called CrashProgramRandomly

There's no point improving the implementation of a bad idea

Dirk Holsopple
  • 8,731
  • 1
  • 24
  • 37
0

One thing you could do is use your own allocator, in which you can allocate all your objects. Your exists function would simply query the allocator to see if the object is still allocated. Yo could use something similar to SMR to make sure your reference doesn't point to something else while still in use...

rlc
  • 2,808
  • 18
  • 23
0

The simplest solution is to use a map. The map should be indexed by a pointer to the object, likely as a void *. The contents of the map item should be the type of object created.

Any time a script creates an object, add an entry to the map. Any time a script deletes an object, remove the map entry. When a script accesses an object, find the pointer in the map, thus confirming both that it exists and that the type is correct.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
-1

This valid check checked in windows only (VS),here is the function:

#pragma once
//ptrvalid.h
__inline bool isValid(void* ptr) {
    if (((uint)ptr)&7==7)
        return false; //Not valid address at all (Maybe random pointer?)
    char _prefix;
    __try {
        _prefix=*(((char*)ptr)-1); //Get the prefix of this data
    } __except (true) { //Catch all unique exceptions (Windows exceptions) 
        return false; //Can't reach this memory
    }
    switch (_prefix) {
    case 0:    //Running release mode with debugger
    case -128: //Running release mode without debugger
    case -2:   //Running debug mode with debugger
    case -35:  //Running debug mode without debugger
        return false; //Deleted :(
        break;
    }
    return true; //Still alive!
}

Usage:

#include <stdio.h>
#include "ptrvalid.h"

void PrintValid(void* ptr) {
    if (isValid(ptr))
        printf("%d is valid.\n",ptr);
    else
        printf("%d is not valid.\n",ptr);
}

int main() {
    int* my_array=(int*)malloc(4);
    PrintValid(my_array);
    PrintValid((void*)99);
    free(my_array);
    PrintValid(my_array);
    my_array=new int[4];
    PrintValid(my_array);
    delete my_array;
    PrintValid(my_array);
    getchar();
}

Output:

764776 is valid.
99 is not valid.
764776 is not valid.
774648 is valid.
774648 is not valid.

Function's explanation: (What it does)

The functions check before the real checking ,if the address is valid\start point to memory. After that he checks if this process can reach this memory's prefix (If caught exception if can't) and the last checking is checking what the prefix of this memory if deleted at any mode. (Debugging\Without Debug Mode\Release Mode) If the function passed all of those checks ,it returns true.

MessyCode
  • 333
  • 1
  • 2
  • 9
  • You've reinvented the `IsBadXxxPtr` functions, but less robustly. See [here](http://blogs.msdn.com/b/oldnewthing/archive/2006/09/27/773741.aspx) for a reasoned explanation as to why that's probably a bad idea. A previous answered, posted long before this one was, already referenced this same link. I suspect people are downvoting this for that reason, combined with the fact that this is a really bad idea that they want to discourage others from using/considering. – Cody Gray - on strike Aug 05 '12 at 05:21
  • @CodyGray The exception throwing when you generated random pointer. When you just wrote `(void*)52256` that it's valid address. But if your address was really allocated it'll not throw any-exception to return false. – MessyCode Aug 05 '12 at 06:32
  • Nice solution, would be nice to know a function that takes an address and checks if the address is definitely invalid(and will cause a crash on dereference) or could be invalid(but won't crash). It's a nice thing to know for interpreted contexts where the user is allowed to address any memory they want(eg interpreted c). It would allow the program to provide more meaningful information than "go debug the program", like "Function x tried to address illegal memory, you might want to debug it.". – Dmytro Jul 13 '16 at 21:43